diff --git a/AUTHORS b/AUTHORS
index 4221dafb..6008db6 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -330,6 +330,7 @@
 Dave Vandyke <kzar@kzar.co.uk>
 David Benjamin <davidben@mit.edu>
 David Brown <develop.david.brown@gmail.com>
+David Cernoch <dcernoch@uplandsoftware.com>
 David Davidovic <david@davidovic.io>
 David Erceg <erceg.david@gmail.com>
 David Faden <dfaden@gmail.com>
diff --git a/DEPS b/DEPS
index 1a7aa4dd..37f94eddb 100644
--- a/DEPS
+++ b/DEPS
@@ -276,23 +276,23 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'src_internal_revision': '7f38ae2436442ada9c5c9c059547f64e6961de04',
+  'src_internal_revision': '014be7b57c088d653120c5997fea33b52107a768',
   # 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': 'c2b103c89e370ea00d0cc918c92d81fa712ff058',
+  'skia_revision': 'a9e8d004f0ea4088f8a333cc36e7c019542fcf7e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': '15c092afb8cad2be5d5a62d970ee38b16a14645b',
+  'v8_revision': '4bd0d4731ea90ff9460eeb6d5cfc6cc1127363d3',
   # 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': '6dc14dedf85a49786d5503fdf5e692d87d57bb2d',
+  'angle_revision': '0dabfe5a6ac5377854a3f5ee673d56315c175e71',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'f6ad529e86e441bad3210b0b714dca7faa99b0f8',
+  'swiftshader_revision': '64af4180372914d6d3f37749a5fab2bc3f43a7f9',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -300,11 +300,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
-  'boringssl_revision': 'bca2d72c5e3174f0e1b541fb0a3d6bdf31c23825',
+  'boringssl_revision': 'd4b6eb542d4fd109baacd550935efd00c521e674',
   # 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:26.20250110.4.1',
+  'fuchsia_version': 'version:26.20250117.5.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.
@@ -352,7 +352,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling chromium_variations
   # and whatever else without interference from each other.
-  'chromium_variations_revision': 'ecd1b2bcb2bca351b58c2d19cf3f149c2ffea50f',
+  'chromium_variations_revision': '187d83f8ee5fbe166ce2b668b65b67fa9b940c7f',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
@@ -372,7 +372,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': 'd023fd41cb61a756301922cb329b5524c7e7110d',
+  'devtools_frontend_revision': 'c6bb3b2ce900d42eefac9c72600ea69e74d40943',
   # 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.
@@ -396,7 +396,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': '42451fad80b27c0fa7332b9d0566ff037d708b04',
+  'dawn_revision': '4548f0cdeb8bef00bbce127760a0cda8bdaa9fb7',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -496,7 +496,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling llvm-libc
   # and whatever else without interference from each other.
-  'llvm_libc_revision':    '3fec72bd5608491ec977142db828cc42b88f0ef3',
+  'llvm_libc_revision':    '12809bfa855813dcef51871e2ee3155e53ed35ea',
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
@@ -1110,7 +1110,7 @@
   },
 
   'src/chrome/release_scripts': {
-      'url': Var('chrome_git') + '/chrome/tools/release/scripts' + '@' + 'b37cf13dae4ea8bdbff22cee2b4820c94c83bab8',
+      'url': Var('chrome_git') + '/chrome/tools/release/scripts' + '@' + 'cc3ac97c66a56f4218fee70f28958eaf11ef5d66',
       'condition': 'checkout_chrome_release_scripts',
   },
 
@@ -1439,7 +1439,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '327a12f72fb1020336bddcf8c48c34ca9807eea1',
+    '7305a3c6540fa265dc3796f66a575054fafab42a',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1934,7 +1934,7 @@
 
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ea75de4c3baba538f4758d507dd304136cfeab52',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '80d1969422e75e8e9eecafa46074074b289e2568',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -2436,7 +2436,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'c28325f6986bd765e5610569211234e403a47a0f',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '943a0d89925e7f9a898c2e49c41feb9bf5dab71e',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2659,7 +2659,7 @@
     Var('chromium_git') + '/external/github.com/google/ruy.git' + '@' + '83fd40d730feb0804fafbc2d8814bcc19a17b2e5',
 
   'src/third_party/search_engines_data/resources':
-    Var('chromium_git') + '/external/search_engines_data.git' + '@' + '797c94d72b29932c8095bd7885ae26b7ed267db9',
+    Var('chromium_git') + '/external/search_engines_data.git' + '@' + '34ba850d314fced586dfaaf5b099c4906ce17b2d',
 
   'src/third_party/skia':
     Var('skia_git') + '/skia.git' + '@' +  Var('skia_revision'),
@@ -2809,7 +2809,7 @@
     Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '88833e6d22580fa7b358eb5ee551e1b4989fed71',
+    Var('webrtc_git') + '/src.git' + '@' + 'b0038dd14a1cc637ffbc48e36e55bd96452eef5d',
 
   # 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.
@@ -2946,7 +2946,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/help_app/app',
-        'version': 'rLhPt0SI0dlLTaBTVzDCrJ1DBRAKAE3Z4lDnlE4qN_MC',
+        'version': 'KJNjd5A4IUXeIHn-ur1hHdryL_rRrO6Pm5G3OC3bJ1MC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -2957,7 +2957,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'JJBjEL8Gb9xdnh0gAGWgHUNOOC8oxf9x3YtCDTFVgI4C',
+        'version': 'r3XXx_HKMw-S2DL0iZxM2kfhbX1AGfC63plVgmnxV6cC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3422,17 +3422,6 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/android_deps/cipd/libs/com_google_dagger_hilt_core': {
-      'packages': [
-          {
-              'package': 'chromium/third_party/android_deps/libs/com_google_dagger_hilt_core',
-              'version': 'version:2@2.52.cr1',
-          },
-      ],
-      'condition': 'checkout_android and non_git_source',
-      'dep_type': 'cipd',
-  },
-
   'src/third_party/android_deps/cipd/libs/com_google_errorprone_error_prone_annotations': {
       'packages': [
           {
@@ -4549,7 +4538,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '46e2e655940ecae3d363c4f438c1aabeaa5669e8',
+        '63647fd54313ac65206bb1093278b8e2e73bfcf8',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
@@ -4561,7 +4550,7 @@
 
   'src/remoting/internal': {
       'url': Var('chrome_git') + '/chrome/remoting/internal.git' + '@' +
-        'bc8e5dbe2ea801d77d77ed8400e752f631f352a8',
+        'f4b7da7fe40499ad775d1b2a730f4cd1de4ac05c',
       'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_feature_map.cc b/android_webview/browser/aw_feature_map.cc
index 5cec4d4..ce3e6ef 100644
--- a/android_webview/browser/aw_feature_map.cc
+++ b/android_webview/browser/aw_feature_map.cc
@@ -55,7 +55,6 @@
     &sensitive_content::features::kSensitiveContent,
     &features::kWebViewWebauthn,
     &::features::kPrefetchBrowserInitiatedTriggers,
-    &features::kWebViewAutofillHandleFocusChange,
 };
 
 // static
diff --git a/android_webview/browser/gfx/viz_compositor_thread_runner_webview.cc b/android_webview/browser/gfx/viz_compositor_thread_runner_webview.cc
index dbe2a99..edf9187 100644
--- a/android_webview/browser/gfx/viz_compositor_thread_runner_webview.cc
+++ b/android_webview/browser/gfx/viz_compositor_thread_runner_webview.cc
@@ -16,7 +16,6 @@
 #include "base/task/thread_pool.h"
 #include "base/threading/thread_restrictions.h"
 #include "components/viz/common/features.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/frame_sinks/shared_image_interface_provider.h"
 #include "components/viz/service/gl/gpu_service_impl.h"
@@ -76,16 +75,7 @@
 void VizCompositorThreadRunnerWebView::InitFrameSinkManagerOnViz() {
   DCHECK_CALLED_ON_VALID_THREAD(viz_thread_checker_);
 
-  // Android doesn't support software compositing, but in some cases
-  // unaccelerated canvas can use SharedBitmaps as resource so we create
-  // SharedBitmapManager anyway.
-  // TODO(crbug.com/40120216): Stop using SharedBitmapManager after fixing
-  // fallback to SharedBitmap.
-  server_shared_bitmap_manager_ =
-      std::make_unique<viz::ServerSharedBitmapManager>();
-
-  auto init_params = viz::FrameSinkManagerImpl::InitParams(
-      server_shared_bitmap_manager_.get());
+  auto init_params = viz::FrameSinkManagerImpl::InitParams();
 
   if (features::UseWebViewNewInvalidateHeuristic()) {
     // HWUI has 2 frames pipelineing and we need another one because we force
diff --git a/android_webview/browser/gfx/viz_compositor_thread_runner_webview.h b/android_webview/browser/gfx/viz_compositor_thread_runner_webview.h
index 22aa55f3..3f21a3f9 100644
--- a/android_webview/browser/gfx/viz_compositor_thread_runner_webview.h
+++ b/android_webview/browser/gfx/viz_compositor_thread_runner_webview.h
@@ -20,7 +20,6 @@
 
 namespace viz {
 class FrameSinkManagerImpl;
-class ServerSharedBitmapManager;
 class SharedImageInterfaceProvider;
 }  // namespace viz
 
@@ -95,7 +94,6 @@
 
   // Only accessed on |viz_task_runner_|.
   THREAD_CHECKER(viz_thread_checker_);
-  std::unique_ptr<viz::ServerSharedBitmapManager> server_shared_bitmap_manager_;
   std::unique_ptr<viz::FrameSinkManagerImpl> frame_sink_manager_;
   raw_ptr<viz::GpuServiceImpl> gpu_service_impl_ = nullptr;
   base::flat_set<base::PlatformThreadId> thread_ids_;
diff --git a/android_webview/common/aw_features.cc b/android_webview/common/aw_features.cc
index 725f34e..2f892a2 100644
--- a/android_webview/common/aw_features.cc
+++ b/android_webview/common/aw_features.cc
@@ -249,12 +249,4 @@
 BASE_FEATURE(kWebViewInterceptedCookieHeaderReadWrite,
              "WebViewInterceptedCookieHeaderReadWrite",
              base::FEATURE_DISABLED_BY_DEFAULT);
-
-// Kill switch for autofill suggestions to be terminated when they are displayed
-// above a soft keyboard and window losses focus. This is to prevent the
-// suggestions of one app being shown in another app with active keyboard.
-BASE_FEATURE(kWebViewAutofillHandleFocusChange,
-             "WebViewAutofillHandleFocusChange",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 }  // namespace android_webview::features
diff --git a/android_webview/common/aw_features.h b/android_webview/common/aw_features.h
index 11c3c674..5f81fd48 100644
--- a/android_webview/common/aw_features.h
+++ b/android_webview/common/aw_features.h
@@ -52,7 +52,6 @@
 BASE_DECLARE_FEATURE(kWebViewWebauthn);
 BASE_DECLARE_FEATURE(kWebViewInterceptedCookieHeader);
 BASE_DECLARE_FEATURE(kWebViewInterceptedCookieHeaderReadWrite);
-BASE_DECLARE_FEATURE(kWebViewAutofillHandleFocusChange);
 }  // namespace android_webview::features
 
 #endif  // ANDROID_WEBVIEW_COMMON_AW_FEATURES_H_
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
index 223b2f8..1f1321af 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumAwInit.java
@@ -160,21 +160,25 @@
         void onSuccess(WebViewStartUpDiagnostics result);
     }
 
-    // TODO(gsennton): store aw-objects instead of adapters here
-    // Initialization guarded by mLock.
-    private AwBrowserContext mDefaultBrowserContext;
-    private SharedStatics mSharedStatics;
-    private GeolocationPermissionsAdapter mDefaultGeolocationPermissions;
+    @GuardedBy("mLock")
     private CookieManagerAdapter mDefaultCookieManager;
 
+    @GuardedBy("mLock")
     private WebIconDatabaseAdapter mWebIconDatabase;
-    private WebStorageAdapter mDefaultWebStorage;
+
+    @GuardedBy("mLock")
     private WebViewDatabaseAdapter mDefaultWebViewDatabase;
-    private AwServiceWorkerController mDefaultServiceWorkerController;
-    private AwTracingController mAwTracingController;
+
+    @GuardedBy("mLock")
+    private ChromiumStartedGlobals mChromiumStartedGlobals;
+
+    @GuardedBy("mLock")
     private VariationsSeedLoader mSeedLoader;
+
+    // This is only accessed during WebViewChromiumFactoryProvider.initialize() which is guarded by
+    // the WebViewFactory lock in the framework, and on the UI thread during startChromiumLocked
+    // which cannot be called before initialize() has completed.
     private Thread mSetUpResourcesThread;
-    private AwProxyController mAwProxyController;
 
     // Guards accees to the other members, and is notifyAll() signalled on the UI thread
     // when the chromium process has been started.
@@ -240,20 +244,20 @@
 
     public AwTracingController getAwTracingController() {
         synchronized (mLock) {
-            if (mAwTracingController == null) {
+            if (mChromiumStartedGlobals == null) {
                 ensureChromiumStartedLocked(true, CallSite.GET_AW_TRACING_CONTROLLER);
             }
+            return mChromiumStartedGlobals.mAwTracingController;
         }
-        return mAwTracingController;
     }
 
     public AwProxyController getAwProxyController() {
         synchronized (mLock) {
-            if (mAwProxyController == null) {
+            if (mChromiumStartedGlobals == null) {
                 ensureChromiumStartedLocked(true, CallSite.GET_AW_PROXY_CONTROLLER);
             }
+            return mChromiumStartedGlobals.mAwProxyController;
         }
-        return mAwProxyController;
     }
 
     public void setProviderInitOnMainLooperLocation(Throwable t) {
@@ -266,7 +270,8 @@
     // lives in the ui/ layer. See ui/base/ui_base_paths.h
     private static final int DIR_RESOURCE_PAKS_ANDROID = 3003;
 
-    protected void startChromiumLocked(@CallSite int callSite, boolean triggeredFromUIThread) {
+    @GuardedBy("mLock")
+    private void startChromiumLocked(@CallSite int callSite, boolean triggeredFromUIThread) {
         long startTime = SystemClock.uptimeMillis();
         try (ScopedSysTraceEvent event =
                 ScopedSysTraceEvent.scoped("WebViewChromiumAwInit.startChromiumLocked")) {
@@ -362,11 +367,6 @@
             AwBrowserProcess.loadComponents();
             AwBrowserProcess.initializeMetricsLogUploader();
 
-            mSharedStatics = new SharedStatics();
-            if (BuildInfo.isDebugAndroidOrApp()) {
-                mSharedStatics.setWebContentsDebuggingEnabledUnconditionally(true);
-            }
-
             RecordHistogram.recordSparseHistogram(
                     "Android.WebView.TargetSdkVersion",
                     context.getApplicationInfo().targetSdkVersion);
@@ -374,18 +374,12 @@
             try (ScopedSysTraceEvent e =
                     ScopedSysTraceEvent.scoped(
                             "WebViewChromiumAwInit.initThreadUnsafeSingletons")) {
-                // Initialize thread-unsafe singletons.
-                mDefaultBrowserContext = AwBrowserContext.getDefault();
-                mDefaultGeolocationPermissions =
-                        new GeolocationPermissionsAdapter(
-                                mFactory, mDefaultBrowserContext.getGeolocationPermissions());
-                mDefaultWebStorage =
-                        new WebStorageAdapter(
-                                mFactory, mDefaultBrowserContext.getQuotaManagerBridge());
-                mAwTracingController = new AwTracingController();
-                mDefaultServiceWorkerController =
-                        mDefaultBrowserContext.getServiceWorkerController();
-                mAwProxyController = new AwProxyController();
+                mChromiumStartedGlobals = new ChromiumStartedGlobals(mFactory);
+            }
+
+            if (BuildInfo.isDebugAndroidOrApp()) {
+                mChromiumStartedGlobals.mSharedStatics
+                        .setWebContentsDebuggingEnabledUnconditionally(true);
             }
 
             if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
@@ -434,7 +428,7 @@
      *
      * @param context The context.
      */
-    public void setUpResourcesOnBackgroundThread(int packageId, Context context) {
+    void setUpResourcesOnBackgroundThread(int packageId, Context context) {
         try (ScopedSysTraceEvent e =
                 ScopedSysTraceEvent.scoped(
                         "WebViewChromiumAwInit.setUpResourcesOnBackgroundThread")) {
@@ -486,8 +480,8 @@
     // This method is not private only because the downstream subclass needs to access it,
     // it shouldn't be accessed from anywhere else.
     // Postcondition: Chromium startup is finished when this method returns.
-    /* package */ void ensureChromiumStartedLocked(
-            boolean fromThreadSafeFunction, @CallSite int callSite) {
+    @GuardedBy("mLock")
+    void ensureChromiumStartedLocked(boolean fromThreadSafeFunction, @CallSite int callSite) {
         assert Thread.holdsLock(mLock);
         ensureChromiumStartupHappensSoon(fromThreadSafeFunction, callSite);
         if (mInitState.get() == INIT_FINISHED) { // Early-out for the common case.
@@ -513,6 +507,7 @@
 
     // Postcondition: Chromium startup will be finished in the near future, but it may or may not be
     // finished after this method returns.
+    @GuardedBy("mLock")
     private void ensureChromiumStartupHappensSoon(
             boolean fromThreadSafeFunction, @CallSite int callSite) {
         assert Thread.holdsLock(mLock);
@@ -602,14 +597,15 @@
         }
     }
 
-    // Only on UI thread.
+    // This is called only on the same thread that initializes the variable, so
+    // no need to hold a lock.
+    @SuppressWarnings("GuardedBy")
     AwBrowserContext getDefaultBrowserContextOnUiThread() {
-        assert (mDefaultBrowserContext != null);
         if (BuildConfig.ENABLE_ASSERTS && !ThreadUtils.runningOnUiThread()) {
             throw new RuntimeException(
                     "getBrowserContextOnUiThread called on " + Thread.currentThread());
         }
-        return mDefaultBrowserContext;
+        return mChromiumStartedGlobals.mDefaultBrowserContext;
     }
 
     /**
@@ -623,23 +619,23 @@
 
     public SharedStatics getStatics() {
         synchronized (mLock) {
-            if (mSharedStatics == null) {
+            if (mChromiumStartedGlobals == null) {
                 // TODO: Optimization potential: most of the static methods only need the native
                 // library loaded and initialized, not the entire browser process started.
                 ensureChromiumStartedLocked(true, CallSite.GET_STATICS);
                 SharedStatics.setStartupTriggered();
             }
+            return mChromiumStartedGlobals.mSharedStatics;
         }
-        return mSharedStatics;
     }
 
     public GeolocationPermissions getDefaultGeolocationPermissions() {
         synchronized (mLock) {
-            if (mDefaultGeolocationPermissions == null) {
+            if (mChromiumStartedGlobals == null) {
                 ensureChromiumStartedLocked(true, CallSite.GET_DEFAULT_GEOLOCATION_PERMISSIONS);
             }
+            return mChromiumStartedGlobals.mDefaultGeolocationPermissions;
         }
-        return mDefaultGeolocationPermissions;
     }
 
     public CookieManager getDefaultCookieManager() {
@@ -648,17 +644,17 @@
                 mDefaultCookieManager =
                         new CookieManagerAdapter(AwCookieManager.getDefaultCookieManager());
             }
+            return mDefaultCookieManager;
         }
-        return mDefaultCookieManager;
     }
 
     public AwServiceWorkerController getDefaultServiceWorkerController() {
         synchronized (mLock) {
-            if (mDefaultServiceWorkerController == null) {
+            if (mChromiumStartedGlobals == null) {
                 ensureChromiumStartedLocked(true, CallSite.GET_DEFAULT_SERVICE_WORKER_CONTROLLER);
             }
+            return mChromiumStartedGlobals.mDefaultServiceWorkerController;
         }
-        return mDefaultServiceWorkerController;
     }
 
     public android.webkit.WebIconDatabase getWebIconDatabase() {
@@ -668,17 +664,17 @@
             if (mWebIconDatabase == null) {
                 mWebIconDatabase = new WebIconDatabaseAdapter();
             }
+            return mWebIconDatabase;
         }
-        return mWebIconDatabase;
     }
 
     public WebStorage getDefaultWebStorage() {
         synchronized (mLock) {
-            if (mDefaultWebStorage == null) {
+            if (mChromiumStartedGlobals == null) {
                 ensureChromiumStartedLocked(true, CallSite.GET_DEFAULT_WEB_STORAGE);
             }
+            return mChromiumStartedGlobals.mDefaultWebStorage;
         }
-        return mDefaultWebStorage;
     }
 
     public WebViewDatabase getDefaultWebViewDatabase(final Context context) {
@@ -689,10 +685,10 @@
                         new WebViewDatabaseAdapter(
                                 mFactory,
                                 HttpAuthDatabase.newInstance(context, HTTP_AUTH_DATABASE_FILE),
-                                mDefaultBrowserContext);
+                                mChromiumStartedGlobals.mDefaultBrowserContext);
             }
+            return mDefaultWebViewDatabase;
         }
-        return mDefaultWebViewDatabase;
     }
 
     // See comments in VariationsSeedLoader.java on when it's safe to call this.
@@ -705,6 +701,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void finishVariationsInitLocked() {
         try (ScopedSysTraceEvent e =
                 ScopedSysTraceEvent.scoped("WebViewChromiumAwInit.finishVariationsInitLocked")) {
@@ -760,4 +757,29 @@
             ensureChromiumStartupHappensSoon(true, CallSite.ASYNC_WEBVIEW_STARTUP);
         }
     }
+
+    // These are objects that need to be created on the UI thread and after chromium has started.
+    // Thus created during startChromiumLocked for ease.
+    private static final class ChromiumStartedGlobals {
+        final AwBrowserContext mDefaultBrowserContext;
+        final GeolocationPermissionsAdapter mDefaultGeolocationPermissions;
+        final WebStorageAdapter mDefaultWebStorage;
+        final AwServiceWorkerController mDefaultServiceWorkerController;
+        final AwTracingController mAwTracingController;
+        final AwProxyController mAwProxyController;
+        final SharedStatics mSharedStatics;
+
+        ChromiumStartedGlobals(WebViewChromiumFactoryProvider factory) {
+            mSharedStatics = new SharedStatics();
+            mDefaultBrowserContext = AwBrowserContext.getDefault();
+            mDefaultGeolocationPermissions =
+                    new GeolocationPermissionsAdapter(
+                            factory, mDefaultBrowserContext.getGeolocationPermissions());
+            mDefaultWebStorage =
+                    new WebStorageAdapter(factory, mDefaultBrowserContext.getQuotaManagerBridge());
+            mAwTracingController = new AwTracingController();
+            mDefaultServiceWorkerController = mDefaultBrowserContext.getServiceWorkerController();
+            mAwProxyController = new AwProxyController();
+        }
+    }
 }
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
index 813d087..1b06c23 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/WebViewChromiumFactoryProvider.java
@@ -879,7 +879,7 @@
     @RequiresApi(Build.VERSION_CODES.P)
     @Override
     public TracingController getTracingController() {
-        synchronized (mAwInit.getLock()) {
+        synchronized (mAwInit.mLock) {
             mAwInit.ensureChromiumStartedLocked(
                     true, WebViewChromiumAwInit.CallSite.GET_TRACING_CONTROLLER);
             // ensureChromiumStartedLocked() can release the lock on first call while
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 1a7e0fe..b7cdeaa5d 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -1673,11 +1673,11 @@
 
     private void installWebContentsObservers() {
         if (mWebContentsObserver != null) {
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
         }
         mWebContentsObserver = new AwWebContentsObserver(mWebContents, this, mContentsClient);
         if (mAwWebContentsMetricsRecorder != null) {
-            mAwWebContentsMetricsRecorder.destroy();
+            mAwWebContentsMetricsRecorder.observe(null);
         }
         mAwWebContentsMetricsRecorder =
                 new AwWebContentsMetricsRecorder(mWebContents, mContext, mSettings);
@@ -1909,9 +1909,9 @@
         if (mCleanupReference != null) {
             assert mNativeAwContents != 0;
 
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
             mWebContentsObserver = null;
-            mAwWebContentsMetricsRecorder.destroy();
+            mAwWebContentsMetricsRecorder.observe(null);
             mAwWebContentsMetricsRecorder = null;
             mNativeAwContents = 0;
             mWebContents = null;
@@ -4597,10 +4597,6 @@
             mViewEventSink.onWindowFocusChanged(hasWindowFocus);
             mStylusWritingController.onWindowFocusChanged(hasWindowFocus);
             Clipboard.getInstance().onWindowFocusChanged(hasWindowFocus);
-            if (AwFeatureMap.isEnabled(AwFeatures.WEBVIEW_AUTOFILL_HANDLE_FOCUS_CHANGE)
-                    && mAutofillProvider != null) {
-                mAutofillProvider.onWindowFocusChanged(hasWindowFocus);
-            }
         }
 
         @Override
diff --git a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
index 86f8bab..6fe5fef8 100644
--- a/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
+++ b/android_webview/java/src/org/chromium/android_webview/common/ProductionSupportedFlagList.java
@@ -1033,11 +1033,6 @@
                 BlinkFeatures.SPECULATIVE_IMAGE_DECODES,
                 "Start decoding in-viewport images as soon as they have loaded, "
                         + "rather than waiting for them to appear in a raster task."),
-        Flag.baseFeature(
-                AwFeatures.WEBVIEW_AUTOFILL_HANDLE_FOCUS_CHANGE,
-                "When enabled it terminates autofill suggestion after window focus loss"),
-        Flag.baseFeature(
-                "LayoutNGShapeCache", "Enables the text shaping cache for the performance."),
         // Add new commandline switches and features above. The final entry should have a
         // trailing comma for cleaner diffs.
     };
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
index 25c3559..e27ff1f2 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
@@ -95,18 +95,6 @@
 
     private static final String SECURE_COOKIE_HISTOGRAM_NAME = "Android.WebView.SecureCookieAction";
 
-    private static final String ASSET_STATEMENT_TEMPLATE =
-            """
-                [{
-                        "relation": ["delegate_permission/common.handle_all_urls"],
-                        "target": {
-                                "namespace": "android_app",
-                                "package_name": "%s",
-                                "sha256_cert_fingerprints": ["%s"]
-                        }
-                }]
-        """;
-
     public CookieManagerTest(AwSettingsMutation param) {
         this.mActivityTestRule = new AwActivityTestRule(param.getMutation());
     }
@@ -1454,15 +1442,7 @@
         String unpartitionedCookie = "regular-cookie=456";
 
         TestWebServer webServer = TestWebServer.start();
-        // Add an asset statement to test storage access since we can auto grant
-        // under these circumstances.
-        webServer.setResponse(
-                "/.well-known/assetlinks.json",
-                String.format(
-                        ASSET_STATEMENT_TEMPLATE,
-                        BuildInfo.getInstance().hostPackageName,
-                        BuildInfo.getInstance().getHostSigningCertSha256()),
-                null);
+        addServerAssetLinks(webServer);
 
         try {
             // TODO(crbug.com/41496912): The WebView cookie manager API does not currently
@@ -1546,13 +1526,7 @@
     @Features.EnableFeatures({AwFeatures.WEBVIEW_AUTO_SAA})
     public void testAutoStorageAccessNetCookies() throws Throwable {
         TestWebServer webServer = TestWebServer.start();
-        webServer.setResponse(
-                "/.well-known/assetlinks.json",
-                String.format(
-                        ASSET_STATEMENT_TEMPLATE,
-                        BuildInfo.getInstance().hostPackageName,
-                        BuildInfo.getInstance().getHostSigningCertSha256()),
-                null);
+        addServerAssetLinks(webServer);
 
         // This test suite relies on an image to force a network request that has cookies attached.
         // The AwParameterizedTest will disable this setting so force enabling it again so that
@@ -1628,6 +1602,98 @@
     @Test
     @MediumTest
     @Feature({"AndroidWebView", "Privacy"})
+    @Features.EnableFeatures({AwFeatures.WEBVIEW_AUTO_SAA})
+    public void testAutoStorageAccessNotAllFrames() throws Throwable {
+        // This test confirms that when one frame is granted storage access,
+        // it is not granted to all frames from that site.
+        // It does this by:
+        // - loading an iframe
+        //   - requesting storage access in this frame
+        //   - then triggering a new iframe to be loaded at the top level
+        // - from the new iframe of the same site, try set a 3PC and report it
+        //
+        // That 3PC is expected to not be set because the second iframe should
+        // not have storage access granted.
+        TestWebServer webServer = TestWebServer.start();
+        SettableFuture<Void> storageAccessFuture = SettableFuture.create();
+        SettableFuture<String> secondFrameCookieFuture = SettableFuture.create();
+        addServerAssetLinks(webServer);
+
+        AwActivityTestRule.addJavascriptInterfaceOnUiThread(
+                mAwContents,
+                new Object() {
+                    @JavascriptInterface
+                    public void done() {
+                        storageAccessFuture.set(null);
+                    }
+
+                    @JavascriptInterface
+                    public void reportCookies(String cookie) {
+                        secondFrameCookieFuture.set(cookie);
+                    }
+                },
+                "testInterface");
+
+        try {
+            String iframeWithNetRequest =
+                    """
+                    <html><body><script>
+                    document.requestStorageAccess().then(() => {
+                        testInterface.done();
+                    });
+                    </script></body></html>
+                    """;
+            String iframeUrl =
+                    toThirdPartyUrl(webServer.setResponse("/", iframeWithNetRequest, null));
+            String url = makeIframeUrl(webServer, "/parent.html", iframeUrl);
+
+            allowFirstPartyCookies();
+            blockThirdPartyCookies(mAwContents);
+
+            mActivityTestRule.loadUrlSync(
+                    mAwContents, mContentsClient.getOnPageFinishedHelper(), url);
+
+            // Wait until the first iframe has storage access granted...
+            AwActivityTestRule.waitForFuture(storageAccessFuture);
+
+            // Once we have granted storage access to one frame, we then load another frame to
+            // ensure that we don't share storage access across all frames.
+            // This frame should not have access to unpartitioned cookies
+            // and so should not report any cookies after attempting to set them.
+            String reportCookies =
+                    """
+                    <html><body><script>
+                    document.cookie="blah=hello;";
+                    testInterface.reportCookies(document.cookie);
+                    </script></body></html>
+                    """;
+
+            String secondFrameUrl =
+                    toThirdPartyUrl(webServer.setResponse("/", reportCookies, null));
+
+            JavaScriptUtils.executeJavaScript(
+                    mAwContents.getWebContents(),
+                    String.format(
+                            """
+                        const secondFrame = document.createElement("iframe");
+                        secondFrame.src="%s";
+                        document.body.appendChild(secondFrame);""",
+                            secondFrameUrl));
+
+            String secondFrameCookieString =
+                    AwActivityTestRule.waitForFuture(secondFrameCookieFuture);
+            Assert.assertEquals(
+                    "Second frame should not have storage access granted.",
+                    "",
+                    secondFrameCookieString);
+        } finally {
+            webServer.shutdown();
+        }
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"AndroidWebView", "Privacy"})
     @CommandLineFlags.Add("disable-partitioned-cookies")
     @Features.EnableFeatures({AwFeatures.WEBVIEW_AUTO_SAA})
     public void testDisabledPartitionedJSCookies() throws Throwable {
@@ -1635,15 +1701,7 @@
         String unpartitionedCookie = "regular-cookie=456";
 
         TestWebServer webServer = TestWebServer.start();
-        // Add an asset statement to test storage access since we can auto grant
-        // under these circumstances.
-        webServer.setResponse(
-                "/.well-known/assetlinks.json",
-                String.format(
-                        ASSET_STATEMENT_TEMPLATE,
-                        BuildInfo.getInstance().hostPackageName,
-                        BuildInfo.getInstance().getHostSigningCertSha256()),
-                null);
+        addServerAssetLinks(webServer);
 
         try {
             // TODO(https://crbug.com/1523964): The WebView cookie manager API does not currently
@@ -2401,6 +2459,26 @@
         Assert.assertFalse(msg, mCookieManager.acceptCookie());
     }
 
+    /** Adds an asset links json to allow SAA auto grants. */
+    private void addServerAssetLinks(TestWebServer webServer) {
+        webServer.setResponse(
+                "/.well-known/assetlinks.json",
+                String.format(
+                        """
+                                [{
+                                        "relation": ["delegate_permission/common.handle_all_urls"],
+                                        "target": {
+                                                "namespace": "android_app",
+                                                "package_name": "%s",
+                                                "sha256_cert_fingerprints": ["%s"]
+                                        }
+                                }]
+                        """,
+                        BuildInfo.getInstance().hostPackageName,
+                        BuildInfo.getInstance().getHostSigningCertSha256()),
+                null);
+    }
+
     interface IframeCookieSupplier {
         String get(boolean requestStorageAccess);
     }
diff --git a/android_webview/lib/BUILD.gn b/android_webview/lib/BUILD.gn
index 0e38f632..127aa56 100644
--- a/android_webview/lib/BUILD.gn
+++ b/android_webview/lib/BUILD.gn
@@ -33,6 +33,7 @@
     "//components/spellcheck:buildflags",
     "//components/stylus_handwriting/android:android",
     "//components/variations",
+    "//components/variations:variations_associated_data",
     "//components/version_info",
     "//components/version_info/android:channel_getter",
     "//components/viz/common",
diff --git a/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt b/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
index 8ac3e4f..2dd6f7fc 100644
--- a/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/android_webview/test/data/web_tests/webexposed/global-interface-listing-expected.txt
@@ -3412,7 +3412,6 @@
     getter hostname
     getter href
     getter hreflang
-    getter interestAction
     getter interestTargetElement
     getter name
     getter origin
@@ -3442,7 +3441,6 @@
     setter hostname
     setter href
     setter hreflang
-    setter interestAction
     setter interestTargetElement
     setter name
     setter password
@@ -3470,7 +3468,6 @@
     getter host
     getter hostname
     getter href
-    getter interestAction
     getter interestTargetElement
     getter noHref
     getter origin
@@ -3496,7 +3493,6 @@
     setter host
     setter hostname
     setter href
-    setter interestAction
     setter interestTargetElement
     setter noHref
     setter password
@@ -3602,7 +3598,6 @@
     getter formMethod
     getter formNoValidate
     getter formTarget
-    getter interestAction
     getter interestTargetElement
     getter labels
     getter name
@@ -3625,7 +3620,6 @@
     setter formMethod
     setter formNoValidate
     setter formTarget
-    setter interestAction
     setter interestTargetElement
     setter name
     setter popoverTargetAction
@@ -4281,7 +4275,6 @@
     getter height
     getter incremental
     getter indeterminate
-    getter interestAction
     getter interestTargetElement
     getter labels
     getter list
@@ -4342,7 +4335,6 @@
     setter height
     setter incremental
     setter indeterminate
-    setter interestAction
     setter interestTargetElement
     setter max
     setter maxLength
@@ -5450,8 +5442,7 @@
     method getTargetRanges
 interface InterestEvent : Event
     attribute @@toStringTag
-    getter action
-    getter invoker
+    getter source
     method constructor
 interface IntersectionObserver
     attribute @@toStringTag
@@ -7673,11 +7664,9 @@
 interface SVGAElement : SVGGraphicsElement
     attribute @@toStringTag
     getter href
-    getter interestAction
     getter interestTargetElement
     getter target
     method constructor
-    setter interestAction
     setter interestTargetElement
 interface SVGAngle
     attribute @@toStringTag
diff --git a/ash/accessibility/magnifier/magnifier_glass.cc b/ash/accessibility/magnifier/magnifier_glass.cc
index 3f8dd873..a68634b 100644
--- a/ash/accessibility/magnifier/magnifier_glass.cc
+++ b/ash/accessibility/magnifier/magnifier_glass.cc
@@ -196,7 +196,6 @@
   zoom_layer_ = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR);
   zoom_layer_->SetBounds(window_bounds);
   zoom_layer_->SetBackgroundZoom(params_.scale, kZoomInset);
-  zoom_layer_->SetFillsBoundsOpaquely(false);
   root_layer->Add(zoom_layer_.get());
 
   // Create a rounded rect clip, so that only we see a circle of the zoomed
diff --git a/ash/accessibility/mouse_keys/mouse_keys_controller.cc b/ash/accessibility/mouse_keys/mouse_keys_controller.cc
index 34a44e8..650363a 100644
--- a/ash/accessibility/mouse_keys/mouse_keys_controller.cc
+++ b/ash/accessibility/mouse_keys/mouse_keys_controller.cc
@@ -162,13 +162,17 @@
 }
 
 void MouseKeysController::OnMouseEvent(ui::MouseEvent* event) {
-  bool is_synthesized = event->IsSynthesized() ||
-                        event->source_device_id() == ui::ED_UNKNOWN_DEVICE;
-  if (is_synthesized || event->type() != ui::EventType::kMouseMoved) {
+  if (event->type() != ui::EventType::kMouseMoved) {
     return;
   }
+
   if (event->target()) {
     last_mouse_position_dips_ = event->target()->GetScreenLocation(*event);
+
+    gfx::Point bubble_position = last_mouse_position_dips_;
+    bubble_position.Offset(16, 16);
+    mouse_keys_bubble_controller_->UpdateMouseKeysBubblePosition(
+        bubble_position);
   }
 }
 
diff --git a/ash/accessibility/mouse_keys/mouse_keys_unittest.cc b/ash/accessibility/mouse_keys/mouse_keys_unittest.cc
index 915dda46..a412845 100644
--- a/ash/accessibility/mouse_keys/mouse_keys_unittest.cc
+++ b/ash/accessibility/mouse_keys/mouse_keys_unittest.cc
@@ -333,6 +333,19 @@
     prefs->SetDouble(prefs::kAccessibilityMouseKeysMaxSpeed, factor);
   }
 
+  gfx::Point GetLastMousePositionFromEvents() {
+    auto events = CheckForMouseEvents();
+    EXPECT_FALSE(events.empty());
+    return events.back().location();
+  }
+
+  void ShowMouseKeysBubble() {
+    EXPECT_FALSE(IsBubbleVisible());
+    PressAndReleaseKey(ui::VKEY_OEM_COMMA);
+    EXPECT_TRUE(IsBubbleVisible());
+    EXPECT_EQ(GetBubbleText(), u"Right mouse button");
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
   TestEventCapturer event_capturer_;
@@ -525,6 +538,7 @@
   ClearEvents();
   PressAndReleaseKey(ui::VKEY_W);
   EXPECT_EQ(0u, CheckForKeyEvents().size());
+  LOG(ERROR) << "[LKupo] this is the first click event";
   ExpectClick(CheckForMouseEvents(), ui::EF_LEFT_MOUSE_BUTTON,
               kDefaultPosition);
 
@@ -620,6 +634,37 @@
   EXPECT_EQ(mouse_events[0].location(), kDefaultPosition);
 }
 
+TEST_F(MouseKeysTest, MouseKeysBubbleMovesWithMouse) {
+  // Initialize mouse and enable Mouse Keys.
+  GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition,
+                                             kDefaultPosition);
+  SetEnabled(true);
+  ClearEvents();
+
+  // Get initial mouse position with a small move up-left.
+  PressAndReleaseKey(ui::VKEY_7);
+  gfx::Point initialMousePosition = GetLastMousePositionFromEvents();
+
+  // Show the bubble and get its initial position.
+  ShowMouseKeysBubble();
+  gfx::Point initialBubblePosition =
+      GetBubbleView()->GetAnchorRect().CenterPoint();
+
+  // Simulate mouse movement up-left.
+  PressAndReleaseKey(ui::VKEY_7);
+
+  // Get the final mouse position.
+  auto finalMousePosition = GetLastMousePositionFromEvents();
+
+  // Get the final bubble position.
+  gfx::Point finalBubblePosition =
+      GetBubbleView()->GetAnchorRect().CenterPoint();
+
+  // Verify mouse and bubble movements match.
+  EXPECT_EQ(finalMousePosition - initialMousePosition,
+            finalBubblePosition - initialBubblePosition);
+}
+
 TEST_F(MouseKeysTest, Move) {
   GetEventGenerator()->MoveMouseToWithNative(kDefaultPosition,
                                              kDefaultPosition);
diff --git a/ash/app_list/views/app_list_bubble_view.cc b/ash/app_list/views/app_list_bubble_view.cc
index b01ac85..a15d0a9 100644
--- a/ash/app_list/views/app_list_bubble_view.cc
+++ b/ash/app_list/views/app_list_bubble_view.cc
@@ -105,7 +105,6 @@
   SeparatorWithLayer() {
     SetPaintToLayer(ui::LAYER_SOLID_COLOR);
     // Color is set in OnThemeChanged().
-    layer()->SetFillsBoundsOpaquely(false);
   }
   SeparatorWithLayer(const SeparatorWithLayer&) = delete;
   SeparatorWithLayer& operator=(const SeparatorWithLayer&) = delete;
diff --git a/ash/app_list/views/app_list_folder_view.cc b/ash/app_list/views/app_list_folder_view.cc
index d5aafe2..f93f27bf 100644
--- a/ash/app_list/views/app_list_folder_view.cc
+++ b/ash/app_list/views/app_list_folder_view.cc
@@ -692,7 +692,6 @@
         ColorProvider::kBackgroundBlurSigma);
     animating_background_->layer()->SetBackdropFilterQuality(
         ColorProvider::kBackgroundBlurQuality);
-    animating_background_->layer()->SetFillsBoundsOpaquely(false);
   }
 
   animating_background_->SetVisible(false);
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc
index 5067ac9..c68bc9c2 100644
--- a/ash/app_list/views/app_list_item_view.cc
+++ b/ash/app_list/views/app_list_item_view.cc
@@ -663,7 +663,6 @@
 
   icon_background_ = AddChildView(std::make_unique<views::View>());
   icon_background_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
-  icon_background_->layer()->SetFillsBoundsOpaquely(false);
   icon_background_->SetCanProcessEventsWithinSubtree(false);
   icon_background_->SetVisible(is_folder_);
 
diff --git a/ash/app_list/views/assistant/assistant_page_view.cc b/ash/app_list/views/assistant/assistant_page_view.cc
index e9098ed..9e7ee7f 100644
--- a/ash/app_list/views/assistant/assistant_page_view.cc
+++ b/ash/app_list/views/assistant/assistant_page_view.cc
@@ -413,7 +413,6 @@
 void AssistantPageView::InitLayout() {
   // Use a solid color layer. The color is set in OnThemeChanged().
   SetPaintToLayer(ui::LAYER_SOLID_COLOR);
-  layer()->SetFillsBoundsOpaquely(!chromeos::features::IsSystemBlurEnabled());
 
   view_shadow_ = std::make_unique<views::ViewShadow>(this, kShadowElevation);
   view_shadow_->SetRoundedCornerRadius(
diff --git a/ash/app_list/views/top_icon_animation_view.cc b/ash/app_list/views/top_icon_animation_view.cc
index 957f4845..1bae598 100644
--- a/ash/app_list/views/top_icon_animation_view.cc
+++ b/ash/app_list/views/top_icon_animation_view.cc
@@ -47,7 +47,6 @@
     icon_background_ = AddChildView(std::make_unique<views::View>());
     if (item_in_folder_icon_) {
       icon_background_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
-      icon_background_->layer()->SetFillsBoundsOpaquely(false);
     } else {
       const int background_diameter =
           app_list_config->GetShortcutBackgroundContainerDimension();
diff --git a/ash/capture_mode/capture_mode_api.cc b/ash/capture_mode/capture_mode_api.cc
index 0d7af950..b41b338b 100644
--- a/ash/capture_mode/capture_mode_api.cc
+++ b/ash/capture_mode/capture_mode_api.cc
@@ -23,20 +23,36 @@
 
 bool IsSunfishOrScannerEnabled() {
   // Returns true if sunfish session can be started, which is true if either the
-  // Sunfish or Scanner feature flag is enabled. Note Scanner operations will
-  // only be available if the secret key is matched.
+  // Sunfish or Scanner feature flag is enabled.
   return features::IsSunfishFeatureEnabled() || features::IsScannerEnabled();
 }
 
 bool IsSunfishAllowedAndEnabled() {
+  if (!IsSunfishOrScannerEnabled()) {
+    return false;
+  }
+
   Shell* shell = Shell::HasInstance() ? Shell::Get() : nullptr;
-  return IsSunfishOrScannerEnabled() &&
-         // When `AppListControllerImpl` is initialised and indirectly calls
-         // this function, the active user session has not been started yet.
-         // Gracefully handle this case.
-         shell && shell->session_controller()->IsActiveUserSessionStarted() &&
-         capture_mode_util::GetActiveUserPrefService()->GetBoolean(
-             prefs::kSunfishEnabled);
+  if (!shell) {
+    return false;
+  }
+
+  // Order here matters: When `AppListControllerImpl` is initialised and
+  // indirectly calls this function, the active user session has not been
+  // started yet. Gracefully handle this case.
+  auto* session_controller = shell->session_controller();
+  if (!session_controller ||
+      !session_controller->IsActiveUserSessionStarted()) {
+    return false;
+  }
+
+  auto* pref_service = capture_mode_util::GetActiveUserPrefService();
+  if (!pref_service || !pref_service->GetBoolean(prefs::kSunfishEnabled)) {
+    return false;
+  }
+
+  auto* controller = CaptureModeController::Get();
+  return controller && controller->IsSearchAllowedByPolicy();
 }
 
 }  // namespace ash
diff --git a/ash/capture_mode/capture_mode_controller.cc b/ash/capture_mode/capture_mode_controller.cc
index 55bad6c..598d98aa 100644
--- a/ash/capture_mode/capture_mode_controller.cc
+++ b/ash/capture_mode/capture_mode_controller.cc
@@ -814,6 +814,10 @@
          CaptureModeDelegate::CapturePathEnforcement::kManaged;
 }
 
+bool CaptureModeController::IsSearchAllowedByPolicy() const {
+  return delegate_->IsSearchAllowedByPolicy();
+}
+
 bool CaptureModeController::IsAudioRecordingInProgress() const {
   return video_recording_watcher_ &&
          !video_recording_watcher_->is_shutting_down() &&
diff --git a/ash/capture_mode/capture_mode_controller.h b/ash/capture_mode/capture_mode_controller.h
index 37a2096..7fc3cec 100644
--- a/ash/capture_mode/capture_mode_controller.h
+++ b/ash/capture_mode/capture_mode_controller.h
@@ -171,6 +171,9 @@
   // `ScreenCaptureLocation` policy and can't be changed.
   bool IsCustomFolderManagedByPolicy() const;
 
+  // Returns true if Search is allowed.
+  bool IsSearchAllowedByPolicy() const;
+
   // Returns true if there's an active video recording that is recording audio.
   bool IsAudioRecordingInProgress() const;
 
diff --git a/ash/capture_mode/sunfish_unittest.cc b/ash/capture_mode/sunfish_unittest.cc
index 28a24f6..bab693a 100644
--- a/ash/capture_mode/sunfish_unittest.cc
+++ b/ash/capture_mode/sunfish_unittest.cc
@@ -517,6 +517,23 @@
   EXPECT_FALSE(capture_label->GetVisible());
 }
 
+// Tests the Search button does not show in default mode.
+TEST_F(SunfishTest, CheckSearchPolicy) {
+  auto* controller =
+      StartCaptureSession(CaptureModeSource::kRegion, CaptureModeType::kImage);
+  auto* test_delegate =
+      static_cast<TestCaptureModeDelegate*>(controller->delegate_for_testing());
+  test_delegate->set_is_search_allowed_by_policy(false);
+
+  // Start default session. Test the Search button does not show.
+  SelectCaptureModeRegion(GetEventGenerator(), gfx::Rect(100, 100, 600, 500),
+                          /*release_mouse=*/true, /*verify_region=*/false);
+  WaitForCaptureModeWidgetsVisible();
+  CaptureModeSessionTestApi session_test_api(
+      controller->capture_mode_session());
+  ASSERT_THAT(session_test_api.GetActionButtons(), IsEmpty());
+}
+
 // Tests that sunfish checks DLP restrictions upon selecting a region.
 TEST_F(SunfishTest, CheckDlpRestrictions) {
   auto* controller = CaptureModeController::Get();
diff --git a/ash/capture_mode/test_capture_mode_delegate.cc b/ash/capture_mode/test_capture_mode_delegate.cc
index 163176c..30fbef95 100644
--- a/ash/capture_mode/test_capture_mode_delegate.cc
+++ b/ash/capture_mode/test_capture_mode_delegate.cc
@@ -110,6 +110,10 @@
   return is_allowed_by_policy_;
 }
 
+bool TestCaptureModeDelegate::IsSearchAllowedByPolicy() const {
+  return is_search_allowed_by_policy_;
+}
+
 void TestCaptureModeDelegate::StartObservingRestrictedContent(
     const aura::Window* window,
     const gfx::Rect& bounds,
diff --git a/ash/capture_mode/test_capture_mode_delegate.h b/ash/capture_mode/test_capture_mode_delegate.h
index 8f401359..3c9092b 100644
--- a/ash/capture_mode/test_capture_mode_delegate.h
+++ b/ash/capture_mode/test_capture_mode_delegate.h
@@ -46,6 +46,9 @@
   }
   void set_is_allowed_by_dlp(bool value) { is_allowed_by_dlp_ = value; }
   void set_is_allowed_by_policy(bool value) { is_allowed_by_policy_ = value; }
+  void set_is_search_allowed_by_policy(bool value) {
+    is_search_allowed_by_policy_ = value;
+  }
   void set_should_save_after_dlp_check(bool value) {
     should_save_after_dlp_check_ = value;
   }
@@ -105,6 +108,7 @@
       const gfx::Rect& bounds,
       OnCaptureModeDlpRestrictionChecked callback) override;
   bool IsCaptureAllowedByPolicy() const override;
+  bool IsSearchAllowedByPolicy() const override;
   void StartObservingRestrictedContent(
       const aura::Window* window,
       const gfx::Rect& bounds,
@@ -169,6 +173,7 @@
   bool is_session_active_ = false;
   bool is_allowed_by_dlp_ = true;
   bool is_allowed_by_policy_ = true;
+  bool is_search_allowed_by_policy_ = true;
   bool should_save_after_dlp_check_ = true;
   bool is_camera_disabled_by_policy_ = false;
   bool is_audio_capture_disabled_by_policy_ = false;
diff --git a/ash/capture_mode/user_nudge_controller.cc b/ash/capture_mode/user_nudge_controller.cc
index b69e58a..454bb56 100644
--- a/ash/capture_mode/user_nudge_controller.cc
+++ b/ash/capture_mode/user_nudge_controller.cc
@@ -68,10 +68,8 @@
       DarkLightModeControllerImpl::Get()->IsDarkModeEnabled() ? SK_ColorWHITE
                                                               : SK_ColorBLACK;
   base_ring_.SetColor(ring_color);
-  base_ring_.SetFillsBoundsOpaquely(false);
   base_ring_.SetOpacity(0);
   ripple_ring_.SetColor(ring_color);
-  ripple_ring_.SetFillsBoundsOpaquely(false);
   ripple_ring_.SetOpacity(0);
 
   Reposition();
diff --git a/ash/public/cpp/capture_mode/capture_mode_delegate.h b/ash/public/cpp/capture_mode/capture_mode_delegate.h
index bab33d4d..87efe71 100644
--- a/ash/public/cpp/capture_mode/capture_mode_delegate.h
+++ b/ash/public/cpp/capture_mode/capture_mode_delegate.h
@@ -124,6 +124,9 @@
   // Returns whether screen capture is allowed by an enterprise policy.
   virtual bool IsCaptureAllowedByPolicy() const = 0;
 
+  // Returns whether search is allowed by the browser enterprise policy.
+  virtual bool IsSearchAllowedByPolicy() const = 0;
+
   // Called when a video capture for |window| and |bounds| area is started, so
   // that Data Leak Prevention can start observing the area.
   // |on_area_restricted_callback| will be called when the area becomes
diff --git a/ash/rotator/screen_rotation_animator.cc b/ash/rotator/screen_rotation_animator.cc
index 7d03ac84a..f319849 100644
--- a/ash/rotator/screen_rotation_animator.cc
+++ b/ash/rotator/screen_rotation_animator.cc
@@ -29,6 +29,7 @@
 #include "ui/compositor/layer_animator.h"
 #include "ui/compositor/layer_owner.h"
 #include "ui/compositor/layer_tree_owner.h"
+#include "ui/compositor/layer_type.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/display/display.h"
 #include "ui/display/manager/display_manager.h"
@@ -367,12 +368,10 @@
       GetScreenRotationContainer(root_window_)->layer()->size();
   std::unique_ptr<ui::Layer> copy_layer =
       CreateLayerFromCopyOutputResult(std::move(result), layer_size);
+  CHECK_EQ(copy_layer->type(), ui::LAYER_SOLID_COLOR);
   DCHECK_EQ(copy_layer->size(),
             GetScreenRotationContainer(root_window_)->layer()->size());
 
-  // TODO(crbug.com/40113966): This is a workaround and should be removed once
-  // the issue is fixed.
-  copy_layer->SetFillsBoundsOpaquely(false);
   return std::make_unique<ui::LayerTreeOwner>(std::move(copy_layer));
 }
 
diff --git a/ash/style/counter_expand_button.cc b/ash/style/counter_expand_button.cc
index 69cf4c7e..f7b9c90 100644
--- a/ash/style/counter_expand_button.cc
+++ b/ash/style/counter_expand_button.cc
@@ -74,7 +74,6 @@
   views::FocusRing::Get(this)->SetOutsetFocusRingDisabled(true);
 
   SetPaintToLayer(ui::LAYER_SOLID_COLOR);
-  layer()->SetFillsBoundsOpaquely(false);
   layer()->SetRoundedCornerRadius(gfx::RoundedCornersF{kTrayItemCornerRadius});
   layer()->SetIsFastRoundedCorner(true);
 }
diff --git a/ash/system/accessibility/mouse_keys/mouse_keys_bubble_controller.cc b/ash/system/accessibility/mouse_keys/mouse_keys_bubble_controller.cc
index c032fee8..e126f9d 100644
--- a/ash/system/accessibility/mouse_keys/mouse_keys_bubble_controller.cc
+++ b/ash/system/accessibility/mouse_keys/mouse_keys_bubble_controller.cc
@@ -26,21 +26,24 @@
   }
 }
 
+void MouseKeysBubbleController::UpdateMouseKeysBubblePosition(
+    gfx::Point position) {
+  if (mouse_keys_bubble_view_) {
+    mouse_keys_bubble_view_->SetAnchorRect(gfx::Rect(position, gfx::Size()));
+  }
+}
+
 void MouseKeysBubbleController::UpdateBubble(
     bool visible,
     MouseKeysBubbleIconType icon,
     const std::optional<std::u16string>& text) {
   EnsureInitialize();
 
-  gfx::Point cursor_position =
+  gfx::Point bubble_position =
       Shell::Get()->mouse_keys_controller()->GetLastMousePositionDips();
-  cursor_position.Offset(16, 16);
+  bubble_position.Offset(16, 16);
 
-  if (mouse_keys_bubble_view_) {
-    mouse_keys_bubble_view_->SetAnchorRect(
-        gfx::Rect(cursor_position, gfx::Size()));
-  }
-
+  UpdateMouseKeysBubblePosition(bubble_position);
   Update(icon, text);
   widget_->SetVisible(visible);
   if (timer_.IsRunning()) {
diff --git a/ash/system/accessibility/mouse_keys/mouse_keys_bubble_controller.h b/ash/system/accessibility/mouse_keys/mouse_keys_bubble_controller.h
index 8ee0c5a..73c1e3b 100644
--- a/ash/system/accessibility/mouse_keys/mouse_keys_bubble_controller.h
+++ b/ash/system/accessibility/mouse_keys/mouse_keys_bubble_controller.h
@@ -33,6 +33,8 @@
       delete;
   ~MouseKeysBubbleController() override;
 
+  void UpdateMouseKeysBubblePosition(gfx::Point position);
+
   // Updates the bubble's visibility and text content.
   void UpdateBubble(bool visible,
                     MouseKeysBubbleIconType icon,
diff --git a/ash/system/accessibility/mouse_keys/mouse_keys_bubble_controller_unittest.cc b/ash/system/accessibility/mouse_keys/mouse_keys_bubble_controller_unittest.cc
index d617130..c5eee49 100644
--- a/ash/system/accessibility/mouse_keys/mouse_keys_bubble_controller_unittest.cc
+++ b/ash/system/accessibility/mouse_keys/mouse_keys_bubble_controller_unittest.cc
@@ -88,4 +88,4 @@
   EXPECT_FALSE(IsBubbleVisible());
 }
 
-}  // namespace ash
+}  // namespace ash
\ No newline at end of file
diff --git a/ash/system/privacy/privacy_indicators_tray_item_view.cc b/ash/system/privacy/privacy_indicators_tray_item_view.cc
index 7927ce7..08d2bd3 100644
--- a/ash/system/privacy/privacy_indicators_tray_item_view.cc
+++ b/ash/system/privacy/privacy_indicators_tray_item_view.cc
@@ -165,7 +165,6 @@
   // Set up a solid color layer to paint the background color, then add a layer
   // to each child so that they are visible and can perform layer animation.
   SetPaintToLayer(ui::LAYER_SOLID_COLOR);
-  layer()->SetFillsBoundsOpaquely(false);
   layer()->SetRoundedCornerRadius(
       gfx::RoundedCornersF{kPrivacyIndicatorsViewExpandedShorterSideSize / 2});
   layer()->SetIsFastRoundedCorner(true);
@@ -173,7 +172,6 @@
   auto add_icon_to_container = [&container_view]() {
     auto icon = std::make_unique<views::ImageView>();
     icon->SetPaintToLayer();
-    icon->layer()->SetFillsBoundsOpaquely(false);
     icon->SetVisible(false);
     return container_view->AddChildView(std::move(icon));
   };
diff --git a/ash/system/privacy/privacy_indicators_tray_item_view_pixeltest.cc b/ash/system/privacy/privacy_indicators_tray_item_view_pixeltest.cc
index 131b874..3a5975b 100644
--- a/ash/system/privacy/privacy_indicators_tray_item_view_pixeltest.cc
+++ b/ash/system/privacy/privacy_indicators_tray_item_view_pixeltest.cc
@@ -75,7 +75,7 @@
   SimulateAnimateToFullyExpandedState(privacy_indicators_view);
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
       "full_view" + GetScreenshotNameSuffix(),
-      /*revision_number=*/1, notification_center_tray));
+      /*revision_number=*/2, notification_center_tray));
 
   SimulateAnimationEnded(privacy_indicators_view);
   EXPECT_TRUE(GetPixelDiffer()->CompareUiComponentsOnPrimaryScreen(
diff --git a/ash/system/tray/tray_background_view.cc b/ash/system/tray/tray_background_view.cc
index 269b241..dac5365f 100644
--- a/ash/system/tray/tray_background_view.cc
+++ b/ash/system/tray/tray_background_view.cc
@@ -298,7 +298,6 @@
   // Use layer color to provide background color. Note that children views
   // need to have their own layers to be visible.
   SetPaintToLayer(ui::LAYER_SOLID_COLOR);
-  layer()->SetFillsBoundsOpaquely(!chromeos::features::IsSystemBlurEnabled());
 
   // Start the tray items not visible, because visibility changes are animated.
   views::View::SetVisible(false);
diff --git a/ash/system/unified/power_button.cc b/ash/system/unified/power_button.cc
index 2d0c30d..6a87f7178 100644
--- a/ash/system/unified/power_button.cc
+++ b/ash/system/unified/power_button.cc
@@ -398,7 +398,6 @@
   background_view_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
   auto* background_layer = background_view_->layer();
   background_layer->SetRoundedCornerRadius(kAllRoundedCorners);
-  background_layer->SetFillsBoundsOpaquely(false);
   background_layer->SetIsFastRoundedCorner(true);
 
   set_context_menu_controller(context_menu_.get());
diff --git a/ash/system/video_conference/bubble/title_view.cc b/ash/system/video_conference/bubble/title_view.cc
index da69a48..a7957b3 100644
--- a/ash/system/video_conference/bubble/title_view.cc
+++ b/ash/system/video_conference/bubble/title_view.cc
@@ -107,7 +107,6 @@
   background_view_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
   auto* background_layer = background_view_->layer();
   background_layer->SetRoundedCornerRadius(gfx::RoundedCornersF(16));
-  background_layer->SetFillsBoundsOpaquely(false);
 
   button_container_ =
       AddChildView(std::make_unique<MicTestButtonContainer>(base::BindRepeating(
diff --git a/ash/user_education/welcome_tour/welcome_tour_scrim.cc b/ash/user_education/welcome_tour/welcome_tour_scrim.cc
index e03ae7f..4bb3528 100644
--- a/ash/user_education/welcome_tour/welcome_tour_scrim.cc
+++ b/ash/user_education/welcome_tour/welcome_tour_scrim.cc
@@ -208,7 +208,6 @@
   // Invoked once to initialize `this` scrim.
   void Init() {
     // Configure static scrim layer properties.
-    layer_owner_.layer()->SetFillsBoundsOpaquely(false);
     layer_owner_.layer()->SetMaskLayer(mask_layer_owner_.layer());
     layer_owner_.layer()->SetName(WelcomeTourScrim::kLayerName);
 
diff --git a/ash/utility/layer_copy_animator.cc b/ash/utility/layer_copy_animator.cc
index a3d0eb5..1a14aaf0 100644
--- a/ash/utility/layer_copy_animator.cc
+++ b/ash/utility/layer_copy_animator.cc
@@ -10,6 +10,7 @@
 #include "ui/base/class_property.h"
 #include "ui/compositor/layer_animation_sequence.h"
 #include "ui/compositor/layer_animator.h"
+#include "ui/compositor/layer_type.h"
 
 DEFINE_UI_CLASS_PROPERTY_TYPE(ash::LayerCopyAnimator*)
 
@@ -114,7 +115,7 @@
 }
 
 void LayerCopyAnimator::RunAnimation() {
-  copied_layer_->SetFillsBoundsOpaquely(false);
+  CHECK_EQ(copied_layer_->type(), ui::LAYER_SOLID_COLOR);
 
   auto* parent_layer = window_->layer()->parent();
   parent_layer->Add(copied_layer_.get());
diff --git a/ash/webui/common/resources/BUILD.gn b/ash/webui/common/resources/BUILD.gn
index d43efa9d..88e345c 100644
--- a/ash/webui/common/resources/BUILD.gn
+++ b/ash/webui/common/resources/BUILD.gn
@@ -30,7 +30,6 @@
   "multidevice_setup/setup_succeeded_page.js",
   "multidevice_setup/start_setup_page.js",
   "multidevice_setup/ui_page.js",
-  "network/apn_list_item.js",
   "network/cr_policy_network_indicator_mojo.js",
   "network/network_config_select.js",
   "network/network_config_toggle.js",
@@ -102,6 +101,7 @@
   "bluetooth/bluetooth_pairing_ui.ts",
   "bluetooth/bluetooth_spinner_page.ts",
   "network/apn_list.ts",
+  "network/apn_list_item.ts",
   "network/apn_selection_dialog.ts",
   "network/network_apnlist.ts",
   "network/network_choose_mobile.ts",
@@ -451,6 +451,7 @@
   "multidevice_setup/ui_page.js",
   "navigation_shared_vars.css.js",
   "network/apn_list.html.js",
+  "network/apn_list_item.html.js",
   "network/apn_selection_dialog.html.js",
   "network/cellular_utils.js",
   "network/cr_policy_network_behavior_mojo.js",
@@ -537,7 +538,6 @@
 
   "network/apn_detail_dialog.d.ts",
   "network/apn_selection_dialog_list_item.d.ts",
-  "network/apn_list_item.d.ts",
   "network/cr_policy_network_indicator_mojo.d.ts",
   "network/network_config_select.d.ts",
   "network/network_config_toggle.d.ts",
diff --git a/ash/webui/common/resources/network/apn_list.html b/ash/webui/common/resources/network/apn_list.html
index 5fdba4b..cf59196 100644
--- a/ash/webui/common/resources/network/apn_list.html
+++ b/ash/webui/common/resources/network/apn_list.html
@@ -95,7 +95,7 @@
   <template>
     <apn-list-item
         apn="[[item]]"
-        is-connected="[[isApnConnected_(index, managedCellularProperties)]]"
+        is-apn-connected="[[isApnConnected_(index, managedCellularProperties)]]"
         should-disallow-disabling-removing="[[shouldDisallowDisablingRemoving_(item)]]"
         should-disallow-enabling="[[shouldDisallowEnabling_(item)]]"
         should-disallow-apn-modification="[[shouldDisallowApnModification]]"
diff --git a/ash/webui/common/resources/network/apn_list_item.d.ts b/ash/webui/common/resources/network/apn_list_item.d.ts
deleted file mode 100644
index 8e110a25..0000000
--- a/ash/webui/common/resources/network/apn_list_item.d.ts
+++ /dev/null
@@ -1,40 +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.
-
-import {I18nMixin} from '//resources/ash/common/cr_elements/i18n_mixin.js';
-import {ApnProperties} from '//resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
-import {PortalState} from '//resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
-import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-export class ApnListItem extends I18nMixin
-(PolymerElement) {
-guid:
-  string;
-apn:
-  ApnProperties;
-isConnected:
-  boolean;
-shouldDisallowDisablingRemoving:
-  boolean;
-shouldDisallowEnabling:
-  boolean;
-shouldDisallowApnModification:
-  boolean;
-itemIndex:
-  number;
-listSize:
-  number;
-portalState:
-  PortalState|null;
-private isDisabled_:
-  boolean;
-private isApnRevampAndAllowApnModificationPolicyEnabled_:
-  boolean;
-}
-
-declare global {
-  interface HTMLElementTagNameMap {
-    'apn-list-item': ApnListItem;
-  }
-}
diff --git a/ash/webui/common/resources/network/apn_list_item.html b/ash/webui/common/resources/network/apn_list_item.html
index 7b6350b3..c230193 100644
--- a/ash/webui/common/resources/network/apn_list_item.html
+++ b/ash/webui/common/resources/network/apn_list_item.html
@@ -53,16 +53,16 @@
     </div>
   </div>
   <div id="subLabel" class="cr-secondary-text" aria-hidden="true"
-      hidden="[[!isConnected]]"
+      hidden="[[!isApnConnected]]"
       warning$="[[isPortalStateNoInternet_(portalState)]]">
-    [[getSublabel_(isConnected, portalState)]]
+    [[getSublabel_(isApnConnected, portalState)]]
   </div>
 </div>
 <cr-icon-button id="actionMenuButton" class="icon-more-vert"
     on-click="onMenuButtonClicked_"
     title="[[i18n('apnMoreActionsTitle', apn.accessPointName)]]"
     focus-row-control focus-type="menu"
-    aria-label="[[getAriaLabel_(apn, itemIndex, listSize, isConnected,
+    aria-label="[[getAriaLabel_(apn, itemIndex, listSize, isApnConnected,
         isDisabled_)]]">
 </cr-icon-button>
 
diff --git a/ash/webui/common/resources/network/apn_list_item.js b/ash/webui/common/resources/network/apn_list_item.ts
similarity index 72%
rename from ash/webui/common/resources/network/apn_list_item.js
rename to ash/webui/common/resources/network/apn_list_item.ts
index 2494adc..7d72bb85 100644
--- a/ash/webui/common/resources/network/apn_list_item.js
+++ b/ash/webui/common/resources/network/apn_list_item.ts
@@ -9,28 +9,23 @@
 import './network_shared.css.js';
 import '//resources/ash/common/cr_elements/cr_action_menu/cr_action_menu.js';
 
-import {assert} from '//resources/ash/common/assert.js';
-import {I18nBehavior, I18nBehaviorInterface} from '//resources/ash/common/i18n_behavior.js';
-import {ApnDetailDialogMode, ApnEventData, getApnDisplayName} from '//resources/ash/common/network/cellular_utils.js';
-import {MojoInterfaceProviderImpl} from '//resources/ash/common/network/mojo_interface_provider.js';
-import {ApnState, ApnType} from '//resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
+import type {CrActionMenuElement} from '//resources/ash/common/cr_elements/cr_action_menu/cr_action_menu.js';
+import {I18nMixin} from '//resources/ash/common/cr_elements/i18n_mixin.js';
+import {assert} from '//resources/js/assert.js';
+import {ApnProperties, ApnState, ApnType, CrosNetworkConfigInterface} from '//resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
 import {PortalState} from '//resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
-import {mixinBehaviors, PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {PolymerElement} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 
 import {getTemplate} from './apn_list_item.html.js';
+import {ApnDetailDialogMode, ApnEventData, getApnDisplayName} from './cellular_utils.js';
+import {MojoInterfaceProviderImpl} from './mojo_interface_provider.js';
 
-/**
- * @constructor
- * @extends {PolymerElement}
- * @implements {I18nBehaviorInterface}
- */
-const ApnListItemBase = mixinBehaviors([I18nBehavior], PolymerElement);
+const ApnListItemBase = I18nMixin(PolymerElement);
 
-/** @polymer */
-export class ApnListItem extends ApnListItemBase {
+export class ApnListItemElement extends ApnListItemBase {
   static get is() {
-    return 'apn-list-item';
+    return 'apn-list-item' as const;
   }
 
   static get template() {
@@ -42,10 +37,9 @@
       /** The GUID of the network to display details for. */
       guid: String,
 
-      /**@type {!ApnProperties}*/
       apn: {type: Object},
 
-      isConnected: {
+      isApnConnected: {
         type: Boolean,
         value: false,
       },
@@ -74,19 +68,16 @@
        */
       listSize: Number,
 
-      /** @type {?PortalState} */
       portalState: {
         type: Object,
       },
 
-      /** @private */
       isDisabled_: {
         reflectToAttribute: true,
         type: Boolean,
         computed: 'computeIsDisabled_(apn)',
       },
 
-      /** @private */
       isApnRevampAndAllowApnModificationPolicyEnabled_: {
         type: Boolean,
         value() {
@@ -99,77 +90,75 @@
     };
   }
 
+  guid: string;
+  apn: ApnProperties;
+  isApnConnected: boolean;
+  shouldDisallowDisablingRemoving: boolean;
+  shouldDisallowEnabling: boolean;
+  shouldDisallowApnModification: boolean;
+  itemIndex: number;
+  listSize: number;
+  portalState: PortalState|null;
+  private isDisabled_: boolean;
+  private isApnRevampAndAllowApnModificationPolicyEnabled_: boolean;
+  private networkConfig_: CrosNetworkConfigInterface;
+
   constructor() {
     super();
-    /** @private {!CrosNetworkConfigInterface} */
+
     this.networkConfig_ =
         MojoInterfaceProviderImpl.getInstance().getMojoServiceRemote();
   }
 
-  /**
-   * @param {!ApnProperties} apn
-   * @private
-   */
-  getApnDisplayName_(apn) {
+  private getApnDisplayName_(apn: ApnProperties): string {
     return getApnDisplayName(this.i18n.bind(this), apn);
   }
 
-  /**
-   * @return {string}
-   * @private
-   */
-  getSublabel_() {
+  private getSublabel_(): string {
     if (this.isPortalStateNoInternet_()) {
       return this.i18n('networkListItemConnectedNoConnectivity');
     }
     return this.i18n('OncConnected');
   }
 
-  /**
-   * @return {boolean}
-   * @private
-   */
-  isPortalStateNoInternet_() {
+  private isPortalStateNoInternet_(): boolean {
     return !!this.portalState && this.portalState === PortalState.kNoInternet;
   }
 
   /**
    * Opens the three dots menu.
-   * @private
    */
-  onMenuButtonClicked_(event) {
-    /** @type {!CrActionMenuElement} */ (this.$.dotsMenu)
-        .showAt(/** @type {!HTMLElement} */ (event.target));
+  private onMenuButtonClicked_(event: {target: HTMLElement}): void {
+    this.shadowRoot!.querySelector<CrActionMenuElement>('#dotsMenu')!.showAt(
+        event.target);
   }
 
-  /** @private */
-  closeMenu_() {
-    /** @type {!CrActionMenuElement} */ (this.$.dotsMenu).close();
+  private closeMenu_(): void {
+    this.shadowRoot!.querySelector<CrActionMenuElement>('#dotsMenu')!.close();
   }
 
   /**
    * Opens APN Details dialog.
-   * @private
    */
-  onDetailsClicked_() {
+  private onDetailsClicked_(): void {
     assert(!!this.apn);
     this.closeMenu_();
+    const apnEventData: ApnEventData = {
+      apn: this.apn,
+      // Only allow editing if the APN is a custom APN.
+      mode: this.getDetailDialogMode_(),
+    };
     this.dispatchEvent(new CustomEvent('show-apn-detail-dialog', {
       composed: true,
       bubbles: true,
-      detail: /** @type {!ApnEventData} */ ({
-        apn: this.apn,
-        // Only allow editing if the APN is a custom APN.
-        mode: this.getDetailDialogMode_(),
-      }),
+      detail: apnEventData,
     }));
   }
 
   /**
    * Returns the mode the APN detail dialog should be if opened.
-   * @private
    */
-  getDetailDialogMode_() {
+  private getDetailDialogMode_(): ApnDetailDialogMode {
     if (!this.apn) {
       return ApnDetailDialogMode.VIEW;
     }
@@ -189,9 +178,8 @@
 
   /**
    * Disables the selected APN.
-   * @private
    */
-  onDisableClicked_() {
+  private onDisableClicked_(): void {
     assert(this.guid);
     assert(this.apn);
     this.closeMenu_();
@@ -214,17 +202,15 @@
       return;
     }
 
-    const apn =
-        /** @type {!ApnProperties} */ (Object.assign({}, this.apn));
+    const apn: ApnProperties = Object.assign({}, this.apn);
     apn.state = ApnState.kDisabled;
     this.networkConfig_.modifyCustomApn(this.guid, apn);
   }
 
   /**
    * Enables the selected APN.
-   * @private
    */
-  onEnableClicked_() {
+  private onEnableClicked_(): void {
     assert(this.guid);
     assert(this.apn);
     this.closeMenu_();
@@ -247,17 +233,15 @@
       return;
     }
 
-    const apn =
-        /** @type {!ApnProperties} */ (Object.assign({}, this.apn));
+    const apn = Object.assign({}, this.apn);
     apn.state = ApnState.kEnabled;
     this.networkConfig_.modifyCustomApn(this.guid, apn);
   }
 
   /**
    * Removes the selected APN.
-   * @private
    */
-  onRemoveClicked_() {
+  private onRemoveClicked_(): void {
     assert(this.guid);
     assert(this.apn);
     this.closeMenu_();
@@ -275,52 +259,41 @@
       return;
     }
 
-    this.networkConfig_.removeCustomApn(
-        this.guid, /** @type {string} */ (this.apn.id));
+    this.networkConfig_.removeCustomApn(this.guid, this.apn.id);
   }
 
   /**
    * Returns true if disable menu button should be shown.
-   * @return {boolean}
-   * @private
    */
-  shouldShowDisableMenuItem_() {
+  private shouldShowDisableMenuItem_(): boolean {
     return !!this.apn.id && this.apn.state === ApnState.kEnabled;
   }
 
   /**
    * Returns true if enable menu button should be shown.
-   * @return {boolean}
-   * @private
    */
-  shouldShowEnableMenuItem_() {
+  private shouldShowEnableMenuItem_(): boolean {
     return !!this.apn.id && this.apn.state === ApnState.kDisabled;
   }
 
   /**
    * Returns true if remove menu button should be shown.
-   * @return {boolean}
-   * @private
    */
-  shouldShowRemoveMenuItem_() {
+  private shouldShowRemoveMenuItem_(): boolean {
     return !!this.apn.id;
   }
 
   /**
    * Returns true if the apn is disabled.
-   * @return {boolean}
-   * @private
    */
-  computeIsDisabled_() {
+  private computeIsDisabled_(): boolean {
     return !!this.apn.id && this.apn.state === ApnState.kDisabled;
   }
 
   /**
    * Returns the label for the "Details" menu item.
-   * @return {string}
-   * @private
    */
-  getDetailsMenuItemLabel_() {
+  private getDetailsMenuItemLabel_(): string {
     return this.getDetailDialogMode_() === ApnDetailDialogMode.EDIT ?
         this.i18n('apnMenuEdit') :
         this.i18n('apnMenuDetails');
@@ -328,10 +301,8 @@
 
   /**
    * Returns accessibility label for the item.
-   * @return {string}
-   * @private
    */
-  getAriaLabel_() {
+  private getAriaLabel_(): string {
     if (!this.apn) {
       return '';
     }
@@ -344,7 +315,7 @@
       a11yLabel += ' ' + this.i18n('apnA11yAutoDetected');
     }
 
-    if (this.isConnected) {
+    if (this.isApnConnected) {
       a11yLabel += ' ' + this.i18n('apnA11yConnected');
     } else if (this.isDisabled_) {
       a11yLabel += ' ' + this.i18n('apnA11yDisabled');
@@ -374,4 +345,11 @@
   }
 }
 
-customElements.define(ApnListItem.is, ApnListItem);
+
+declare global {
+  interface HTMLElementTagNameMap {
+    [ApnListItemElement.is]: ApnListItemElement;
+  }
+}
+
+customElements.define(ApnListItemElement.is, ApnListItemElement);
diff --git a/ash/wm/window_mini_view.cc b/ash/wm/window_mini_view.cc
index 7e0e5e8..23ed53e 100644
--- a/ash/wm/window_mini_view.cc
+++ b/ash/wm/window_mini_view.cc
@@ -136,9 +136,7 @@
     backdrop_view_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
 
     ui::Layer* layer = backdrop_view_->layer();
-
     layer->SetName("BackdropView");
-    layer->SetFillsBoundsOpaquely(false);
 
     const int corner_radius = window_util::GetMiniWindowRoundedCornerRadius();
     layer->SetRoundedCornerRadius(
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 914cc1a9..99d5767 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -555,6 +555,8 @@
     "profiler/stack_unwind_data.h",
     "profiler/suspendable_thread_delegate.h",
     "profiler/thread_delegate.h",
+    "profiler/thread_group_profiler.cc",
+    "profiler/thread_group_profiler.h",
     "profiler/thread_group_profiler_client.h",
     "profiler/unwinder.cc",
     "profiler/unwinder.h",
@@ -3383,6 +3385,7 @@
     "profiler/stack_copier_unittest.cc",
     "profiler/stack_sampler_unittest.cc",
     "profiler/stack_sampling_profiler_unittest.cc",
+    "profiler/thread_group_profiler_unittest.cc",
     "rand_util_unittest.cc",
     "ranges/algorithm_unittest.cc",
     "ranges/functional_unittest.cc",
@@ -4429,6 +4432,7 @@
       "android/java/src/org/chromium/base/BuildInfo.java",
       "android/java/src/org/chromium/base/BundleUtils.java",
       "android/java/src/org/chromium/base/CallbackController.java",
+      "android/java/src/org/chromium/base/CancelableRunnable.java",
       "android/java/src/org/chromium/base/CollectionUtil.java",
       "android/java/src/org/chromium/base/CommandLineInitUtil.java",
       "android/java/src/org/chromium/base/CpuFeatures.java",
diff --git a/base/android/java/src/org/chromium/base/CancelableRunnable.java b/base/android/java/src/org/chromium/base/CancelableRunnable.java
new file mode 100644
index 0000000..72f273c
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/CancelableRunnable.java
@@ -0,0 +1,37 @@
+// Copyright 2025 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.base;
+
+import org.chromium.build.BuildConfig;
+
+/**
+ * CancelableRunnable is a Runnable class that can be canceled. A canceled task is not removed from
+ * the task queue - it instead becomes a noop.
+ *
+ * <p>This class is *not* threadsafe, and is only for canceling tasks from the thread on which it
+ * would be run.
+ */
+public class CancelableRunnable implements Runnable {
+    private Runnable mRunnable;
+    private Thread mExpectedThread;
+
+    public CancelableRunnable(Runnable runnable) {
+        mRunnable = runnable;
+    }
+
+    public void cancel() {
+        mRunnable = null;
+        if (BuildConfig.ENABLE_ASSERTS) {
+            mExpectedThread = Thread.currentThread();
+        }
+    }
+
+    @Override
+    public void run() {
+        assert mExpectedThread == null || Thread.currentThread() == mExpectedThread
+                : "Canceling thread: " + mExpectedThread.getName();
+        if (mRunnable != null) mRunnable.run();
+    }
+}
diff --git a/base/profiler/periodic_sampling_scheduler.h b/base/profiler/periodic_sampling_scheduler.h
index 2488490..6e0dd9a3 100644
--- a/base/profiler/periodic_sampling_scheduler.h
+++ b/base/profiler/periodic_sampling_scheduler.h
@@ -30,7 +30,8 @@
   virtual ~PeriodicSamplingScheduler();
 
   // Returns the amount of time between now and the next collection.
-  TimeDelta GetTimeToNextCollection();
+  // Virtual to provide dependency injection for test use.
+  virtual TimeDelta GetTimeToNextCollection();
 
  protected:
   // Virtual to provide seams for test use.
diff --git a/base/profiler/thread_group_profiler.cc b/base/profiler/thread_group_profiler.cc
new file mode 100644
index 0000000..ff3f4751
--- /dev/null
+++ b/base/profiler/thread_group_profiler.cc
@@ -0,0 +1,407 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/profiler/thread_group_profiler.h"
+
+#include <memory>
+
+#include "base/check.h"
+#include "base/functional/bind.h"
+#include "base/memory/ptr_util.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/profiler/periodic_sampling_scheduler.h"
+#include "base/profiler/sampling_profiler_thread_token.h"
+#include "base/profiler/stack_sampling_profiler.h"
+#include "base/profiler/thread_group_profiler_client.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/task/bind_post_task.h"
+#include "base/task/sequenced_task_runner.h"
+// Required solely to avoid complaints on incomplete type for
+// Unretained(worker_thread) invocations. This code otherwise treats
+// WorkerThread pointers as opaque.
+#include "base/task/thread_pool/worker_thread.h"
+#include "base/time/time.h"
+
+// Periodic sampling collection is done in CollectProfilesTask(). The function
+// is scheduled based on PeriodicSamplingScheduler timing and will start
+// profiling all active worker threads.
+//
+// During a sampling session, new worker threads and worker threads that become
+// active (being signalled for work while idle) will call OnWorkerThreadActive
+// so profiling can be started for them. If at any point the worker thread is
+// shutdown (this should only happen in test as we only sample active threads
+// and the thread reclaim time after idle is longer than sampling duration), the
+// profiler for that thread is stopped and worker thread blocked until profiler
+// is destroyed. This should guarantee a uniform sampling for all worker thread
+// executions as all the work happening inside a sampling session is collected
+// regardless of which thread the work is scheduled.
+//
+// Thread group shutdown happens after task runner shutdown so no more sampling
+// can be scheduled. All existing profilers will be cleared on the main thread
+// during shutdown and a profiler shutdown event will signal. Note that after
+// ThreadGroup shutdown is started worker threads may still execute
+// CONTINUE_ON_SHUTDOWN tasks and these tasks will never be sampled. This is
+// acceptable as these profiles are unlikely to be uploaded anyway.
+
+// ThreadGroupProfiler will only be destructed in test through
+// ThreadGroupImpl::JoinForTesting. This also happens after task runner shutdown
+// so same logic applies as normal shutdown. In prod the thread pool (which
+// holds thread group) is always leaked during shutdown.
+
+namespace base {
+namespace {
+// Pointer to the embedder-specific client implementation.
+// |g_thread_group_profiler_client| is intentionally leaked on shutdown.
+ThreadGroupProfilerClient* g_thread_group_profiler_client = nullptr;
+
+// Run continuous profiling 2% of the time.
+constexpr double kFractionOfExecutionTimeToSample = 0.02;
+
+// Keep sampling new worker thread until last second of sampling duration.
+// This is intended as an performance optimization, i.e. it's not worth it to do
+// the whole StackSamplingProfiler set up just to get less than 10 samples. And
+// since this treats all threads equally it does not affect the unbiased nature
+// of sampling.
+const TimeDelta kMinRemainingTimeForNewThreadSampling = Seconds(1);
+}  // namespace
+
+// static
+void ThreadGroupProfiler::SetClient(
+    std::unique_ptr<ThreadGroupProfilerClient> client) {
+  // Generally, the client should only be set once, at process startup. However,
+  // some test infrastructure causes initialization to happen more than once.
+  delete g_thread_group_profiler_client;
+  g_thread_group_profiler_client = client.release();
+}
+
+// static
+bool ThreadGroupProfiler::IsProfilingEnabled() {
+  // TODO(crbug.com/40226611): Remove GetClient() check once client is set on
+  // all embedders. This is to temporarily support testing with mock client when
+  // real clients aren't set on embedders.
+  return GetClient() && GetClient()->IsProfilerEnabledForCurrentProcess();
+}
+
+ThreadGroupProfiler::ThreadGroupProfiler(
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    std::string_view thread_group_label,
+    std::unique_ptr<PeriodicSamplingScheduler> periodic_sampling_scheduler,
+    ProfilerFactory profiler_factory)
+    : thread_group_label_(thread_group_label),
+      periodic_sampling_scheduler_(std::move(periodic_sampling_scheduler)),
+      task_runner_(std::move(task_runner)),
+      stack_sampling_profiler_factory_(std::move(profiler_factory)) {
+  DETACH_FROM_SEQUENCE(task_runner_sequence_checker_);
+  if (!periodic_sampling_scheduler_) {
+    periodic_sampling_scheduler_ = std::make_unique<PeriodicSamplingScheduler>(
+        GetClient()->GetSamplingParams().sampling_interval *
+            GetClient()->GetSamplingParams().samples_per_profile,
+        kFractionOfExecutionTimeToSample, TimeTicks::Now());
+  }
+  task_runner_->PostTask(
+      FROM_HERE, BindOnce(&ThreadGroupProfiler::StartTask, Unretained(this)));
+}
+
+ThreadGroupProfiler::~ThreadGroupProfiler() {
+  // Shutdown has been run before destruction.
+  CHECK(!active_collection_);
+}
+
+void ThreadGroupProfiler::Shutdown() {
+  // Must be destroyed from the same sequence as constructor.
+  DCHECK_CALLED_ON_VALID_SEQUENCE(construction_sequence_checker_);
+  // CHECK that the task runner has actually been shutdown.
+  CHECK(!task_runner_->PostTask(FROM_HERE, DoNothing()));
+
+  TS_UNCHECKED_READ(active_collection_).reset();
+  thread_group_profiler_shutdown_.Signal();
+}
+
+void ThreadGroupProfiler::OnWorkerThreadStarted(
+    internal::WorkerThread* worker_thread) {
+  task_runner_->PostTask(
+      FROM_HERE, BindOnce(&ThreadGroupProfiler::OnWorkerThreadStartedTask,
+                          Unretained(this), Unretained(worker_thread),
+                          GetSamplingProfilerCurrentThreadToken()));
+}
+
+void ThreadGroupProfiler::OnWorkerThreadActive(
+    internal::WorkerThread* worker_thread) {
+  task_runner_->PostTask(
+      FROM_HERE, BindOnce(&ThreadGroupProfiler::OnWorkerThreadActiveTask,
+                          Unretained(this), Unretained(worker_thread)));
+}
+
+void ThreadGroupProfiler::OnWorkerThreadIdle(
+    internal::WorkerThread* worker_thread) {
+  task_runner_->PostTask(FROM_HERE,
+                         BindOnce(&ThreadGroupProfiler::OnWorkerThreadIdleTask,
+                                  Unretained(this), Unretained(worker_thread)));
+}
+
+void ThreadGroupProfiler::OnWorkerThreadExiting(
+    internal::WorkerThread* worker_thread) {
+  WaitableEvent profiling_has_stopped;
+  task_runner_->PostTask(
+      FROM_HERE, BindOnce(&ThreadGroupProfiler::OnWorkerThreadExitingTask,
+                          Unretained(this), Unretained(worker_thread),
+                          Unretained(&profiling_has_stopped)));
+  base::WaitableEvent* event_array[] = {&profiling_has_stopped,
+                                        &thread_group_profiler_shutdown_};
+  // During shutdown profiling_has_stopped may not get a chance to signal as
+  // task runner is stopped, profiler_shutdown event will signal instead
+  // indicating that clean up has finished and worker thread may safely exit.
+  WaitableEvent::WaitMany(event_array, std::size(event_array));
+}
+
+// Production implementation that wraps an actual StackSamplingProfiler.
+class ThreadGroupProfiler::ProfilerImpl : public ThreadGroupProfiler::Profiler {
+ public:
+  ProfilerImpl(SamplingProfilerThreadToken thread_token,
+               const StackSamplingProfiler::SamplingParams& params,
+               std::unique_ptr<ProfileBuilder> profile_builder,
+               StackSamplingProfiler::UnwindersFactory unwinder_factory)
+      : sampling_profiler_{thread_token, params, std::move(profile_builder),
+                           std::move(unwinder_factory)} {}
+  ~ProfilerImpl() override = default;
+
+  // Profiler:
+  void Start() override { sampling_profiler_.Start(); }
+
+ private:
+  StackSamplingProfiler sampling_profiler_;
+};
+
+ThreadGroupProfiler::ActiveCollection::ActiveCollection(
+    const flat_map<internal::WorkerThread*, WorkerThreadContext>&
+        worker_thread_context_set,
+    const TimeDelta& sampling_duration,
+    SequencedTaskRunner* task_runner,
+    ProfilerFactory factory,
+    OnceClosure collection_complete_callback)
+    : task_runner_(task_runner),
+      stack_sampling_profiler_factory_(factory),
+      collection_complete_callback_(std::move(collection_complete_callback)),
+      sampling_duration_(sampling_duration),
+      collection_end_time_(TimeTicks::Now() + sampling_duration),
+      empty_collection_closure_{
+          BindOnce(&ActiveCollection::OnEmptyCollectionCompleted,
+                   Unretained(this))} {
+  decltype(profilers_)::container_type new_profilers;
+  for (auto& [worker_thread, context] : worker_thread_context_set) {
+    // Only create profilers for active threads.
+    if (!context.is_idle) {
+      std::unique_ptr<Profiler> profiler = CreateSamplingProfilerForThread(
+          worker_thread, context.token, GetClient()->GetSamplingParams());
+      profiler->Start();
+      new_profilers.emplace_back(worker_thread, std::move(profiler));
+    }
+  }
+  // More efficient to construct flat_map from containers then adding each
+  // profiler in a loop.
+  profilers_ = flat_map(std::move(new_profilers));
+  if (profilers_.empty()) {
+    // Queue a delayed empty collection callback to run after the sampling
+    // duration if there are no active threads to sample.
+    task_runner_->PostDelayedTask(
+        FROM_HERE, empty_collection_closure_.callback(), sampling_duration_);
+  } else {
+    empty_collection_closure_.Cancel();
+  }
+}
+
+void ThreadGroupProfiler::ActiveCollection::MaybeAddWorkerThread(
+    internal::WorkerThread* worker_thread,
+    const SamplingProfilerThreadToken& token) {
+  // Skip if the remaining time of current sampling session is less than the
+  // threshold.
+  if ((collection_end_time_ - TimeTicks::Now()) <
+      kMinRemainingTimeForNewThreadSampling) {
+    return;
+  }
+  // Skip if there's already a profiler for this thread. A worker thread can
+  // flip between idle and active anytime during the collection but profiler
+  // should only be created for it the first time it becomes active.
+  if (profilers_.find(worker_thread) != profilers_.end()) {
+    return;
+  }
+  StackSamplingProfiler::SamplingParams sampling_params =
+      GetClient()->GetSamplingParams();
+  // Calculate remaining samples until end of collection period.
+  sampling_params.samples_per_profile =
+      ClampFloor((collection_end_time_ - TimeTicks::Now()) /
+                 sampling_params.sampling_interval);
+  std::unique_ptr<Profiler> profiler =
+      CreateSamplingProfilerForThread(worker_thread, token, sampling_params);
+  profiler->Start();
+  profilers_.emplace(worker_thread, std::move(profiler));
+  // Cancel empty callback since there is a profiler running now.
+  empty_collection_closure_.Cancel();
+}
+
+void ThreadGroupProfiler::ActiveCollection::RemoveWorkerThread(
+    internal::WorkerThread* worker_thread) {
+  // If there's a profiler associated, remove it. Will block until profiler
+  // destructor finishes but it should be a rare case (during shutdown or
+  // ThreadGroup::JoinForTesting) as we only sample active threads; they should
+  // not get reclaimed during sampling session.
+  const bool was_present = profilers_.erase(worker_thread) == 1;
+  if (!was_present || !profilers_.empty()) {
+    return;
+  }
+  // Queue a delayed empty collection callback to run after the sampling
+  // duration if there are no active threads to sample.
+  empty_collection_closure_.Reset(BindOnce(
+      &ActiveCollection::OnEmptyCollectionCompleted, Unretained(this)));
+  task_runner_->PostDelayedTask(FROM_HERE, empty_collection_closure_.callback(),
+                                collection_end_time_ - TimeTicks::Now());
+}
+
+std::unique_ptr<ThreadGroupProfiler::Profiler>
+ThreadGroupProfiler::ActiveCollection::CreateSamplingProfilerForThread(
+    internal::WorkerThread* worker_thread,
+    const SamplingProfilerThreadToken& token,
+    const StackSamplingProfiler::SamplingParams& sampling_params) {
+  ThreadGroupProfilerClient* client = ThreadGroupProfiler::GetClient();
+  return stack_sampling_profiler_factory_.Run(
+      token, sampling_params,
+      client->CreateProfileBuilder(BindPostTask(
+          task_runner_,
+          BindOnce(&ActiveCollection::OnProfilerCollectionCompleted,
+                   Unretained(this), Unretained(worker_thread)))),
+      client->GetUnwindersFactory());
+}
+
+void ThreadGroupProfiler::ActiveCollection::OnProfilerCollectionCompleted(
+    internal::WorkerThread* worker_thread) {
+  DCHECK(!profilers_.empty());
+  profilers_.erase(worker_thread);
+  // Notify the collection is complete when there's no outstanding profilers.
+  if (profilers_.empty()) {
+    std::move(collection_complete_callback_).Run();
+  }
+}
+
+void ThreadGroupProfiler::ActiveCollection::OnEmptyCollectionCompleted() {
+  DCHECK(profilers_.empty());
+  std::move(collection_complete_callback_).Run();
+}
+
+ThreadGroupProfiler::ActiveCollection::~ActiveCollection() = default;
+
+// static
+ThreadGroupProfilerClient* ThreadGroupProfiler::GetClient() {
+  // TODO(crbug.com/40226611): Add check once client is set on all embedders.
+  // CHECK(g_thread_group_profiler_client);
+  return g_thread_group_profiler_client;
+}
+
+// static
+ThreadGroupProfiler::ProfilerFactory
+ThreadGroupProfiler::GetDefaultProfilerFactory() {
+  return BindRepeating(
+      [](SamplingProfilerThreadToken thread_token,
+         const StackSamplingProfiler::SamplingParams& params,
+         std::unique_ptr<ProfileBuilder> profile_builder,
+         StackSamplingProfiler::UnwindersFactory unwinder_factory)
+          -> std::unique_ptr<Profiler> {
+        return std::make_unique<ProfilerImpl>(thread_token, params,
+                                              std::move(profile_builder),
+                                              std::move(unwinder_factory));
+      });
+}
+
+// static
+TimeDelta ThreadGroupProfiler::GetSamplingDuration() {
+  StackSamplingProfiler::SamplingParams params =
+      GetClient()->GetSamplingParams();
+  return params.sampling_interval * params.samples_per_profile;
+}
+
+void ThreadGroupProfiler::ThreadGroupProfiler::StartTask() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(task_runner_sequence_checker_);
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&ThreadGroupProfiler::CollectProfilesTask,
+                     Unretained(this)),
+      periodic_sampling_scheduler_->GetTimeToNextCollection());
+}
+
+void ThreadGroupProfiler::OnWorkerThreadStartedTask(
+    internal::WorkerThread* worker_thread,
+    SamplingProfilerThreadToken token) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(task_runner_sequence_checker_);
+  const bool inserted =
+      worker_thread_context_set_
+          .emplace(worker_thread, WorkerThreadContext{token,
+                                                      /*is_idle=*/true})
+          .second;
+  // Worker thread should not be present before this call.
+  DCHECK(inserted);
+}
+
+// A worker thread starts out on the idle set when it's created. On its
+// ThreadMain it will call Delegate::GetWork() and when it does obtain a task
+// source it will be removed from idle set and becomes active.
+// OnWorkerThreadActive() will be called at that point. When it exhausted the
+// task source, it will be placed on idle set and nullptr returned from
+// GetWork()/ProcessSwappedTask(). The worker thread will then enter a
+// TimedWait until it's either wake up or reaches its reclaim time.
+void ThreadGroupProfiler::OnWorkerThreadActiveTask(
+    internal::WorkerThread* worker_thread) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(task_runner_sequence_checker_);
+  auto it = worker_thread_context_set_.find(worker_thread);
+  // Profiler token should already be set since OnWorkerThreadActive will
+  // be called strictly after worker thread creation.
+  DCHECK(it != worker_thread_context_set_.end());
+  // Mark worker thread as active.
+  it->second.is_idle = false;
+  if (active_collection_) {
+    active_collection_->MaybeAddWorkerThread(worker_thread, it->second.token);
+  }
+}
+
+void ThreadGroupProfiler::OnWorkerThreadIdleTask(
+    internal::WorkerThread* worker_thread) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(task_runner_sequence_checker_);
+  auto it = worker_thread_context_set_.find(worker_thread);
+  DCHECK(it != worker_thread_context_set_.end());
+  // Mark worker thread as idle.
+  it->second.is_idle = true;
+}
+
+void ThreadGroupProfiler::OnWorkerThreadExitingTask(
+    internal::WorkerThread* worker_thread,
+    WaitableEvent* profiling_has_stopped) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(task_runner_sequence_checker_);
+  if (active_collection_) {
+    active_collection_->RemoveWorkerThread(worker_thread);
+  }
+  worker_thread_context_set_.erase(worker_thread);
+  profiling_has_stopped->Signal();
+}
+
+void ThreadGroupProfiler::CollectProfilesTask() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(task_runner_sequence_checker_);
+  DCHECK(!active_collection_);
+  active_collection_.emplace(
+      worker_thread_context_set_, GetSamplingDuration(), task_runner_.get(),
+      stack_sampling_profiler_factory_,
+      BindOnce(&ThreadGroupProfiler::EndActiveCollectionTask,
+               Unretained(this)));
+}
+
+void ThreadGroupProfiler::EndActiveCollectionTask() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(task_runner_sequence_checker_);
+  DCHECK(active_collection_);
+  active_collection_.reset();
+  // Schedule the next collection.
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&ThreadGroupProfiler::CollectProfilesTask,
+                     Unretained(this)),
+      periodic_sampling_scheduler_->GetTimeToNextCollection());
+}
+
+}  // namespace base
diff --git a/base/profiler/thread_group_profiler.h b/base/profiler/thread_group_profiler.h
new file mode 100644
index 0000000..7fb0261
--- /dev/null
+++ b/base/profiler/thread_group_profiler.h
@@ -0,0 +1,254 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_PROFILER_THREAD_GROUP_PROFILER_H_
+#define BASE_PROFILER_THREAD_GROUP_PROFILER_H_
+
+#include <memory>
+
+#include "base/cancelable_callback.h"
+#include "base/containers/flat_map.h"
+#include "base/functional/callback.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/profiler/periodic_sampling_scheduler.h"
+#include "base/profiler/sampling_profiler_thread_token.h"
+#include "base/profiler/stack_sampling_profiler.h"
+#include "base/task/sequenced_task_runner.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+
+namespace base {
+namespace internal {
+class WorkerThread;
+}  // namespace internal
+
+class ThreadGroupProfilerClient;
+
+// ThreadGroupProfiler manages sampling of active worker threads and
+// schedules periodic sampling.
+// This class will be accessed on
+//   - Main thread: Construction, shutdown and destruction.
+//   - Worker thread: Invokes the OnWorkerThread* functions to inform the class
+//   of their lifetime events.
+//   - Sequenced task runner: Internal operations of this class are scheduled on
+//   the task runner.
+// Once created, ThreadGroupProfiler will periodically profile active worker
+// threads by creating a StackSamplingProfiler for each thread. At the beginning
+// of a session, all active worker threads are sampled. During the session, if a
+// worker thread becomes active (via OnWorkerThreadActive) it will be sampled
+// for the remainder of this session. Once the sampling starts for a thread it
+// will continue until either the thread is exiting (via OnWorkerThreadExit) or
+// the profile is completed. When a profile completes the associated
+// StackSamplingProfiler is destroyed. Worker threads being sampled will be
+// blocked on exit until the profiling is stopped.
+//
+// When shutting down, the class requires that the provided SequencedTaskRunner
+// is shutdown prior to invoking Shutdown().
+class BASE_EXPORT ThreadGroupProfiler {
+ public:
+  // Interface for profiling stack samples from a specific thread.
+  // This provides an abstraction over StackSamplingProfiler to enable testing
+  // of ThreadGroupProfiler without depending on actual profiler implementation.
+  class Profiler {
+   public:
+    virtual ~Profiler() = default;
+    virtual void Start() = 0;
+
+   protected:
+    Profiler() = default;
+  };
+
+  // Sets the instance of ThreadProfilerClient to provide embedder-specific
+  // implementation logic. This instance must be set early, before
+  // CreateThreadGroupProfiler() and IsProfilingEnabled() are called.
+  static void SetClient(std::unique_ptr<ThreadGroupProfilerClient> client);
+
+  // Must be called after SetClient().
+  static bool IsProfilingEnabled();
+
+  using ProfilerFactory = RepeatingCallback<std::unique_ptr<Profiler>(
+      SamplingProfilerThreadToken thread_token,
+      const StackSamplingProfiler::SamplingParams& params,
+      std::unique_ptr<ProfileBuilder> profile_builder,
+      StackSamplingProfiler::UnwindersFactory unwinder_factory)>;
+
+  using GetTimeToNextCollectionCallback = RepeatingCallback<TimeDelta()>;
+
+  // ThreadGroupProfiler constructor. |task_runner| will be used to schedule the
+  // profile collection. |thread_group_label| will used to tag the metadata for
+  // all samples collected in this profiler. |profiler_factory| is a repeating
+  // callback that will be used to make Profiler, intended to be used for
+  // dependency injection for testing. |time_to_next_collection_callback| is a
+  // repeating callback that will be used to get the next collection time,
+  // intended to be used for dependency injection for testing.
+  explicit ThreadGroupProfiler(
+      scoped_refptr<SequencedTaskRunner> task_runner,
+      std::string_view thread_group_label,
+      std::unique_ptr<PeriodicSamplingScheduler> periodic_sampling_scheduler =
+          nullptr,
+      ProfilerFactory profiler_factory = GetDefaultProfilerFactory());
+  ThreadGroupProfiler(const ThreadGroupProfiler&) = delete;
+  ThreadGroupProfiler& operator=(const ThreadGroupProfiler&) = delete;
+
+  ~ThreadGroupProfiler();
+
+  // Shuts down ThreadGroupProfiler instance and stops all current profiling.
+  // This should only be called after task runner is stopped as it expects
+  // exclusive access on this instance. No more sampling will happen and worker
+  // threads are freed to exit after shutdown finishes.
+  void Shutdown();
+
+  // Register new worker thread on starting. Must be called on worker
+  // thread.
+  void OnWorkerThreadStarted(internal::WorkerThread* worker_thread);
+
+  // Starts profilng on worker that has become active during a sampling
+  // session. Must be called on worker thread.
+  void OnWorkerThreadActive(internal::WorkerThread* worker_thread);
+
+  // Must be called on worker thread when it becomes idle, i.e. no more work is
+  // scheduled to run on this thread.
+  void OnWorkerThreadIdle(internal::WorkerThread* worker_thread);
+
+  // Clean up on worker thread exiting. Must be called on worker thread.
+  void OnWorkerThreadExiting(internal::WorkerThread* worker_thread);
+
+ private:
+  struct WorkerThreadContext {
+    SamplingProfilerThreadToken token;
+    bool is_idle;
+  };
+  class ProfilerImpl;
+
+  // Represents an active sample collection phase and is responsible for
+  // creating profilers for active threads both at the beginning as well as
+  // during the sampling duration.
+  class ActiveCollection {
+   public:
+    explicit ActiveCollection(
+        const flat_map<internal::WorkerThread*, WorkerThreadContext>&
+            worker_thread_context_set,
+        const TimeDelta& sampling_duration,
+        SequencedTaskRunner* task_runner,
+        ProfilerFactory stack_sampling_profiler_factory,
+        OnceClosure collection_completed_callback);
+    ~ActiveCollection();
+    ActiveCollection(const ActiveCollection&) = delete;
+    ActiveCollection& operator=(const ActiveCollection&) = delete;
+
+    // Maybe create a new profiler for worker_thread depending on how close
+    // the collection is to being complete.
+    void MaybeAddWorkerThread(internal::WorkerThread* worker_thread,
+                              const SamplingProfilerThreadToken& token);
+
+    // Destroy the profiler for worker_thread if it exists.
+    void RemoveWorkerThread(internal::WorkerThread* worker_thread);
+
+   private:
+    // Helper function for creating the StackSamplingProfiler.
+    std::unique_ptr<Profiler> CreateSamplingProfilerForThread(
+        internal::WorkerThread* worker_thread,
+        const SamplingProfilerThreadToken& token,
+        const StackSamplingProfiler::SamplingParams& sampling_params);
+
+    // Remove completed profiler from collection. If this is the last profiler,
+    // invokes the collection completed callback.
+    void OnProfilerCollectionCompleted(internal::WorkerThread* worker_thread);
+    // Invokes collection completed callback to end an empty collection.
+    void OnEmptyCollectionCompleted();
+
+    // A map that stores the active `StackSamplingProfiler` instances
+    // for each worker thread.
+    flat_map<internal::WorkerThread*, std::unique_ptr<Profiler>> profilers_;
+
+    scoped_refptr<SequencedTaskRunner> task_runner_;
+
+    ProfilerFactory stack_sampling_profiler_factory_;
+
+    // Callback to notify on collection complete. After this callback is run
+    // there's no guarantee that the instance is still alive.
+    OnceClosure collection_complete_callback_;
+
+    const TimeDelta sampling_duration_;
+
+    // Tracks the end time (an estimate calculated at start of sampling by
+    // adding the sampling duration) of the current sampling session.
+    const TimeTicks collection_end_time_;
+
+    // Used to trigger collection completed when the collection is empty at the
+    // end of a session. This callback is only alive when there are no profilers
+    // in this collection and is cancelled immediately when there are active
+    // profilers.
+    CancelableOnceClosure empty_collection_closure_;
+  };
+
+  // Retrieve the ThreadProfilerClient instance provided via SetClient().
+  static ThreadGroupProfilerClient* GetClient();
+
+  static ProfilerFactory GetDefaultProfilerFactory();
+
+  static GetTimeToNextCollectionCallback
+  GetDefaultTimeToNextCollectionCallback();
+
+  static TimeDelta GetSamplingDuration();
+
+  // All the private functions below are executed on the task runner to
+  // ensure proper synchronization. This is enforced through
+  // DCHECK_CALLED_ON_VALID_SEQUENCE(task_runner_sequence_checker_) for
+  // functions that are called as PostTask task and
+  // VALID_CONTEXT_REQUIRED(task_runner_sequence_checker_) for functions that
+  // are called directly.
+
+  void StartTask();
+
+  void OnWorkerThreadStartedTask(internal::WorkerThread* worker_thread,
+                                 SamplingProfilerThreadToken token);
+  void OnWorkerThreadActiveTask(internal::WorkerThread* worker_thread);
+  void OnWorkerThreadIdleTask(internal::WorkerThread* worker_thread);
+  void OnWorkerThreadExitingTask(internal::WorkerThread* worker_thread,
+                                 WaitableEvent* profiling_has_stopped);
+
+  // Starts the thread group profiler collection. This will create stack
+  // sampling profilers for all active worker threads in the thread group,
+  // monitor new active worker threads (these include both new worker threads
+  // that are spawned and idle worker threads becoming active) during sampling
+  // duration and schedules the next sampling session.
+  void CollectProfilesTask();
+
+  void EndActiveCollectionTask();
+
+  // A map that stores the worker threads, their corresponding profiler
+  // token and their idle states.
+  flat_map<internal::WorkerThread*, WorkerThreadContext>
+      worker_thread_context_set_
+          GUARDED_BY_CONTEXT(task_runner_sequence_checker_);
+
+  // This has no value if not in an active collection phase.
+  std::optional<ActiveCollection> active_collection_
+      GUARDED_BY_CONTEXT(task_runner_sequence_checker_);
+
+  // Label to use as metadata for specifying which group (foreground,
+  // background, utility) the worker thread being sampled belongs to.
+  const std::string thread_group_label_;
+
+  // Used to block worker threads from exiting during ThreadGroupProfiler
+  // shutdown.
+  WaitableEvent thread_group_profiler_shutdown_{
+      base::WaitableEvent::ResetPolicy::MANUAL,
+      base::WaitableEvent::InitialState::NOT_SIGNALED};
+
+  std::unique_ptr<PeriodicSamplingScheduler> periodic_sampling_scheduler_
+      GUARDED_BY_CONTEXT(task_runner_sequence_checker_);
+
+  scoped_refptr<SequencedTaskRunner> task_runner_;
+
+  ProfilerFactory stack_sampling_profiler_factory_;
+
+  SEQUENCE_CHECKER(task_runner_sequence_checker_);
+  SEQUENCE_CHECKER(construction_sequence_checker_);
+};
+
+}  // namespace base
+
+#endif  // BASE_PROFILER_THREAD_GROUP_PROFILER_H_
diff --git a/base/profiler/thread_group_profiler_unittest.cc b/base/profiler/thread_group_profiler_unittest.cc
new file mode 100644
index 0000000..e965e57
--- /dev/null
+++ b/base/profiler/thread_group_profiler_unittest.cc
@@ -0,0 +1,868 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/profiler/thread_group_profiler.h"
+
+#include <map>
+#include <memory>
+
+#include "base/functional/bind.h"
+#include "base/functional/function_ref.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/raw_ptr.h"
+#include "base/profiler/periodic_sampling_scheduler.h"
+#include "base/profiler/stack_sampling_profiler.h"
+#include "base/profiler/stack_sampling_profiler_test_util.h"
+#include "base/profiler/thread_group_profiler_client.h"
+#include "base/task/thread_pool.h"
+#include "base/test/bind.h"
+#include "base/test/gtest_util.h"
+#include "base/test/task_environment.h"
+#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace base {
+
+namespace internal {
+class WorkerThread;
+}
+
+namespace {
+constexpr int kSamplesPerProfile = 20;
+constexpr TimeDelta kSamplingInterval = Milliseconds(100);
+constexpr TimeDelta kTimeToNextCollection = Hours(1);
+}  // namespace
+
+class MockPeriodicSamplingScheduler : public PeriodicSamplingScheduler {
+ public:
+  explicit MockPeriodicSamplingScheduler(TimeDelta time_to_next_collection)
+      : PeriodicSamplingScheduler(Seconds(30), 0.02, TimeTicks::Now()),
+        time_to_next_collection_(time_to_next_collection) {}
+  TimeDelta GetTimeToNextCollection() override {
+    return time_to_next_collection_;
+  }
+
+ protected:
+  TimeDelta time_to_next_collection_;
+};
+
+class MockProfileBuilder : public ProfileBuilder {
+ public:
+  explicit MockProfileBuilder(OnceClosure completed_callback)
+      : completed_callback_(std::move(completed_callback)) {}
+  void OnProfileCompleted(TimeDelta profile_duration,
+                          TimeDelta sampling_period) override {
+    std::move(completed_callback_).Run();
+  }
+  ModuleCache* GetModuleCache() override { return &module_cache_; }
+  MOCK_METHOD2(OnSampleCompleted,
+               void(std::vector<Frame> frames, TimeTicks sample_timestamp));
+
+ protected:
+  ModuleCache module_cache_;
+  // Callback made when sampling a profile completes.
+  OnceClosure completed_callback_;
+};
+
+class MockThreadGroupProfilerClient : public ThreadGroupProfilerClient {
+ public:
+  MockThreadGroupProfilerClient() = default;
+  StackSamplingProfiler::SamplingParams GetSamplingParams() override {
+    return {.samples_per_profile = kSamplesPerProfile,
+            .sampling_interval = kSamplingInterval};
+  }
+  std::unique_ptr<ProfileBuilder> CreateProfileBuilder(
+      OnceClosure callback) override {
+    return std::make_unique<MockProfileBuilder>(std::move(callback));
+  }
+  bool IsProfilerEnabledForCurrentProcess() override { return true; }
+  bool IsSingleProcess(const CommandLine& command_line) override {
+    return false;
+  }
+  StackSamplingProfiler::UnwindersFactory GetUnwindersFactory() override {
+    return CreateCoreUnwindersFactoryForTesting(nullptr);
+  }
+};
+
+class MockProfiler : public ThreadGroupProfiler::Profiler {
+ public:
+  MockProfiler(std::map<PlatformThreadId, MockProfiler*>& sampling_profilers,
+               int& sampling_profilers_created,
+               PlatformThreadId target_thread_id,
+               const StackSamplingProfiler::SamplingParams& params,
+               std::unique_ptr<ProfileBuilder> profile_builder)
+      : sampling_profilers_(sampling_profilers),
+        sampling_profilers_created_(sampling_profilers_created),
+        target_thread_id_(target_thread_id),
+        sampling_params_(params),
+        profile_builder_(std::move(profile_builder)) {
+    EXPECT_EQ(sampling_profilers_->count(target_thread_id_), 0);
+    (*sampling_profilers_)[target_thread_id_] = this;
+    ++*sampling_profilers_created_;
+    EXPECT_CALL(*this, Start());
+  }
+
+  ~MockProfiler() override { sampling_profilers_->erase(target_thread_id_); }
+
+  // ThreadGroupProfiler::Profiler:
+  MOCK_METHOD(void, Start, (), (override));
+
+  const StackSamplingProfiler::SamplingParams& sampling_params() const {
+    return sampling_params_;
+  }
+
+  void CompleteProfiling() {
+    profile_builder_->OnProfileCompleted(TimeDelta(), TimeDelta());
+  }
+
+ private:
+  raw_ref<std::map<PlatformThreadId, MockProfiler*>> sampling_profilers_;
+  raw_ref<int> sampling_profilers_created_;
+  PlatformThreadId target_thread_id_;
+  const StackSamplingProfiler::SamplingParams sampling_params_;
+  std::unique_ptr<ProfileBuilder> profile_builder_;
+};
+
+ThreadGroupProfiler::ProfilerFactory GetMockProfilerFactory(
+    std::map<PlatformThreadId, MockProfiler*>& sampling_profilers,
+    int& sampling_profilers_created) {
+  return BindRepeating(BindLambdaForTesting(
+      [&sampling_profilers, &sampling_profilers_created](
+          SamplingProfilerThreadToken thread_token,
+          const StackSamplingProfiler::SamplingParams& params,
+          std::unique_ptr<ProfileBuilder> profile_builder,
+          StackSamplingProfiler::UnwindersFactory unwinder_factory)
+          -> std::unique_ptr<ThreadGroupProfiler::Profiler> {
+        return std::make_unique<MockProfiler>(
+            sampling_profilers, sampling_profilers_created, thread_token.id,
+            params, std::move(profile_builder));
+      }));
+}
+
+class ThreadGroupProfilerTest : public testing::Test {
+ public:
+  void SetUp() override {
+    ThreadGroupProfiler::SetClient(
+        std::make_unique<MockThreadGroupProfilerClient>());
+    profiler_ = std::make_unique<ThreadGroupProfiler>(
+        ThreadPool::CreateSequencedTaskRunner({}), "Test",
+        std::make_unique<MockPeriodicSamplingScheduler>(kTimeToNextCollection),
+        GetMockProfilerFactory(sampling_profilers_,
+                               sampling_profilers_created_));
+  }
+
+  void TearDown() override {
+    task_environment_.reset();
+    if (!shutdown_started_) {
+      profiler_->Shutdown();
+    }
+    ThreadGroupProfiler::SetClient(nullptr);
+  }
+
+ protected:
+  class FakeWorkerThread : public Thread {
+   public:
+    FakeWorkerThread(internal::WorkerThread* fake_pointer,
+                     test::TaskEnvironment& task_environment)
+        : Thread("FakeWorkerThread"),
+          fake_pointer_(fake_pointer),
+          task_environment_(task_environment) {
+      Start();
+    }
+
+    ~FakeWorkerThread() override { Stop(); }
+
+    void RunOnThread(FunctionRef<void(internal::WorkerThread*)> callable) {
+      // First, synchronously execute the callable on the Thread's task runner.
+      WaitableEvent callable_completed{
+          WaitableEvent::ResetPolicy::MANUAL,
+          WaitableEvent::InitialState::NOT_SIGNALED};
+
+      RunOnThreadAsync(callable, callable_completed);
+
+      callable_completed.Wait();
+
+      // Then, ensure any thread pool tasks that the profiler posted run to
+      // completion. The thread pool is configured to run tasks eagerly via
+      // ThreadPoolExecutionMode::ASYNC, so tasks may already be executing. This
+      // call ensures they finish.
+      task_environment_->RunUntilIdle();
+    }
+
+    void RunOnThreadAsync(FunctionRef<void(internal::WorkerThread*)> callable,
+                          WaitableEvent& callable_completed) {
+      task_runner()->PostTask(
+          FROM_HERE, BindLambdaForTesting([callable, &callable_completed,
+                                           fake_pointer = fake_pointer_] {
+            callable(fake_pointer);
+            callable_completed.Signal();
+          }));
+    }
+
+   private:
+    raw_ptr<internal::WorkerThread> const fake_pointer_;
+    raw_ref<test::TaskEnvironment> task_environment_;
+  };
+
+  std::unique_ptr<FakeWorkerThread> CreateFakeWorkerThread() {
+    return std::make_unique<FakeWorkerThread>(
+        reinterpret_cast<internal::WorkerThread*>(next_worker_thread_id_++),
+        *task_environment_);
+  }
+
+  void InitiateNextCollection() {
+    task_environment_->FastForwardBy(time_to_next_collection_);
+    task_environment_->RunUntilIdle();
+    time_to_next_collection_ = kTimeToNextCollection;
+  }
+
+  void AdvanceBySamples(int samples) {
+    const TimeDelta samples_duration = kSamplingInterval * samples;
+    task_environment_->FastForwardBy(samples_duration);
+    task_environment_->RunUntilIdle();
+    time_to_next_collection_ -= samples_duration;
+  }
+
+  void AdvanceToEndOfCollection() {
+    const TimeDelta duration_already_advanced =
+        kTimeToNextCollection - time_to_next_collection_;
+    const TimeDelta collection_duration =
+        kSamplingInterval * kSamplesPerProfile;
+    const TimeDelta duration_to_end_of_collection =
+        collection_duration - duration_already_advanced;
+    task_environment_->FastForwardBy(duration_to_end_of_collection);
+    task_environment_->RunUntilIdle();
+    time_to_next_collection_ -= duration_to_end_of_collection;
+  }
+
+  void CompleteProfiling(MockProfiler* profiler) {
+    profiler->CompleteProfiling();
+    task_environment_->RunUntilIdle();
+  }
+
+  // Optional to support destruction prior to profiler_.
+  std::optional<test::TaskEnvironment> task_environment_{
+      std::in_place, test::TaskEnvironment::TimeSource::MOCK_TIME,
+      test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC};
+  std::map<PlatformThreadId, MockProfiler*> sampling_profilers_;
+  int sampling_profilers_created_ = 0;
+  std::unique_ptr<ThreadGroupProfiler> profiler_;
+  ModuleCache module_cache_;
+  int next_worker_thread_id_ = 1;
+  TimeDelta time_to_next_collection_ = kTimeToNextCollection;
+  bool shutdown_started_ = false;
+};
+
+TEST_F(ThreadGroupProfilerTest, Construction) {
+  EXPECT_TRUE(sampling_profilers_.empty());
+}
+
+TEST_F(ThreadGroupProfilerTest, CollectionInactive_WorkerInactiveLifecycle) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadExiting(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+}
+
+TEST_F(ThreadGroupProfilerTest, CollectionInactive_WorkerActiveLifecycle) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadIdle(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadExiting(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+}
+
+TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_NoWorkers) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+}
+
+TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_WorkerExited) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadExiting(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+}
+
+TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_InactiveWorker) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+}
+
+TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_NewlyInactiveWorker) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadIdle(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+}
+
+TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_ActiveWorker) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  InitiateNextCollection();
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+  ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
+              sampling_profilers_.end());
+  MockProfiler* const sampling_profiler =
+      sampling_profilers_[worker->GetThreadId()];
+  EXPECT_EQ(sampling_profiler->sampling_params().samples_per_profile,
+            kSamplesPerProfile);
+  EXPECT_EQ(sampling_profiler->sampling_params().sampling_interval,
+            kSamplingInterval);
+}
+
+TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_ReactivatedWorker) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadIdle(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  InitiateNextCollection();
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+  ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
+              sampling_profilers_.end());
+  MockProfiler* const sampling_profiler =
+      sampling_profilers_[worker->GetThreadId()];
+  EXPECT_EQ(sampling_profiler->sampling_params().samples_per_profile,
+            kSamplesPerProfile);
+  EXPECT_EQ(sampling_profiler->sampling_params().sampling_interval,
+            kSamplingInterval);
+}
+
+TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_MultipleWorkers) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+  std::unique_ptr<FakeWorkerThread> worker2 = CreateFakeWorkerThread();
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  worker2->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  worker2->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  InitiateNextCollection();
+  EXPECT_EQ(sampling_profilers_.size(), 2);
+  ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
+              sampling_profilers_.end());
+  ASSERT_TRUE(sampling_profilers_.find(worker2->GetThreadId()) !=
+              sampling_profilers_.end());
+  MockProfiler* const sampling_profiler =
+      sampling_profilers_[worker->GetThreadId()];
+  EXPECT_EQ(sampling_profiler->sampling_params().samples_per_profile,
+            kSamplesPerProfile);
+  EXPECT_EQ(sampling_profiler->sampling_params().sampling_interval,
+            kSamplingInterval);
+  MockProfiler* const sampling_profiler2 =
+      sampling_profilers_[worker2->GetThreadId()];
+  EXPECT_EQ(sampling_profiler2->sampling_params().samples_per_profile,
+            kSamplesPerProfile);
+  EXPECT_EQ(sampling_profiler2->sampling_params().sampling_interval,
+            kSamplingInterval);
+}
+
+TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_WorkerBecomesActive) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+  ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
+              sampling_profilers_.end());
+  MockProfiler* const sampling_profiler =
+      sampling_profilers_[worker->GetThreadId()];
+  EXPECT_EQ(sampling_profiler->sampling_params().samples_per_profile,
+            kSamplesPerProfile);
+  EXPECT_EQ(sampling_profiler->sampling_params().sampling_interval,
+            kSamplingInterval);
+}
+
+TEST_F(ThreadGroupProfilerTest, CollectionActive_WorkerInactiveLifecycle) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadExiting(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+}
+
+TEST_F(ThreadGroupProfilerTest, CollectionActive_WorkerActiveStartsProfiling) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+  ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
+              sampling_profilers_.end());
+  MockProfiler* const sampling_profiler =
+      sampling_profilers_[worker->GetThreadId()];
+  EXPECT_EQ(sampling_profiler->sampling_params().samples_per_profile,
+            kSamplesPerProfile);
+  EXPECT_EQ(sampling_profiler->sampling_params().sampling_interval,
+            kSamplingInterval);
+}
+
+TEST_F(ThreadGroupProfilerTest,
+       CollectionActive_WorkerReactivatedContinuesExistingProfiling) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadIdle(worker_thread);
+  });
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+  EXPECT_EQ(sampling_profilers_created_, 1);
+}
+
+TEST_F(ThreadGroupProfilerTest,
+       CollectionActive_WorkerActiveToIdleContinuesProfiling) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadIdle(worker_thread);
+  });
+
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+  EXPECT_EQ(sampling_profilers_created_, 1);
+  ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
+              sampling_profilers_.end());
+}
+
+TEST_F(ThreadGroupProfilerTest,
+       CollectionActive_WorkerActiveToExitStopsProfiling) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadExiting(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+}
+
+TEST_F(ThreadGroupProfilerTest,
+       CollectionActive_CollectionContinuesOnWorkerExit) {
+  std::unique_ptr<FakeWorkerThread> worker_to_exit = CreateFakeWorkerThread();
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  // Start a collection and make the worker thread active to start profiling,
+  // then have the worker exit.
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker_to_exit->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker_to_exit->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+
+  worker_to_exit->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadExiting(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  // Make a new worker thread active. It should start profiling as part of the
+  // collection.
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+}
+
+TEST_F(ThreadGroupProfilerTest,
+       CollectionActive_WorkerProfilesOnlyUntilEndOfCollection) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  AdvanceBySamples(5);
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+  ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
+              sampling_profilers_.end());
+  MockProfiler* const sampling_profiler =
+      sampling_profilers_[worker->GetThreadId()];
+  EXPECT_EQ(sampling_profiler->sampling_params().samples_per_profile,
+            kSamplesPerProfile - 5);
+  EXPECT_EQ(sampling_profiler->sampling_params().sampling_interval,
+            kSamplingInterval);
+}
+
+TEST_F(ThreadGroupProfilerTest,
+       CollectionActive_WorkerDoesNotProfileNearEndOfCollection) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  AdvanceBySamples(kSamplesPerProfile - 5);
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+}
+
+TEST_F(ThreadGroupProfilerTest, CollectionEnded_NoWorkers) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  AdvanceToEndOfCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  // Making the worker active should not result in any new profiling.
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+}
+
+TEST_F(ThreadGroupProfilerTest,
+       CollectionEnded_ActiveWorker_FinishesBeforeCollection) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  // Start a collection and make the worker thread active to start profiling.
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+
+  // Advance to just before the end of the collection period, and have the
+  // profiler complete.
+  AdvanceBySamples(kSamplesPerProfile - 1);
+
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+  ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
+              sampling_profilers_.end());
+  CompleteProfiling(sampling_profilers_[worker->GetThreadId()]);
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  // Complete the collection.
+  AdvanceToEndOfCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  // Make the thread idle then active again. This should not result in any new
+  // profiling.
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadIdle(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+}
+
+TEST_F(ThreadGroupProfilerTest,
+       CollectionEnded_ActiveWorker_FinishesAfterCollection) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  // Start a collection and make the worker thread active to start profiling.
+  InitiateNextCollection();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+
+  // Advance to the end of the collection. The worker profiling should still be
+  // taking place since it hasn't completed yet.
+  AdvanceToEndOfCollection();
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+
+  // Complete the worker profiling.
+  EXPECT_EQ(sampling_profilers_.size(), 1);
+  ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
+              sampling_profilers_.end());
+  CompleteProfiling(sampling_profilers_[worker->GetThreadId()]);
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  // Make the thread idle then active again. This should not result in any new
+  // profiling.
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadIdle(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  });
+  EXPECT_TRUE(sampling_profilers_.empty());
+}
+
+// Ensure that worker thread calls post task runner shutdown have no effect.
+TEST_F(ThreadGroupProfilerTest, PostTaskRunnerShutdown) {
+  std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
+
+  // Shut down the task runner by destroying the TaskEnvironment.
+  task_environment_.reset();
+
+  WaitableEvent on_thread_call_completed{
+      WaitableEvent::ResetPolicy::AUTOMATIC,
+      WaitableEvent::InitialState::NOT_SIGNALED};
+
+  auto worker_started = [this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadStarted(worker_thread);
+  };
+  worker->RunOnThreadAsync(worker_started, on_thread_call_completed);
+
+  on_thread_call_completed.Wait();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  auto worker_active = [this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadActive(worker_thread);
+  };
+  worker->RunOnThreadAsync(worker_active, on_thread_call_completed);
+
+  on_thread_call_completed.Wait();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  auto worker_idle = [this](internal::WorkerThread* worker_thread) {
+    profiler_->OnWorkerThreadIdle(worker_thread);
+  };
+  worker->RunOnThreadAsync(worker_idle, on_thread_call_completed);
+
+  on_thread_call_completed.Wait();
+  EXPECT_TRUE(sampling_profilers_.empty());
+
+  // When the task runner has been shut down, OnWorkerThreadExiting depends on
+  // ThreadGroupProfiler::Shutdown() being invoked to know that the thread's
+  // profiling has ceased. Choreograph shutdown to mimic those steps.
+  auto worker_exit =
+      [profiler = profiler_.get()](internal::WorkerThread* worker_thread) {
+        profiler->OnWorkerThreadExiting(worker_thread);
+      };
+  worker->RunOnThreadAsync(worker_exit, on_thread_call_completed);
+
+  profiler_->Shutdown();
+  shutdown_started_ = true;
+  on_thread_call_completed.Wait();
+  EXPECT_TRUE(sampling_profilers_.empty());
+}
+
+}  // namespace base
diff --git a/base/task/thread_pool/thread_group_impl.cc b/base/task/thread_pool/thread_group_impl.cc
index ecb2669..581569f 100644
--- a/base/task/thread_pool/thread_group_impl.cc
+++ b/base/task/thread_pool/thread_group_impl.cc
@@ -9,6 +9,7 @@
 
 #include "base/auto_reset.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/profiler/thread_group_profiler.h"
 #include "base/sequence_token.h"
 #include "base/strings/stringprintf.h"
 #include "base/task/common/checked_lock.h"
@@ -234,6 +235,13 @@
       service_thread_task_runner, worker_thread_observer, worker_environment,
       synchronous_thread_start_for_testing, may_block_threshold);
 
+  // Create thread group profiler if profiling is enabled after the thread group
+  // start but before worker threads are created.
+  if (ThreadGroupProfiler::IsProfilingEnabled()) {
+    thread_group_profiler_.emplace(service_thread_task_runner,
+                                   thread_group_label_);
+  }
+
   ScopedCommandsExecutor executor(this);
   CheckedAutoLock auto_lock(lock_);
   DCHECK(workers_.empty());
@@ -330,10 +338,15 @@
   worker_only().worker_thread_ = static_cast<WorkerThread*>(worker);
   SetBlockingObserverForCurrentThread(this);
 
+  if (outer_->thread_group_profiler_) {
+    outer_->thread_group_profiler_->OnWorkerThreadStarted(worker);
+  }
+
   if (outer_->worker_started_for_testing_) {
-    // When |worker_started_for_testing_| is set, the thread that starts workers
-    // should wait for a worker to have started before starting the next one,
-    // and there should only be one thread that wakes up workers at a time.
+    // When |worker_started_for_testing_| is set, the thread that starts
+    // workers should wait for a worker to have started before starting the
+    // next one, and there should only be one thread that wakes up workers at
+    // a time.
     DCHECK(!outer_->worker_started_for_testing_->IsSignaled());
     outer_->worker_started_for_testing_->Signal();
   }
@@ -363,6 +376,10 @@
   worker_only().win_thread_environment.reset();
 #endif  // BUILDFLAG(IS_WIN)
 
+  if (outer_->thread_group_profiler_) {
+    outer_->thread_group_profiler_->OnWorkerThreadExiting(worker_base);
+  }
+
   // Count cleaned up workers for tests. It's important to do this here
   // instead of at the end of CleanupLockRequired() because some side-effects
   // of cleaning up happen outside the lock (e.g. recording histograms) and
@@ -413,8 +430,22 @@
   DCHECK(!read_worker().current_shutdown_behavior);
 
   ScopedCommandsExecutor executor(outer_.get());
-  CheckedAutoLock auto_lock(outer_->lock_);
-  return GetWorkLockRequired(&executor, worker);
+  RegisteredTaskSource task_source;
+  {
+    CheckedAutoLock auto_lock(outer_->lock_);
+    task_source = GetWorkLockRequired(&executor, worker);
+  }
+  // Notify the profiler on the worker thread status when profiling is enabled.
+  // This must be called without holding lock_ as lock_ is not a universal
+  // predecessor that does not satisfy OnWorkerThreadIdle's CheckedLock.
+  if (outer_->thread_group_profiler_) {
+    // GetWork is only called when waking up, i.e. from an idle state. No need
+    // to mark it idle again if no task source available.
+    if (task_source) {
+      outer_->thread_group_profiler_->OnWorkerThreadActive(worker);
+    }
+  }
+  return task_source;
 }
 
 RegisteredTaskSource ThreadGroupImpl::WorkerDelegate::GetWorkLockRequired(
@@ -500,39 +531,47 @@
 
   ScopedCommandsExecutor workers_executor(outer_.get());
   ScopedReenqueueExecutor reenqueue_executor;
-  CheckedAutoLock auto_lock(outer_->lock_);
+  RegisteredTaskSource next_task_source;
+  {
+    CheckedAutoLock auto_lock(outer_->lock_);
 
-  // During shutdown, max_tasks may have been incremented in
-  // OnShutdownStartedLockRequired().
-  if (incremented_max_tasks_for_shutdown_) {
-    DCHECK(outer_->shutdown_started_);
-    outer_->DecrementMaxTasksLockRequired();
-    if (*read_worker().current_task_priority == TaskPriority::BEST_EFFORT) {
-      outer_->DecrementMaxBestEffortTasksLockRequired();
+    // During shutdown, max_tasks may have been incremented in
+    // OnShutdownStartedLockRequired().
+    if (incremented_max_tasks_for_shutdown_) {
+      DCHECK(outer_->shutdown_started_);
+      outer_->DecrementMaxTasksLockRequired();
+      if (*read_worker().current_task_priority == TaskPriority::BEST_EFFORT) {
+        outer_->DecrementMaxBestEffortTasksLockRequired();
+      }
+      incremented_max_tasks_since_blocked_ = false;
+      incremented_max_best_effort_tasks_since_blocked_ = false;
+      incremented_max_tasks_for_shutdown_ = false;
     }
-    incremented_max_tasks_since_blocked_ = false;
-    incremented_max_best_effort_tasks_since_blocked_ = false;
-    incremented_max_tasks_for_shutdown_ = false;
+
+    DCHECK(read_worker().blocking_start_time.is_null());
+    DCHECK(!incremented_max_tasks_since_blocked_);
+    DCHECK(!incremented_max_best_effort_tasks_since_blocked_);
+
+    // Running task bookkeeping.
+    outer_->DecrementTasksRunningLockRequired(
+        *read_worker().current_task_priority);
+    write_worker().current_shutdown_behavior = std::nullopt;
+    write_worker().current_task_priority = std::nullopt;
+
+    if (transaction_with_task_source) {
+      outer_->ReEnqueueTaskSourceLockRequired(
+          &workers_executor, &reenqueue_executor,
+          std::move(transaction_with_task_source.value()));
+    }
+
+    next_task_source = GetWorkLockRequired(&workers_executor,
+                                           static_cast<WorkerThread*>(worker));
   }
-
-  DCHECK(read_worker().blocking_start_time.is_null());
-  DCHECK(!incremented_max_tasks_since_blocked_);
-  DCHECK(!incremented_max_best_effort_tasks_since_blocked_);
-
-  // Running task bookkeeping.
-  outer_->DecrementTasksRunningLockRequired(
-      *read_worker().current_task_priority);
-  write_worker().current_shutdown_behavior = std::nullopt;
-  write_worker().current_task_priority = std::nullopt;
-
-  if (transaction_with_task_source) {
-    outer_->ReEnqueueTaskSourceLockRequired(
-        &workers_executor, &reenqueue_executor,
-        std::move(transaction_with_task_source.value()));
+  // Must be called without holding a lock.
+  if (outer_->thread_group_profiler_ && !task_source) {
+    outer_->thread_group_profiler_->OnWorkerThreadIdle(worker);
   }
-
-  return GetWorkLockRequired(&workers_executor,
-                             static_cast<WorkerThread*>(worker));
+  return next_task_source;
 }
 
 bool ThreadGroupImpl::WorkerDelegate::CanCleanupLockRequired(
@@ -755,6 +794,10 @@
 }
 
 void ThreadGroupImpl::JoinForTesting() {
+  // profiler needs to shutdown first to not block worker thread joins.
+  if (thread_group_profiler_) {
+    thread_group_profiler_->Shutdown();
+  }
   decltype(workers_) workers_copy;
   {
     CheckedAutoLock auto_lock(lock_);
@@ -859,6 +902,10 @@
     return;
   }
 
+  if (thread_group_profiler_) {
+    thread_group_profiler_->Shutdown();
+  }
+
   // Start a MAY_BLOCK scope on each worker that is already running a task.
   for (scoped_refptr<WorkerThread>& worker : workers_) {
     // The delegates of workers inside a ThreadGroupImpl should be
diff --git a/base/task/thread_pool/thread_group_impl.h b/base/task/thread_pool/thread_group_impl.h
index b015309..da2249f 100644
--- a/base/task/thread_pool/thread_group_impl.h
+++ b/base/task/thread_pool/thread_group_impl.h
@@ -11,6 +11,7 @@
 
 #include "base/base_export.h"
 #include "base/gtest_prod_util.h"
+#include "base/profiler/thread_group_profiler.h"
 #include "base/synchronization/condition_variable.h"
 #include "base/synchronization/waitable_event.h"
 #include "base/task/thread_pool/task_source.h"
@@ -23,6 +24,7 @@
 namespace base {
 
 class WorkerThreadObserver;
+class ThreadGroupProfiler;
 
 namespace internal {
 
@@ -141,6 +143,12 @@
   // https://crbug.com/810464. Uses AtomicRefCount to make its only public
   // method thread-safe.
   TrackedRefFactory<ThreadGroupImpl> tracked_ref_factory_;
+
+  // This is set in Start() if profiling is enabled, before any worker thread is
+  // created. If profiling is not enabled, this will remain std::nullopt. If
+  // created the ThreadGroupProfiler instance will exist until ThreadGroupImpl
+  // destruction.
+  std::optional<ThreadGroupProfiler> thread_group_profiler_;
 };
 
 }  // namespace internal
diff --git a/base/task/thread_pool/thread_pool_impl.cc b/base/task/thread_pool/thread_pool_impl.cc
index eba8f8cc..66f9dda 100644
--- a/base/task/thread_pool/thread_pool_impl.cc
+++ b/base/task/thread_pool/thread_pool_impl.cc
@@ -338,6 +338,8 @@
 
   // Ensures that there are enough background worker to run BLOCK_SHUTDOWN
   // tasks.
+  // Shutdown must happen after service thread STOP as ThreadGroupProfiler
+  // destructor expects exclusive access to the instance during destruction.
   foreground_thread_group_->OnShutdownStarted();
   if (utility_thread_group_) {
     utility_thread_group_->OnShutdownStarted();
diff --git a/base/test/task_environment.h b/base/test/task_environment.h
index 3d46bc8..0fe6347 100644
--- a/base/test/task_environment.h
+++ b/base/test/task_environment.h
@@ -126,7 +126,7 @@
   // This type will determine what types of messages will get pumped by the main
   // thread.
   // Note: If your test needs to use a custom MessagePump you should
-  // consider using a SingleThreadTaskExecutor instead.
+  // consider using a SingleThreadTaskEnvironment instead.
   enum class MainThreadType {
     // The main thread doesn't pump system messages.
     DEFAULT,
diff --git a/build/config/mac/rules.gni b/build/config/mac/rules.gni
index b69b7b7..b4ee8b8 100644
--- a/build/config/mac/rules.gni
+++ b/build/config/mac/rules.gni
@@ -6,6 +6,14 @@
 import("//build/config/apple/symbols.gni")
 import("//build/config/mac/mac_sdk.gni")
 
+_sanitizer_runtime_names = []
+if (is_asan) {
+  _sanitizer_runtime_names += [ "libclang_rt.asan_osx_dynamic.dylib" ]
+}
+if (is_ubsan_any) {
+  _sanitizer_runtime_names += [ "libclang_rt.ubsan_osx_dynamic.dylib" ]
+}
+
 # Generates Info.plist files for Mac apps and frameworks.
 #
 # Arguments
@@ -216,7 +224,8 @@
                      "Version=" + invoker.framework_version,
                      _output_name,
                    ] + invoker.framework_contents
-  _framework_contents = [ _output_name ] + invoker.framework_contents
+  _framework_contents = [ _output_name ] + invoker.sanitizer_runtime_names +
+                        invoker.framework_contents
   _framework_toc_file = "$target_out_dir/${target_name}.toc"
   write_file(_framework_toc_file, _framework_toc)
 
@@ -383,6 +392,7 @@
 
 set_defaults("mac_framework_bundle") {
   configs = default_shared_library_configs
+  sanitizer_runtime_names = _sanitizer_runtime_names
 }
 
 # Template to create a Mac executable application bundle.
diff --git a/cc/test/test_layer_tree_frame_sink.cc b/cc/test/test_layer_tree_frame_sink.cc
index e7348238..f06e2213 100644
--- a/cc/test/test_layer_tree_frame_sink.cc
+++ b/cc/test/test_layer_tree_frame_sink.cc
@@ -96,8 +96,7 @@
     return false;
 
   frame_sink_manager_ = std::make_unique<viz::FrameSinkManagerImpl>(
-      viz::FrameSinkManagerImpl::InitParams(
-          /*shared_bitmap_manager_.get()*/ nullptr));
+      viz::FrameSinkManagerImpl::InitParams());
   frame_sink_manager_->SetSharedImageInterfaceProviderForTest(
       shared_image_interface_provider_.get());
 
@@ -187,11 +186,6 @@
 }
 
 void TestLayerTreeFrameSink::DetachFromClient() {
-  // This acts like the |shared_bitmap_manager_| is a global object, while
-  // in fact it is tied to the lifetime of this class and is destroyed below:
-  // The shared_bitmap_manager_ has ownership of shared memory for each
-  // SharedBitmapId that has been reported from the client. Since the client is
-  // gone that memory can be freed. If we don't then it would leak.
   DebugScopedSetImplThread impl(task_runner_provider_);
 
   if (display_begin_frame_source_) {
diff --git a/cc/trees/layer_tree_frame_sink.h b/cc/trees/layer_tree_frame_sink.h
index 9844ede..338a3726 100644
--- a/cc/trees/layer_tree_frame_sink.h
+++ b/cc/trees/layer_tree_frame_sink.h
@@ -60,9 +60,8 @@
   // Optional and won't be used unless |worker_context_provider_wrapper| is
   // present.
   //
-  // |gpu_memory_buffer_manager| and |shared_bitmap_manager| must outlive the
-  // LayerTreeFrameSink. |shared_bitmap_manager| is optional (won't be used) if
-  // |context_provider| is present. |gpu_memory_buffer_manager| is optional
+  // |gpu_memory_buffer_manager|  must outlive the
+  // LayerTreeFrameSink. |gpu_memory_buffer_manager| is optional
   // (won't be used) unless |context_provider| is present.
   LayerTreeFrameSink(
       scoped_refptr<viz::RasterContextProvider> context_provider,
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 4889482..609dfb0 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2497,6 +2497,10 @@
         input_delegate_->IsHandlingTouchSequence();
   }
 
+  auto active_types = FrameSequenceTrackerActiveTypes();
+  metadata.is_handling_animation = HasMainThreadAnimation(active_types) ||
+                                   HasCompositorThreadAnimation(active_types);
+
   const base::flat_set<viz::SurfaceRange>& referenced_surfaces =
       active_tree_->SurfaceRanges();
   for (auto& surface_range : referenced_surfaces)
diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn
index 7ae2e00..9f046f2c 100644
--- a/chrome/BUILD.gn
+++ b/chrome/BUILD.gn
@@ -1652,6 +1652,7 @@
 
     deps = [
       ":dependencies",
+      "//chrome/browser/flags:flags_android",
       "//chrome/browser/ui",
       "//chrome/child",
       "//chrome/common",
@@ -1659,17 +1660,19 @@
       "//chrome/common/profiler",
       "//chrome/gpu",
       "//chrome/renderer",
+      "//components/crash/android:crash_android",
       "//components/minidump_uploader",
       "//components/safe_browsing:buildflags",
       "//components/safe_browsing/android:safe_browsing_api_handler",
       "//components/safe_browsing/android:safe_browsing_mobile",
       "//components/stylus_handwriting/android",
+      "//components/variations:variations_associated_data",
       "//content/public/app",
     ]
 
     # Explicit dependency required for JNI registration to be able to
     # find the native side functions.
-    if (is_android && is_component_build) {
+    if (is_component_build) {
       deps += [
         "//components/viz/service",
         "//device/gamepad",
@@ -1677,13 +1680,6 @@
       ]
     }
 
-    if (is_android) {
-      deps += [
-        "//chrome/browser/flags:flags_android",
-        "//components/crash/android:crash_android",
-      ]
-    }
-
     if (is_chromeos) {
       public_deps += [ "//ui/lottie" ]
       deps += [ "//chrome/browser/ash/schedqos" ]
diff --git a/chrome/VERSION b/chrome/VERSION
index b54b32c..b3002dcc 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=134
 MINOR=0
-BUILD=6964
+BUILD=6965
 PATCH=0
diff --git a/chrome/android/chrome_junit_test_java_sources.gni b/chrome/android/chrome_junit_test_java_sources.gni
index 72f4e769..a67b5df 100644
--- a/chrome/android/chrome_junit_test_java_sources.gni
+++ b/chrome/android/chrome_junit_test_java_sources.gni
@@ -42,6 +42,7 @@
   "junit/src/org/chromium/chrome/browser/app/tabmodel/TabPersistentStoreIntegrationTest.java",
   "junit/src/org/chromium/chrome/browser/app/tabmodel/TabbedModeTabModelOrchestratorUnitTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/AutofillImageFetcherTest.java",
+  "junit/src/org/chromium/chrome/browser/autofill/AutofillImageFetcherUtilsTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/AutofillSuggestionTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/AutofillUiUtilsTest.java",
   "junit/src/org/chromium/chrome/browser/autofill/PlusAddressesHelperTest.java",
diff --git a/chrome/android/features/tab_ui/java/res/layout/dynamic_bottom_tab_strip_toolbar.xml b/chrome/android/features/tab_ui/java/res/layout/dynamic_bottom_tab_strip_toolbar.xml
index c7a6fe77..be9c9da8 100644
--- a/chrome/android/features/tab_ui/java/res/layout/dynamic_bottom_tab_strip_toolbar.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/dynamic_bottom_tab_strip_toolbar.xml
@@ -106,6 +106,7 @@
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintTop_toTopOf="parent"
             app:layout_constraintVertical_bias="0.5"
-            app:layout_constraintEnd_toEndOf="parent" />
+            app:layout_constraintEnd_toEndOf="parent"
+            android:contentDescription="@string/accessibility_bottom_tab_strip_expand_tab_sheet" />
     </androidx.constraintlayout.widget.ConstraintLayout>
 </org.chromium.chrome.browser.tasks.tab_management.TabGroupUiToolbarView>
diff --git a/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_toolbar_two_row.xml b/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_toolbar_two_row.xml
index 95ed97b..6f2407a 100644
--- a/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_toolbar_two_row.xml
+++ b/chrome/android/features/tab_ui/java/res/layout/tab_grid_dialog_toolbar_two_row.xml
@@ -66,7 +66,8 @@
                 android:layout_height="@dimen/min_touch_target_size"
                 android:minWidth="@dimen/min_touch_target_size"
                 android:layout_marginEnd="12dp"
-                android:visibility="gone"/>
+                android:visibility="gone"
+                android:contentDescription="@string/manage_sharing_content_description"/>
             <include layout="@layout/toolbar_new_tab_and_menu_button"/>
         </LinearLayout>
         <LinearLayout
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ActionConfirmationManager.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ActionConfirmationManager.java
index 889c0a6..ee238984 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ActionConfirmationManager.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ActionConfirmationManager.java
@@ -92,7 +92,7 @@
                 R.string.delete_shared_tab_group_dialog_title,
                 R.string.delete_shared_tab_group_description,
                 groupTitle,
-                R.string.delete_tab_group_menu_item,
+                R.string.delete_tab_group_action,
                 onResult);
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPane.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPane.java
index 8487f6b..69191b200 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPane.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPane.java
@@ -11,6 +11,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import org.chromium.base.Callback;
 import org.chromium.base.CallbackController;
 import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.base.supplier.OneshotSupplier;
@@ -25,6 +26,7 @@
 import org.chromium.chrome.browser.hub.ResourceButtonData;
 import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthController;
 import org.chromium.chrome.browser.incognito.reauth.IncognitoReauthManager.IncognitoReauthCallback;
+import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.IncognitoTabModel;
 import org.chromium.chrome.browser.tabmodel.IncognitoTabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter;
@@ -35,6 +37,7 @@
 import org.chromium.chrome.tab_ui.R;
 import org.chromium.components.sensitive_content.SensitiveContentFeatures;
 
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.DoubleConsumer;
 
 /** A {@link Pane} representing the incognito tab switcher. */
@@ -48,14 +51,50 @@
 
                 @Override
                 public void didBecomeEmpty() {
-                    mReferenceButtonDataSupplier.set(null);
-                    if (isFocused()) {
-                        @Nullable PaneHubController controller = getPaneHubController();
-                        assert controller != null
-                                : "isFocused requires a non-null PaneHubController.";
-                        controller.focusPane(PaneId.TAB_SWITCHER);
-                    }
-                    destroyTabSwitcherPaneCoordinator();
+                    TabSwitcherPaneCoordinator paneCoordinator = getTabSwitcherPaneCoordinator();
+                    assert paneCoordinator != null;
+
+                    ObservableSupplier<Boolean> isAnimatingSupplier =
+                            paneCoordinator.getIsRecyclerViewAnimatorRunning();
+
+                    AtomicBoolean startedAnimating = new AtomicBoolean(false);
+
+                    // Create Callback object to allow us to pass a reference to said callback
+                    // inside the onResult method.
+                    Callback<Boolean> onAnimationStatusChange =
+                            new Callback<>() {
+                                @Override
+                                public void onResult(Boolean isAnimating) {
+                                    // This ensures that:
+                                    // a) The RecyclerView has started any final
+                                    //        animation prior to changing tab switcher panes.
+                                    // b) The animation only runs when the tab grid dialog is not
+                                    // visible.
+                                    Supplier<Boolean> dialogShowingOrAnimationSupplier =
+                                            paneCoordinator
+                                                    .getTabGridDialogShowingOrAnimationSupplier();
+                                    boolean isTabGridDialogVisible =
+                                            dialogShowingOrAnimationSupplier != null
+                                                    && dialogShowingOrAnimationSupplier.get();
+                                    if (!isTabGridDialogVisible
+                                            && (isAnimating || !startedAnimating.get())) {
+                                        startedAnimating.set(isAnimating);
+                                        return;
+                                    }
+                                    mReferenceButtonDataSupplier.set(null);
+                                    if (isFocused()) {
+                                        @Nullable
+                                        PaneHubController controller = getPaneHubController();
+                                        assert controller != null
+                                                : "isFocused requires a non-null"
+                                                        + " PaneHubController.";
+                                        controller.focusPane(PaneId.TAB_SWITCHER);
+                                    }
+                                    destroyTabSwitcherPaneCoordinator();
+                                    isAnimatingSupplier.removeObserver(this);
+                                }
+                            };
+                    isAnimatingSupplier.addObserver(onAnimationStatusChange);
                 }
             };
 
@@ -206,8 +245,8 @@
     }
 
     @Override
-    public int getCurrentTabId() {
-        return TabModelUtils.getCurrentTabId(
+    public @Nullable Tab getCurrentTab() {
+        return TabModelUtils.getCurrentTab(
                 mIncognitoTabGroupModelFilterSupplier.get().getTabModel());
     }
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPaneUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPaneUnitTest.java
index b0fb910..4bca061 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPaneUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/IncognitoTabSwitcherPaneUnitTest.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.tasks.tab_management;
 
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -87,6 +88,8 @@
             mIncognitoReauthControllerSupplier = new OneshotSupplierImpl<>();
     private final ObservableSupplierImpl<EdgeToEdgeController> mEdgeToEdgeSupplier =
             new ObservableSupplierImpl<>();
+    private final ObservableSupplierImpl<Boolean> mIsRecyclerViewAnimatorRunningSupplier =
+            new ObservableSupplierImpl<>(false);
 
     private Context mContext;
     private IncognitoTabSwitcherPane mIncognitoTabSwitcherPane;
@@ -115,6 +118,8 @@
 
         when(mTabGroupModelFilter.getTabModel()).thenReturn(mIncognitoTabModel);
         when(mTabGroupModelFilter.isTabModelRestored()).thenReturn(true);
+        when(mTabSwitcherPaneCoordinator.getIsRecyclerViewAnimatorRunning())
+                .thenReturn(mIsRecyclerViewAnimatorRunningSupplier);
 
         mIncognitoTabSwitcherPane =
                 new IncognitoTabSwitcherPane(
@@ -400,6 +405,10 @@
         IncognitoTabModelObserver observer = mIncognitoTabModelObserverCaptor.getValue();
 
         observer.didBecomeEmpty();
+        mIsRecyclerViewAnimatorRunningSupplier.set(true);
+        mIsRecyclerViewAnimatorRunningSupplier.set(false);
+        ShadowLooper.runUiThreadTasks();
+
         assertNull(mIncognitoTabSwitcherPane.getReferenceButtonDataSupplier().get());
         verify(mPaneHubController).focusPane(PaneId.TAB_SWITCHER);
         assertNull(mIncognitoTabSwitcherPane.getTabSwitcherPaneCoordinator());
@@ -416,7 +425,12 @@
                 buttonData.resolveContentDescription(mContext));
         assertNotNull(buttonData.resolveIcon(mContext));
 
+        mIncognitoTabSwitcherPane.createTabSwitcherPaneCoordinator();
         observer.didBecomeEmpty();
+        mIsRecyclerViewAnimatorRunningSupplier.set(true);
+        mIsRecyclerViewAnimatorRunningSupplier.set(false);
+        ShadowLooper.runUiThreadTasks();
+
         assertNull(mIncognitoTabSwitcherPane.getReferenceButtonDataSupplier().get());
         verify(mPaneHubController, times(2)).focusPane(PaneId.TAB_SWITCHER);
         assertNull(mIncognitoTabSwitcherPane.getTabSwitcherPaneCoordinator());
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
index dc5de93..f879ed3 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediator.java
@@ -1315,7 +1315,15 @@
                     new CollaborationActivityMessageCardViewModel(
                             mActivity,
                             this::showRecentActivityOrDismissActivityMessageCard,
-                            (unused) -> removeCollaborationActivityMessageCard());
+                            (unused) -> {
+                                @Nullable Token tabGroupId = getCurrentTabGroupId();
+                                if (mMessagingBackendService != null && tabGroupId != null) {
+                                    mMessagingBackendService.clearDirtyTabMessagesForGroup(
+                                            EitherGroupId.createLocalId(
+                                                    new LocalTabGroupId(tabGroupId)));
+                                }
+                                removeCollaborationActivityMessageCard();
+                            });
         }
         mCollaborationActivityPropertyModel.updateDescriptionText(
                 mActivity, tabsAdded, tabsChanged, tabsClosed);
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java
index eb55bf4..8a46b081 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupListCoordinator.java
@@ -185,12 +185,14 @@
             Callback<Drawable> callback) {
         Resources resources = context.getResources();
         int faviconSizePixels = resources.getDimensionPixelSize(R.dimen.tab_grid_favicon_size);
+        FaviconHelper faviconHelper = new FaviconHelper();
         FaviconImageCallback faviconImageCallback =
-                (Bitmap bitmap, GURL ignored) ->
-                        onForeignFavicon(context, fallbackProvider, callback, bitmap);
-        new FaviconHelper()
-                .getForeignFaviconImageForURL(
-                        profile, url, faviconSizePixels, faviconImageCallback);
+                (Bitmap bitmap, GURL ignored) -> {
+                    onForeignFavicon(context, fallbackProvider, callback, bitmap);
+                    faviconHelper.destroy();
+                };
+        faviconHelper.getForeignFaviconImageForURL(
+                profile, url, faviconSizePixels, faviconImageCallback);
     }
 
     private static void onForeignFavicon(
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListItemAnimator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListItemAnimator.java
index a82665e..e9c962f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListItemAnimator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListItemAnimator.java
@@ -16,10 +16,13 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 import androidx.recyclerview.widget.SimpleItemAnimator;
 
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.ui.interpolators.Interpolators;
 import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter;
 
@@ -133,13 +136,20 @@
         }
     }
 
-    private AnimatorHolder mAdds = new AnimatorHolder("Add");
-    private AnimatorHolder mChanges = new AnimatorHolder("Change");
-    private AnimatorHolder mMoves = new AnimatorHolder("Move");
-    private AnimatorHolder mRemovals = new AnimatorHolder("Removal");
+    private final AnimatorHolder mAdds = new AnimatorHolder("Add");
+    private final AnimatorHolder mChanges = new AnimatorHolder("Change");
+    private final AnimatorHolder mMoves = new AnimatorHolder("Move");
+    private final AnimatorHolder mRemovals = new AnimatorHolder("Removal");
+    private final @NonNull ObservableSupplierImpl<Boolean> mIsAnimatorRunningSupplier;
 
     TabListItemAnimator() {
+        this(new ObservableSupplierImpl<>(false));
+    }
+
+    @VisibleForTesting
+    TabListItemAnimator(@NonNull ObservableSupplierImpl<Boolean> isAnimatorRunningSupplier) {
         setRemoveDuration(DEFAULT_REMOVE_DURATION);
+        mIsAnimatorRunningSupplier = isAnimatorRunningSupplier;
     }
 
     @Override
@@ -227,6 +237,7 @@
                     @Override
                     public void onAnimationStart(Animator animator) {
                         dispatchAddStarting(holder);
+                        mIsAnimatorRunningSupplier.set(true);
                     }
 
                     @Override
@@ -310,6 +321,7 @@
                     @Override
                     public void onAnimationStart(Animator animator) {
                         dispatchChangeStarting(oldHolder, true);
+                        mIsAnimatorRunningSupplier.set(true);
                     }
 
                     @Override
@@ -346,6 +358,7 @@
                         @Override
                         public void onAnimationStart(Animator animator) {
                             dispatchChangeStarting(newHolder, false);
+                            mIsAnimatorRunningSupplier.set(true);
                         }
 
                         @Override
@@ -397,6 +410,7 @@
                     @Override
                     public void onAnimationStart(Animator animator) {
                         dispatchMoveStarting(holder);
+                        mIsAnimatorRunningSupplier.set(true);
                     }
 
                     @Override
@@ -454,6 +468,7 @@
                     @Override
                     public void onAnimationStart(Animator animator) {
                         dispatchRemoveStarting(holder);
+                        mIsAnimatorRunningSupplier.set(true);
                     }
 
                     @Override
@@ -497,6 +512,7 @@
                     @Override
                     public void onAnimationStart(Animator animator) {
                         dispatchRemoveStarting(holder);
+                        mIsAnimatorRunningSupplier.set(true);
                     }
 
                     @Override
@@ -517,9 +533,14 @@
     void dispatchFinishedWhenAllAnimationsDone() {
         if (!isRunning()) {
             dispatchAnimationsFinished();
+            mIsAnimatorRunningSupplier.set(false);
         }
     }
 
+    public ObservableSupplier<Boolean> getIsAnimatorRunningSupplier() {
+        return mIsAnimatorRunningSupplier;
+    }
+
     private Interpolator getRearrangeInterpolator() {
         return Interpolators.STANDARD_INTERPOLATOR;
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListItemAnimatorUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListItemAnimatorUnitTest.java
index f4f1895..a397ad0 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListItemAnimatorUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListItemAnimatorUnitTest.java
@@ -32,11 +32,13 @@
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.Mockito;
+import org.mockito.Spy;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.base.Callback;
+import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.ModelType;
 import org.chromium.ui.modelutil.PropertyKey;
@@ -49,12 +51,16 @@
 public class TabListItemAnimatorUnitTest {
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
+    @Spy
+    private ObservableSupplierImpl<Boolean> mIsAnimatorRunningSupplier =
+            new ObservableSupplierImpl<>(false);
+
     @Mock private SimpleRecyclerViewAdapter mAdapter;
     private TabListItemAnimator mItemAnimator;
 
     @Before
     public void setUp() {
-        mItemAnimator = spy(new TabListItemAnimator());
+        mItemAnimator = spy(new TabListItemAnimator(mIsAnimatorRunningSupplier));
     }
 
     private static void emptyBind(PropertyModel model, View view, PropertyKey key) {}
@@ -442,4 +448,32 @@
         verify(mItemAnimator, times(4)).dispatchFinishedWhenAllAnimationsDone();
         assertFalse(mItemAnimator.isRunning());
     }
+
+    @Test
+    public void animatorRunningSupplier_RunAnimations() {
+        var removedHolder = buildViewHolder(TAB, /* useShrinkCloseAnimation= */ true);
+        mItemAnimator.animateAdd(removedHolder);
+
+        mItemAnimator.runPendingAnimations();
+        ShadowLooper.shadowMainLooper().idle();
+
+        InOrder inOrder = Mockito.inOrder(mIsAnimatorRunningSupplier);
+        inOrder.verify(mIsAnimatorRunningSupplier).set(true);
+        inOrder.verify(mIsAnimatorRunningSupplier).set(false);
+        assertFalse(mIsAnimatorRunningSupplier.get());
+    }
+
+    @Test
+    public void animatorRunningSupplier_EndAnimations() {
+        var removedHolder = buildViewHolder(TAB, /* useShrinkCloseAnimation= */ true);
+        mItemAnimator.animateAdd(removedHolder);
+
+        mItemAnimator.endAnimations();
+        ShadowLooper.shadowMainLooper().idle();
+
+        InOrder inOrder = Mockito.inOrder(mIsAnimatorRunningSupplier);
+        inOrder.verify(mIsAnimatorRunningSupplier).set(true);
+        inOrder.verify(mIsAnimatorRunningSupplier).set(false);
+        assertFalse(mIsAnimatorRunningSupplier.get());
+    }
 }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
index 13e01d4..f2c90a882 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
@@ -20,6 +20,7 @@
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 
+import org.chromium.base.supplier.ObservableSupplier;
 import org.chromium.chrome.browser.tab_ui.RecyclerViewPosition;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.tab_ui.R;
@@ -104,6 +105,14 @@
     }
 
     /**
+     * Returns a boolean indicating whether any animator in {@link TabListItemAnimator} is running.
+     */
+    @NonNull
+    ObservableSupplier<Boolean> getIsAnimatorRunningSupplier() {
+        return mTabListItemAnimator.getIsAnimatorRunningSupplier();
+    }
+
+    /**
      * @param tabIndex The index in the RecyclerView of the tab.
      * @param tabId The tab ID of the tab.
      * @return The {@link Rect} of the thumbnail of the tab in global coordinates.
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManager.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManager.java
index 872dbcc..8dfb14c1 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManager.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManager.java
@@ -15,7 +15,12 @@
 import org.chromium.chrome.browser.collaboration.messaging.MessagingBackendServiceFactory;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabCreationState;
+import org.chromium.chrome.browser.tab.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter;
+import org.chromium.chrome.browser.tabmodel.TabGroupModelFilterObserver;
 import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
 import org.chromium.components.collaboration.CollaborationService;
@@ -57,11 +62,56 @@
                 }
             };
 
+    // Any operation aside from grouping a tab for restoring from TabGroupSyncService should "just
+    // work" via the MessagingBackendService so no need to observe anything else.
+    private final TabGroupModelFilterObserver mTabGroupModelFilterObserver =
+            new TabGroupModelFilterObserver() {
+                @Override
+                public void didMergeTabToGroup(Tab movedTab) {
+                    maybeUpdateForTab(movedTab, /* mayAddDot= */ true);
+                }
+            };
+
+    // MessagingBackendService and TabGroupSyncService see tabs moving between the hidden and open
+    // states as essentially no-ops. This observer is necessary to ensure dots are updated
+    // correctly.
+    private final TabModelObserver mTabModelObserver =
+            new TabModelObserver() {
+                @Override
+                public void willCloseTab(Tab tab, boolean didCloseAlone) {
+                    maybeUpdateForTab(tab, /* mayAddDot= */ false);
+                }
+
+                @Override
+                public void onFinishingTabClosure(Tab tab) {
+                    maybeUpdateForTab(tab, /* mayAddDot= */ false);
+                }
+
+                @Override
+                public void tabRemoved(Tab tab) {
+                    maybeUpdateForTab(tab, /* mayAddDot= */ false);
+                }
+
+                @Override
+                public void tabClosureUndone(Tab tab) {
+                    maybeUpdateForTab(tab, /* mayAddDot= */ true);
+                }
+
+                @Override
+                public void didAddTab(
+                        Tab tab,
+                        @TabLaunchType int type,
+                        @TabCreationState int creationState,
+                        boolean markedForSelection) {
+                    maybeUpdateForTab(tab, /* mayAddDot= */ true);
+                }
+            };
+
     private final ObservableSupplierImpl<Boolean> mNotificationDotObservableSupplier =
             new ObservableSupplierImpl<>(false);
     private final CallbackController mCallbackController = new CallbackController();
     private @Nullable MessagingBackendService mMessagingBackendService;
-    private @Nullable TabModel mTabModel;
+    private @Nullable TabGroupModelFilter mTabGroupModelFilter;
     private boolean mTabModelSelectorInitialized;
     private boolean mMessagingBackendServiceInitialized;
 
@@ -72,10 +122,13 @@
      *     observed. However, the selector is needed to know when the tab model is initialized.
      */
     public void initWithNative(TabModelSelector tabModelSelector) {
-        mTabModel = tabModelSelector.getModel(/* incognito= */ false);
-        assert mTabModel != null : "TabModel & native should be initialized.";
+        mTabGroupModelFilter =
+                tabModelSelector
+                        .getTabGroupModelFilterProvider()
+                        .getTabGroupModelFilter(/* incognito= */ false);
+        assert mTabGroupModelFilter != null : "TabModel & native should be initialized.";
 
-        Profile profile = mTabModel.getProfile();
+        Profile profile = mTabGroupModelFilter.getTabModel().getProfile();
         CollaborationService collaborationService =
                 CollaborationServiceFactory.getForProfile(profile);
         if (!collaborationService.getServiceStatus().isAllowedToJoin()) return;
@@ -87,6 +140,9 @@
                 mCallbackController.makeCancelable(
                         unused -> {
                             mTabModelSelectorInitialized = true;
+                            mTabGroupModelFilter.addTabGroupObserver(mTabGroupModelFilterObserver);
+                            mTabGroupModelFilter.getTabModel().addObserver(mTabModelObserver);
+
                             computeUpdate();
                         }));
     }
@@ -105,6 +161,19 @@
         if (mMessagingBackendService != null) {
             mMessagingBackendService.removePersistentMessageObserver(mPersistentMessageObserver);
         }
+        if (mTabGroupModelFilter != null) {
+            mTabGroupModelFilter.removeTabGroupObserver(mTabGroupModelFilterObserver);
+            mTabGroupModelFilter.getTabModel().removeObserver(mTabModelObserver);
+        }
+    }
+
+    private void maybeUpdateForTab(Tab tab, boolean mayAddDot) {
+        @Nullable Boolean showingDot = mNotificationDotObservableSupplier.get();
+        boolean stateWillBeUnchanged = showingDot != null && showingDot == mayAddDot;
+        if (tab.getTabGroupId() == null || stateWillBeUnchanged) {
+            return;
+        }
+        computeUpdate();
     }
 
     private void computeUpdate() {
@@ -115,7 +184,9 @@
     }
 
     private boolean anyTabsInModelHaveDirtyBit() {
-        assert mTabModel != null && mMessagingBackendService != null;
+        assert mTabGroupModelFilter != null && mMessagingBackendService != null;
+
+        TabModel tabModel = mTabGroupModelFilter.getTabModel();
 
         List<PersistentMessage> messages =
                 mMessagingBackendService.getMessages(
@@ -124,7 +195,8 @@
             int tabId = MessageUtils.extractTabId(message);
             if (tabId == Tab.INVALID_TAB_ID) continue;
 
-            if (mTabModel.getTabById(tabId) != null) return true;
+            @Nullable Tab tab = tabModel.getTabById(tabId);
+            if (tab != null && !tab.isClosing()) return true;
         }
         return false;
     }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManagerUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManagerUnitTest.java
index dc6408d..74ca0ef 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManagerUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabModelNotificationDotManagerUnitTest.java
@@ -21,12 +21,19 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import org.chromium.base.Token;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.chrome.browser.collaboration.CollaborationServiceFactory;
 import org.chromium.chrome.browser.collaboration.messaging.MessagingBackendServiceFactory;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tab.TabCreationState;
+import org.chromium.chrome.browser.tab.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter;
+import org.chromium.chrome.browser.tabmodel.TabGroupModelFilterObserver;
+import org.chromium.chrome.browser.tabmodel.TabGroupModelFilterProvider;
 import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModelObserver;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
 import org.chromium.components.collaboration.CollaborationService;
@@ -47,11 +54,14 @@
 public class TabModelNotificationDotManagerUnitTest {
     private static final int EXISTING_TAB_ID = 5;
     private static final int NON_EXISTANT_TAB_ID = 7;
+    private static final Token TAB_GROUP_ID = new Token(378L, 4378L);
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Mock private Profile mProfile;
     @Mock private TabModelSelector mTabModelSelector;
+    @Mock private TabGroupModelFilterProvider mTabGroupModelFilterProvider;
+    @Mock private TabGroupModelFilter mTabGroupModelFilter;
     @Mock private TabModel mTabModel;
     @Mock private Tab mTab;
     @Mock private MessagingBackendService mMessagingBackendService;
@@ -60,6 +70,8 @@
 
     @Captor private ArgumentCaptor<PersistentMessageObserver> mPersistentMessageObserverCaptor;
     @Captor private ArgumentCaptor<TabModelSelectorObserver> mTabModelSelectorObserverCaptor;
+    @Captor private ArgumentCaptor<TabModelObserver> mTabModelObserverCaptor;
+    @Captor private ArgumentCaptor<TabGroupModelFilterObserver> mTabGroupModelFilterObserverCaptor;
 
     private TabModelNotificationDotManager mTabModelNotificationDotManager;
     private PersistentMessage mDirtyTabMessage = new PersistentMessage();
@@ -76,7 +88,11 @@
         MessagingBackendServiceFactory.setForTesting(mMessagingBackendService);
 
         when(mTabModelSelector.isTabStateInitialized()).thenReturn(false);
-        when(mTabModelSelector.getModel(false)).thenReturn(mTabModel);
+        when(mTabModelSelector.getTabGroupModelFilterProvider())
+                .thenReturn(mTabGroupModelFilterProvider);
+        when(mTabGroupModelFilterProvider.getTabGroupModelFilter(false))
+                .thenReturn(mTabGroupModelFilter);
+        when(mTabGroupModelFilter.getTabModel()).thenReturn(mTabModel);
         when(mTabModel.getProfile()).thenReturn(mProfile);
         when(mTabModel.getTabById(EXISTING_TAB_ID)).thenReturn(mTab);
 
@@ -109,6 +125,9 @@
         createDirtyTabMessageForIds(List.of(EXISTING_TAB_ID));
 
         mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized();
+        verify(mTabModel).addObserver(mTabModelObserverCaptor.capture());
+        verify(mTabGroupModelFilter)
+                .addTabGroupObserver(mTabGroupModelFilterObserverCaptor.capture());
         assertFalse(mTabModelNotificationDotManager.getNotificationDotObservableSupplier().get());
 
         mPersistentMessageObserverCaptor.getValue().onMessagingBackendServiceInitialized();
@@ -123,6 +142,9 @@
         assertFalse(mTabModelNotificationDotManager.getNotificationDotObservableSupplier().get());
 
         mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized();
+        verify(mTabModel).addObserver(mTabModelObserverCaptor.capture());
+        verify(mTabGroupModelFilter)
+                .addTabGroupObserver(mTabGroupModelFilterObserverCaptor.capture());
         assertTrue(mTabModelNotificationDotManager.getNotificationDotObservableSupplier().get());
     }
 
@@ -183,10 +205,68 @@
         assertTrue(mTabModelNotificationDotManager.getNotificationDotObservableSupplier().get());
     }
 
+    @Test
+    public void testComputeUpdateTabGroupModelFilterObserver() {
+        initializeBothBackends();
+        createDirtyTabMessageForIds(List.of(EXISTING_TAB_ID));
+
+        mTabGroupModelFilterObserverCaptor.getValue().didMergeTabToGroup(mTab);
+        assertFalse(mTabModelNotificationDotManager.getNotificationDotObservableSupplier().get());
+    }
+
+    @Test
+    public void testComputeUpdateTabModelObserver() {
+        initializeBothBackends();
+        createDirtyTabMessageForIds(List.of(EXISTING_TAB_ID));
+
+        mTabModelObserverCaptor
+                .getValue()
+                .didAddTab(
+                        mTab,
+                        TabLaunchType.FROM_SYNC_BACKGROUND,
+                        TabCreationState.LIVE_IN_BACKGROUND,
+                        /* markedForSelection= */ false);
+        assertFalse(mTabModelNotificationDotManager.getNotificationDotObservableSupplier().get());
+
+        when(mTab.getTabGroupId()).thenReturn(TAB_GROUP_ID);
+
+        mTabModelObserverCaptor
+                .getValue()
+                .didAddTab(
+                        mTab,
+                        TabLaunchType.FROM_SYNC_BACKGROUND,
+                        TabCreationState.LIVE_IN_BACKGROUND,
+                        /* markedForSelection= */ false);
+        assertTrue(mTabModelNotificationDotManager.getNotificationDotObservableSupplier().get());
+
+        when(mTabModel.getTabById(EXISTING_TAB_ID)).thenReturn(null);
+        mTabModelObserverCaptor.getValue().tabRemoved(mTab);
+        assertFalse(mTabModelNotificationDotManager.getNotificationDotObservableSupplier().get());
+
+        when(mTabModel.getTabById(EXISTING_TAB_ID)).thenReturn(mTab);
+        mTabModelObserverCaptor.getValue().tabClosureUndone(mTab);
+        assertTrue(mTabModelNotificationDotManager.getNotificationDotObservableSupplier().get());
+
+        when(mTabModel.getTabById(EXISTING_TAB_ID)).thenReturn(null);
+        mTabModelObserverCaptor.getValue().onFinishingTabClosure(mTab);
+        assertFalse(mTabModelNotificationDotManager.getNotificationDotObservableSupplier().get());
+
+        when(mTabModel.getTabById(EXISTING_TAB_ID)).thenReturn(mTab);
+        mTabModelObserverCaptor.getValue().tabClosureUndone(mTab);
+        assertTrue(mTabModelNotificationDotManager.getNotificationDotObservableSupplier().get());
+
+        when(mTabModel.getTabById(EXISTING_TAB_ID)).thenReturn(null);
+        mTabModelObserverCaptor.getValue().willCloseTab(mTab, true);
+        assertFalse(mTabModelNotificationDotManager.getNotificationDotObservableSupplier().get());
+    }
+
     private void initializeBothBackends() {
         when(mTabModelSelector.isTabStateInitialized()).thenReturn(true);
         mPersistentMessageObserverCaptor.getValue().onMessagingBackendServiceInitialized();
         mTabModelSelectorObserverCaptor.getValue().onTabStateInitialized();
+        verify(mTabModel).addObserver(mTabModelObserverCaptor.capture());
+        verify(mTabGroupModelFilter)
+                .addTabGroupObserver(mTabGroupModelFilterObserverCaptor.capture());
     }
 
     private void createDirtyTabMessageForIds(List<Integer> ids) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPane.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPane.java
index c5580341..f250b19 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPane.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPane.java
@@ -190,8 +190,8 @@
     }
 
     @Override
-    public int getCurrentTabId() {
-        return TabModelUtils.getCurrentTabId(mTabGroupModelFilterSupplier.get().getTabModel());
+    public @Nullable Tab getCurrentTab() {
+        return TabModelUtils.getCurrentTab(mTabGroupModelFilterSupplier.get().getTabModel());
     }
 
     @Override
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
index cd83457..0b449af 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneBase.java
@@ -54,6 +54,7 @@
 import org.chromium.chrome.browser.tab_ui.TabSwitcher;
 import org.chromium.chrome.browser.tab_ui.TabSwitcherCustomViewManager;
 import org.chromium.chrome.browser.tasks.tab_management.TabListCoordinator.TabListMode;
+import org.chromium.chrome.browser.toolbar.ToolbarPositionController;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeUtils;
 import org.chromium.chrome.browser.user_education.UserEducationHelper;
@@ -264,15 +265,15 @@
     public @NonNull HubLayoutAnimatorProvider createShowHubLayoutAnimatorProvider(
             @NonNull HubContainerView hubContainerView) {
         assert !DeviceFormFactor.isNonMultiDisplayContextOnTablet(hubContainerView.getContext());
-        int tabId = getCurrentTabId();
-        if (getTabListMode() == TabListMode.LIST || tabId == Tab.INVALID_TAB_ID) {
+        @Nullable Tab tab = getCurrentTab();
+        if (getTabListMode() == TabListMode.LIST || tab == null) {
             return FadeHubLayoutAnimationFactory.createFadeInAnimatorProvider(
                     hubContainerView, HUB_LAYOUT_FADE_DURATION_MS, mOnToolbarAlphaChange);
         }
 
         @ColorInt int backgroundColor = getAnimationBackgroundColor();
         SyncOneshotSupplier<ShrinkExpandAnimationData> animationDataSupplier =
-                requestAnimationData(hubContainerView, /* isShrink= */ true, tabId);
+                requestAnimationData(hubContainerView, /* isShrink= */ true, tab);
         return ShrinkExpandHubLayoutAnimationFactory.createShrinkTabAnimatorProvider(
                 hubContainerView,
                 animationDataSupplier,
@@ -285,15 +286,15 @@
     public @NonNull HubLayoutAnimatorProvider createHideHubLayoutAnimatorProvider(
             @NonNull HubContainerView hubContainerView) {
         assert !DeviceFormFactor.isNonMultiDisplayContextOnTablet(hubContainerView.getContext());
-        int tabId = getCurrentTabId();
-        if (getTabListMode() == TabListMode.LIST || tabId == Tab.INVALID_TAB_ID) {
+        Tab tab = getCurrentTab();
+        if (getTabListMode() == TabListMode.LIST || tab == null) {
             return FadeHubLayoutAnimationFactory.createFadeOutAnimatorProvider(
                     hubContainerView, HUB_LAYOUT_FADE_DURATION_MS, mOnToolbarAlphaChange);
         }
 
         @ColorInt int backgroundColor = getAnimationBackgroundColor();
         SyncOneshotSupplier<ShrinkExpandAnimationData> animationDataSupplier =
-                requestAnimationData(hubContainerView, /* isShrink= */ false, tabId);
+                requestAnimationData(hubContainerView, /* isShrink= */ false, tab);
         return ShrinkExpandHubLayoutAnimationFactory.createExpandTabAnimatorProvider(
                 hubContainerView,
                 animationDataSupplier,
@@ -313,7 +314,7 @@
     }
 
     private SyncOneshotSupplier<ShrinkExpandAnimationData> requestAnimationData(
-            @NonNull HubContainerView hubContainerView, boolean isShrink, int tabId) {
+            @NonNull HubContainerView hubContainerView, boolean isShrink, @NonNull Tab tab) {
         SyncOneshotSupplierImpl<ShrinkExpandAnimationData> animationDataSupplier =
                 new SyncOneshotSupplierImpl<>();
         @Nullable TabSwitcherPaneCoordinator coordinator = getTabSwitcherPaneCoordinator();
@@ -341,6 +342,9 @@
             finalBottomCornerRadius = 0;
         }
 
+        int tabId = tab.getId();
+        boolean isTopToolbar = ToolbarPositionController.shouldShowToolbarOnTop(tab);
+
         Runnable provideAnimationData =
                 () -> {
                     Rect hubRect = new Rect();
@@ -358,6 +362,7 @@
                     }
 
                     int leftOffset = 0;
+                    // Account for the hub's search box container height.
                     int searchBoxHeight =
                             OmniboxFeatures.sAndroidHubSearch.isEnabled()
                                     ? HubUtils.getSearchBoxHeight(
@@ -365,9 +370,16 @@
                                             R.id.hub_toolbar,
                                             R.id.toolbar_action_container)
                                     : 0;
-                    // Account for the hub's search box container height.
-                    recyclerViewRect.offset(0, -searchBoxHeight);
-                    recyclerViewRect.bottom += searchBoxHeight;
+                    // If the bottom toolbar will show for the tab we need to offset the
+                    // initial/final location by the height of the toolbar.
+                    int topToolbarOffset =
+                            isTopToolbar
+                                    ? 0
+                                    : res.getDimensionPixelSize(R.dimen.toolbar_height_no_shadow);
+                    int topOffset = searchBoxHeight + topToolbarOffset;
+
+                    recyclerViewRect.offset(0, -topOffset);
+                    recyclerViewRect.bottom += topOffset;
                     if (isShrink) {
                         initialRect = recyclerViewRect;
                         finalRect = coordinator.getTabThumbnailRect(tabId);
@@ -512,8 +524,8 @@
      */
     protected abstract void showAllTabs();
 
-    /** Returns the current selected tab ID. */
-    protected abstract int getCurrentTabId();
+    /** Returns the current selected tab. */
+    protected abstract @Nullable Tab getCurrentTab();
 
     /** Returns whether to eagerly create the coordinator in the {@link LoadHint.WARM} state. */
     protected abstract boolean shouldEagerlyCreateCoordinator();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinator.java
index 58d50098..d13dfd5de 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinator.java
@@ -478,12 +478,24 @@
         return mTabGridDialogVisibilitySupplier;
     }
 
+    /** Provides information on whether the tab grid dialog is showing or animating. */
+    public @Nullable ObservableSupplier<Boolean> getTabGridDialogShowingOrAnimationSupplier() {
+        return mTabGridDialogCoordinator != null
+                ? mTabGridDialogCoordinator.getShowingOrAnimationSupplier()
+                : null;
+    }
+
     /** Returns a {@link TabSwitcherCustomViewManager.Delegate} for supplying custom views. */
     public @Nullable TabSwitcherCustomViewManager.Delegate
             getTabSwitcherCustomViewManagerDelegate() {
         return mMediator;
     }
 
+    /** Indicates whether any animator for the {@link TabListRecyclerView} is running. */
+    public ObservableSupplier<Boolean> getIsRecyclerViewAnimatorRunning() {
+        return mTabListCoordinator.getContainerView().getIsAnimatorRunningSupplier();
+    }
+
     /** Returns the number of elements in the tab switcher's tab list model. */
     public int getTabSwitcherTabListModelSize() {
         return mTabListCoordinator.getTabListModelSize();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinatorFactory.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinatorFactory.java
index 59af965c1..be3cb6e9 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinatorFactory.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherPaneCoordinatorFactory.java
@@ -193,11 +193,7 @@
         // TODO(crbug.com/40067282): Because the show/hide animation already uses the
         // RootUiCoordinator's ScrimCoordinator, a separate instance is needed. However, the way
         // this is implemented the status bar color is not updated. This should be fixed.
-        return new ScrimCoordinator(
-                activity,
-                /* systemUiScrimDelegate= */ null,
-                coordinator,
-                activity.getColor(R.color.omnibox_focused_fading_background_color));
+        return new ScrimCoordinator(activity, /* systemUiScrimDelegate= */ null, coordinator);
     }
 
     @VisibleForTesting
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java
index 9be03f3..02b196b 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtils.java
@@ -29,6 +29,7 @@
 import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncUtils;
 import org.chromium.chrome.browser.tabmodel.TabClosureParams;
 import org.chromium.chrome.browser.tabmodel.TabGroupModelFilter;
+import org.chromium.chrome.browser.tabmodel.TabGroupTitleUtils;
 import org.chromium.chrome.browser.tabmodel.TabList;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelActionListener;
@@ -220,12 +221,20 @@
                                 memberRole);
                     }
                 };
+
+        // The default title is not included in the savedTabGroup data. Use the filter to get the
+        // last known title for the tab group.
+        String title = savedTabGroup.title;
+        @Nullable Tab tab = tabModel.getTabById(tabId);
+        if (tab != null) {
+            int rootId = tab.getRootId();
+            title = TabGroupTitleUtils.getDisplayableTitle(context, filter, rootId);
+        }
+
         if (memberRole == MemberRole.OWNER) {
-            actionConfirmationManager.processDeleteSharedGroupAttempt(
-                    savedTabGroup.title, onActionConfirmation);
+            actionConfirmationManager.processDeleteSharedGroupAttempt(title, onActionConfirmation);
         } else if (memberRole == MemberRole.MEMBER) {
-            actionConfirmationManager.processLeaveGroupAttempt(
-                    savedTabGroup.title, onActionConfirmation);
+            actionConfirmationManager.processLeaveGroupAttempt(title, onActionConfirmation);
         } else {
             showGenericErrorDialog(context, modalDialogManager);
         }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtilsUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtilsUnitTest.java
index dbb2b99..3d8feea 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtilsUnitTest.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiUtilsUnitTest.java
@@ -113,6 +113,8 @@
         when(mTabModel.getTabById(TAB_ID)).thenReturn(mTab);
         when(mTab.getRootId()).thenReturn(ROOT_ID);
         when(mFilter.getRelatedTabListForRootId(ROOT_ID)).thenReturn(mTabsToClose);
+        when(mFilter.getRelatedTabCountForRootId(ROOT_ID)).thenReturn(mTabsToClose.size());
+        when(mFilter.getTabGroupTitle(ROOT_ID)).thenReturn(GROUP_TITLE);
         when(mTabModel.getTabById(TAB_ID)).thenReturn(mTab);
         when(mTab.isClosing()).thenReturn(false);
         when(mTab.getId()).thenReturn(TAB_ID);
@@ -328,7 +330,9 @@
                 .when(mActionConfirmationManager)
                 .processLeaveGroupAttempt(any(), any());
         mockIdentity(EMAIL2, GAIA_ID2);
-        createSyncGroup(COLLABORATION_ID1);
+        SavedTabGroup group = createSyncGroup(COLLABORATION_ID1);
+        group.title = null;
+        when(mFilter.getTabGroupTitle(ROOT_ID)).thenReturn(null);
         createSharedGroup(GROUP_MEMBER1, GROUP_MEMBER2);
 
         TabUiUtils.exitSharedTabGroupWithDialog(
@@ -337,7 +341,7 @@
                 mActionConfirmationManager,
                 mModalDialogManager,
                 TAB_ID);
-        verify(mActionConfirmationManager).processLeaveGroupAttempt(eq(GROUP_TITLE), any());
+        verify(mActionConfirmationManager).processLeaveGroupAttempt(eq("1 tab"), any());
         verify(mDataSharingService, never()).removeMember(any(), any(), any());
     }
 
diff --git a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
index fcb6fdecd..bf9118f5 100644
--- a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
+++ b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings.grd
@@ -200,6 +200,9 @@
       <message name="IDS_TAB_GRID_MANAGE_BUTTON_TEXT" desc="Text for managing the sharing of the current tab group from the tab group dialog toolbar.">
         Manage
       </message>
+      <message name="IDS_MANAGE_SHARING_CONTENT_DESCRIPTION" desc="Content description for the account avatar button that manages sharing for a shared tab group.">
+        Manage sharing
+      </message>
 
       <!-- Bottom Tab Strip strings -->
       <message name="IDS_ACCESSIBILITY_BOTTOM_TAB_STRIP_EXPAND_TAB_SHEET" desc="Accessibility string for BottomTabStripToolbar button indicated visually by the '^' sign.">
diff --git a/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_MANAGE_SHARING_CONTENT_DESCRIPTION.png.sha1 b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_MANAGE_SHARING_CONTENT_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..0f970288
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/strings/android_chrome_tab_ui_strings_grd/IDS_MANAGE_SHARING_CONTENT_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+64ffbe52200268135be8736a81c22e57def4f494
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewBinderTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewBinderTest.java
index 2efcfe0..54ac0f2f 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewBinderTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewBinderTest.java
@@ -27,7 +27,6 @@
 
 import android.app.Activity;
 import android.content.res.ColorStateList;
-import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.GradientDrawable;
 import android.os.Build;
@@ -180,8 +179,7 @@
                         assertNull(mShareButton);
                         assertNull(mImageTilesContainer);
                     }
-                    mScrimCoordinator =
-                            new ScrimCoordinator(sActivity, null, parentView, Color.RED);
+                    mScrimCoordinator = new ScrimCoordinator(sActivity, null, parentView);
                     mTabGridDialogView.setupScrimCoordinator(mScrimCoordinator);
 
                     mModel =
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewTest.java
index 4e1e025e..7f002e2 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogViewTest.java
@@ -20,7 +20,6 @@
 import android.app.Activity;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
-import android.graphics.Color;
 import android.os.SystemClock;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -107,7 +106,7 @@
                             mTabGridDialogView.findViewById(R.id.dialog_animation_card_view);
                     mBackgroundFrameView = mTabGridDialogView.findViewById(R.id.dialog_frame);
                     ScrimCoordinator scrimCoordinator =
-                            new ScrimCoordinator(sActivity, null, mTestParent, Color.RED);
+                            new ScrimCoordinator(sActivity, null, mTestParent);
                     mTabGridDialogView.setupScrimCoordinator(scrimCoordinator);
                     mTabGridDialogView.setScrimClickRunnable(() -> {});
 
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
index 0ef65cb..a16f001 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabGridDialogMediatorUnitTest.java
@@ -17,6 +17,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.atLeastOnce;
@@ -1454,6 +1455,7 @@
     public void testDialogToolbarMenu_DeleteSharedGroup() {
         resetForDataSharing(/* isShared= */ true, GROUP_MEMBER1);
 
+        when(mTabGroupModelFilter.getTabGroupTitle(anyInt())).thenReturn(GROUP_TITLE);
         CoreAccountInfo coreAccountInfo =
                 CoreAccountInfo.createFromEmailAndGaiaId(EMAIL1, GAIA_ID1);
         when(mIdentityManager.getPrimaryAccountInfo(anyInt())).thenReturn(coreAccountInfo);
@@ -1467,6 +1469,7 @@
     public void testDialogToolbarMenu_LeaveSharedGroup() {
         resetForDataSharing(/* isShared= */ true, GROUP_MEMBER1, GROUP_MEMBER2);
 
+        when(mTabGroupModelFilter.getTabGroupTitle(anyInt())).thenReturn(GROUP_TITLE);
         CoreAccountInfo coreAccountInfo =
                 CoreAccountInfo.createFromEmailAndGaiaId(EMAIL2, GAIA_ID2);
         when(mIdentityManager.getPrimaryAccountInfo(anyInt())).thenReturn(coreAccountInfo);
@@ -1712,6 +1715,14 @@
                 .dismiss(MessageType.COLLABORATION_ACTIVITY);
 
         verify(mDialogController).removeMessageCardItem(MessageType.COLLABORATION_ACTIVITY);
+        verify(mMessagingBackendService)
+                .clearDirtyTabMessagesForGroup(
+                        argThat(
+                                eitherGroupId ->
+                                        eitherGroupId
+                                                .getLocalId()
+                                                .tabGroupId
+                                                .equals(TAB_GROUP_ID)));
     }
 
     @Test
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java
index 364867147..972fdf6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/LayerTitleCache.java
@@ -122,6 +122,11 @@
 
     /** Destroys the native reference. */
     public void shutDown() {
+        if (mFaviconHelper != null) {
+            mFaviconHelper.destroy();
+            mFaviconHelper = null;
+        }
+
         if (mNativeLayerTitleCache == 0) return;
         LayerTitleCacheJni.get().destroy(mNativeLayerTitleCache);
         mNativeLayerTitleCache = 0;
@@ -336,6 +341,13 @@
         mSharedAvatarResIds.put(rootId, resId);
     }
 
+    public void transferAvatarToNewRootId(int oldRootId, int newRootId) {
+        int avatarResId = mSharedAvatarResIds.get(oldRootId, ResourcesCompat.ID_NULL);
+        if (avatarResId == ResourcesCompat.ID_NULL) return;
+        mSharedAvatarResIds.delete(oldRootId);
+        mSharedAvatarResIds.put(newRootId, avatarResId);
+    }
+
     private void unregisterSharedGroupAvatar(int resId) {
         DynamicResourceLoader dynamicResourceLoader = mResourceManager.getDynamicResourceLoader();
         dynamicResourceLoader.unregisterResource(resId);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
index ed218b19..59de60b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/OverlayPanelContent.java
@@ -475,7 +475,7 @@
             OverlayPanelContentJni.get().destroyWebContents(mNativeOverlayPanelContentPtr);
             mWebContents = null;
             if (mWebContentsObserver != null) {
-                mWebContentsObserver.destroy();
+                mWebContentsObserver.observe(null);
                 mWebContentsObserver = null;
             }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
index dd91a73..36c978d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerChromeTablet.java
@@ -205,4 +205,9 @@
     public StripLayoutHelperManager getStripLayoutHelperManager() {
         return mTabStripLayoutHelperManager;
     }
+
+    @Override
+    public boolean hasTabletUi() {
+        return true;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
index 6b8cdb45..a7c090a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutManagerImpl.java
@@ -613,7 +613,8 @@
                         selector,
                         mTabContentManagerSupplier.get(),
                         mBrowserControlsStateProvider,
-                        mTopUiThemeColorProvider);
+                        mTopUiThemeColorProvider,
+                        !hasTabletUi());
 
         setNextLayout(null, true);
 
@@ -1414,4 +1415,8 @@
     public void removeObserver(LayoutStateObserver listener) {
         mLayoutObservers.removeObserver(listener);
     }
+
+    public boolean hasTabletUi() {
+        return false;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutUpdateHost.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutUpdateHost.java
index 8a45f6d..c90751c5f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutUpdateHost.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/LayoutUpdateHost.java
@@ -24,8 +24,9 @@
      * Requests a next update to refresh the transforms and changing properties. The update occurs
      * once a frame. This is requesting a new frame to be updated and rendered (no need to call
      * {@link LayoutRenderHost#requestRender()}).
+     *
      * @param onUpdateEffective Callback that will be called when there is a buffer swap for the
-     *                          updated frame.
+     *     updated frame.
      */
     default void requestUpdate(Runnable onUpdateEffective) {}
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/StaticLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/StaticLayout.java
index 3dd1ac4..f29ba94 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/StaticLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/StaticLayout.java
@@ -123,15 +123,17 @@
 
     /**
      * Creates an instance of the {@link StaticLayout}.
-     * @param context             The current Android's context.
-     * @param updateHost          The {@link LayoutUpdateHost} view for this layout.
-     * @param renderHost          The {@link LayoutRenderHost} view for this layout.
-     * @param viewHost            The {@link LayoutManagerHost} view for this layout
+     *
+     * @param context The current Android's context.
+     * @param updateHost The {@link LayoutUpdateHost} view for this layout.
+     * @param renderHost The {@link LayoutRenderHost} view for this layout.
+     * @param viewHost The {@link LayoutManagerHost} view for this layout.
      * @param requestSupplier Frame request supplier for Compositor MCP.
      * @param tabModelSelector {@link TabModelSelector} instance.
      * @param tabContentManager {@link TabContentsManager} instance.
      * @param browserControlsStateProvider A {@link BrowserControlsStateProvider}.
      * @param topUiThemeColorProvider {@link ThemeColorProvider} for top UI.
+     * @param needsOffsetTag Whether or not this layout needs an OffsetTag.
      */
     public StaticLayout(
             Context context,
@@ -142,7 +144,8 @@
             TabModelSelector tabModelSelector,
             TabContentManager tabContentManager,
             BrowserControlsStateProvider browserControlsStateProvider,
-            Supplier<TopUiThemeColorProvider> topUiThemeColorProvider) {
+            Supplier<TopUiThemeColorProvider> topUiThemeColorProvider,
+            boolean needsOffsetTag) {
         this(
                 context,
                 updateHost,
@@ -153,7 +156,8 @@
                 tabContentManager,
                 browserControlsStateProvider,
                 topUiThemeColorProvider,
-                null);
+                null,
+                needsOffsetTag);
     }
 
     /** Protected constructor for testing, allows specifying a custom SceneLayer. */
@@ -168,16 +172,18 @@
             TabContentManager tabContentManager,
             BrowserControlsStateProvider browserControlsStateProvider,
             Supplier<TopUiThemeColorProvider> topUiThemeColorProvider,
-            StaticTabSceneLayer testSceneLayer) {
+            StaticTabSceneLayer testSceneLayer,
+            boolean needsOffsetTag) {
         super(context, updateHost, renderHost);
 
         mContext = context;
-        boolean isTablet = DeviceFormFactor.isNonMultiDisplayContextOnTablet(mContext);
+
         // Only handle tab lifecycle on tablets.
-        mHandlesTabLifecycles = isTablet;
-        // On tablets, StaticTabSceneLayer is a subtree of TabStripSceneLayer,
-        // and the tag would have been set on the TabStripSceneLayer already.
-        mNeedsOffsetTag = !isTablet;
+        mHandlesTabLifecycles = DeviceFormFactor.isNonMultiDisplayContextOnTablet(mContext);
+
+        // StaticTabSceneLayer is a subtree of TabStripSceneLayer, and the tag would have been set
+        // on the TabStripSceneLayer already if tablet UI is present.
+        mNeedsOffsetTag = needsOffsetTag;
 
         mViewHost = viewHost;
         mRequestSupplier = requestSupplier;
@@ -219,8 +225,6 @@
                             BrowserControlsOffsetTagsInfo offsetTagsInfo,
                             @BrowserControlsState int constraints) {
                         if (ChromeFeatureList.sBrowserControlsInViz.isEnabled()) {
-                            // On tablets, StaticTabSceneLayer is a subtree of TabStripSceneLayer,
-                            // and the tag would have been set on the TabStripSceneLayer already.
                             if (mNeedsOffsetTag) {
                                 mModel.set(
                                         LayoutTab.CONTENT_OFFSET_TAG,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ReorderDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ReorderDelegate.java
index b8ef581..02585663 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ReorderDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/ReorderDelegate.java
@@ -159,14 +159,10 @@
                         || mActiveStrategy == mTabStrategy);
     }
 
-    boolean getReorderingForTabDrop() {
+    private boolean isReorderingForTabDrop() {
         return getInReorderMode() && mActiveStrategy == mExternalViewDragDropReorderStrategy;
     }
 
-    StripLayoutTab getInteractingTab() {
-        return (StripLayoutTab) mActiveStrategy.getInteractingView();
-    }
-
     private ReorderStrategy getReorderStrategy(
             StripLayoutView interactingView, @ReorderType int reorderType) {
         if (mSourceViewDragDropReorderStrategy != null
@@ -348,7 +344,7 @@
         // added. As such, we need to base this on the most recent x-position of the drag, rather
         // than the interacting view's drawX.
         final float x =
-                getReorderingForTabDrop()
+                isReorderingForTabDrop()
                         ? adjustXForTabDrop(mLastReorderX)
                         : mActiveStrategy.getInteractingView().getDrawX();
 
@@ -367,7 +363,7 @@
         // Note that we only allow scrolling in each direction if the user has already manually
         // moved that way.
         final float width =
-                getReorderingForTabDrop()
+                isReorderingForTabDrop()
                         ? mTabWidthSupplier.get()
                         : mActiveStrategy.getInteractingView().getWidth();
         float dragSpeedRatio = 0.f;
@@ -397,6 +393,13 @@
         mInReorderModeSupplier.removeObserver(observer);
     }
 
+    /** Update and animate views for external view drop on strip. */
+    void handleTabDropForExternalView(
+            StripLayoutGroupTitle[] groupTitles, int draggedTabId, int dropIndex) {
+        assert mExternalViewDragDropReorderStrategy != null;
+        mExternalViewDragDropReorderStrategy.handleDrop(groupTitles, draggedTabId, dropIndex);
+    }
+
     // ============================================================================================
     // Margin helpers
     // ============================================================================================
@@ -1348,6 +1351,9 @@
     private class ExternalViewDragDropReorderStrategy implements ReorderStrategy {
         // View on the strip being hovered on by the dragged view.
         private StripLayoutView mInteractingView;
+        // View on the strip last hovered on by dragged view. This can be used post stop reorder to
+        // handle drop event (eg: reparenting dropped tab).
+        private StripLayoutView mInteractingViewDuringStop;
 
         /** Initiate reorder when external view is dragged onto strip. */
         @Override
@@ -1358,6 +1364,7 @@
                 PointF startPoint) {
             // 1. Set initial state and add edge margins.
             mInteractingView = interactingView;
+            mInteractingViewDuringStop = null;
             resetReorderState(startPoint.x);
             setEdgeMarginsForReorder(stripTabs);
 
@@ -1445,6 +1452,7 @@
                 StripLayoutGroupTitle[] groupTitles, StripLayoutTab[] stripTabs) {
             List<Animator> animatorList = new ArrayList<>();
             handleStopReorderMode(groupTitles, stripTabs, mInteractingView, animatorList);
+            mInteractingViewDuringStop = mInteractingView;
             // Start animations.
             mAnimationHost.startAnimations(
                     animatorList,
@@ -1460,6 +1468,44 @@
         public StripLayoutView getInteractingView() {
             return mInteractingView;
         }
+
+        /** Merges dropped tab to interacting view's tab group, if one exists. */
+        public void handleDrop(
+                StripLayoutGroupTitle[] groupTitles, int draggedTabId, int dropIndex) {
+            if (mInteractingViewDuringStop == null) return;
+
+            StripLayoutTab interactingView = (StripLayoutTab) mInteractingViewDuringStop;
+            Tab interactingTab = mModel.getTabById(interactingView.getTabId());
+
+            // 1. If hovered on tab is not part of group, no-op.
+            if (!mTabGroupModelFilter.isTabInTabGroup(interactingTab)) return;
+
+            // 2. Merge dragged tab to hovered tab's group at drop index.
+            mTabGroupModelFilter.mergeTabsToGroup(
+                    draggedTabId, interactingTab.getId(), /* skipUpdateTabModel= */ true);
+            mModel.moveTab(draggedTabId, dropIndex);
+
+            // 3. Animate bottom indicator. Done after merging the dragged tab to group,
+            // so that the calculated bottom indicator width will be correct.
+            StripLayoutGroupTitle groupTitle =
+                    StripLayoutUtils.findGroupTitle(groupTitles, interactingTab.getRootId());
+            List<Animator> animators = new ArrayList<>();
+            updateBottomIndicatorWidthForTabReorder(
+                    mAnimationHost.getAnimationHandler(),
+                    mTabGroupModelFilter,
+                    groupTitle,
+                    /* isMovingOutOfGroup= */ false,
+                    /* throughGroupTitle= */ false,
+                    animators);
+            mAnimationHost.startAnimations(
+                    animators,
+                    new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            mInteractingViewDuringStop = null;
+                        }
+                    });
+        }
     }
 
     float adjustXForTabDrop(float x) {
@@ -1615,15 +1661,19 @@
         mInReorderModeSupplier.set(inReorderMode);
     }
 
+    float getLastReorderXForTesting() {
+        return mLastReorderX;
+    }
+
+    StripLayoutTab getInteractingTabForTesting() {
+        return (StripLayoutTab) mActiveStrategy.getInteractingView();
+    }
+
     float getDragLastOffsetXForTesting() {
         if (mSourceViewDragDropReorderStrategy == null) return 0f;
         return mSourceViewDragDropReorderStrategy.mLastOffsetX;
     }
 
-    float getLastReorderXForTesting() {
-        return mLastReorderX;
-    }
-
     void setDragLastOffsetXForTesting(float offsetX) {
         if (mSourceViewDragDropReorderStrategy == null) return;
         mSourceViewDragDropReorderStrategy.mLastOffsetX = offsetX;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
index 459b7d17..ee99bf2a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelper.java
@@ -304,6 +304,7 @@
                         groupTitle.updateRootId(newRootId);
                         // Refresh properties since removing the root tab may have cleared the ones
                         // associated with the oldRootId before updating to the newRootId here.
+                        mLayerTitleCache.transferAvatarToNewRootId(oldRootId, newRootId);
                         updateGroupTextAndSharedState(groupTitle);
                         updateGroupTitleTint(groupTitle);
                     }
@@ -1710,10 +1711,6 @@
         mTouchableRect.set(touchableRect);
     }
 
-    private boolean isTabDraggingInProgress() {
-        return mTabDragSource != null && mTabDragSource.isTabDraggingInProgress();
-    }
-
     /**
      * Checks whether a tab at the edge of the strip is partially hidden, in which case the close
      * button will be hidden to avoid accidental clicks.
@@ -1834,12 +1831,6 @@
         mUpdateHost.requestUpdate();
     }
 
-    void dragForTabDrop(long time, float x, float y, float deltaX, boolean draggedTabIncognito) {
-        if (mIncognito == draggedTabIncognito) {
-            drag(time, x, y, deltaX);
-        }
-    }
-
     private void onStripScrollStart() {
         long currentTime = SystemClock.elapsedRealtime();
 
@@ -3734,43 +3725,6 @@
         return mStripTabs.length;
     }
 
-    int getTabDropId() {
-        if (!mReorderDelegate.getReorderingForTabDrop()) {
-            return Tab.INVALID_TAB_ID;
-        }
-        StripLayoutTab interactingTab = mReorderDelegate.getInteractingTab();
-        if (interactingTab == null) {
-            return Tab.INVALID_TAB_ID;
-        }
-
-        Tab tab = getTabById(interactingTab.getTabId());
-        return mTabGroupModelFilter.isTabInTabGroup(tab) ? tab.getId() : Tab.INVALID_TAB_ID;
-    }
-
-    void mergeToGroupForTabDropIfNeeded(int destTabId, int draggedTabId, int index) {
-        if (destTabId == Tab.INVALID_TAB_ID) return;
-
-        Tab destTab = getTabById(destTabId);
-        StripLayoutGroupTitle groupTitle = findGroupTitle(destTab.getRootId());
-
-        mTabGroupModelFilter.mergeTabsToGroup(draggedTabId, destTabId, true);
-        mModel.moveTab(draggedTabId, index);
-
-        // Animate bottom indicator. Done after merging the dropped tab into a group, so that the
-        // calculated bottom indicator width will be correct.
-        if (groupTitle != null) {
-            List<Animator> animators = new ArrayList<>();
-            mReorderDelegate.updateBottomIndicatorWidthForTabReorder(
-                    mUpdateHost.getAnimationHandler(),
-                    mTabGroupModelFilter,
-                    groupTitle,
-                    /* isMovingOutOfGroup= */ false,
-                    /* throughGroupTitle= */ false,
-                    animators);
-            startAnimations(animators);
-        }
-    }
-
     StripLayoutTab getTabAtPosition(float x) {
         return (StripLayoutTab) getViewAtPositionX(x, false);
     }
@@ -3797,12 +3751,6 @@
         startReorderMode(x, 0, getTabAtPosition(x), ReorderType.DRAG_WITHIN_STRIP);
     }
 
-    void stopReorderMode() {
-        if (mReorderDelegate.getInReorderMode()) {
-            mReorderDelegate.stopReorderMode(mStripGroupTitles, mStripTabs);
-        }
-    }
-
     private void setCompositorButtonsVisible(boolean visible) {
         float endOpacity = visible ? 1.f : 0.f;
 
@@ -4181,7 +4129,7 @@
      * @return The currently interacting tab.
      */
     StripLayoutTab getInteractingTabForTesting() {
-        return mReorderDelegate.getInteractingTab();
+        return mReorderDelegate.getInteractingTabForTesting(); // IN-TEST
     }
 
     /**
@@ -4269,21 +4217,11 @@
         stripTab.setAccessibilityDescription(builder.toString(), title, resId);
     }
 
-    void setLastOffsetXForTesting(float lastOffsetX) {
-        mReorderDelegate.setDragLastOffsetXForTesting(lastOffsetX); // IN-TEST
-    }
+    // ============================================================================================
+    // Drag and Drop View Delegate.
+    // ============================================================================================
 
-    float getLastOffsetXForTesting() {
-        return mReorderDelegate.getDragLastOffsetXForTesting(); // IN-TEST
-    }
-
-    void startDragAndDropTabForTesting(
-            @NonNull StripLayoutTab clickedTab, @NonNull PointF dragStartPointF) {
-        startReorderMode(
-                dragStartPointF.x, dragStartPointF.y, clickedTab, ReorderType.START_DRAG_DROP);
-    }
-
-    void prepareForTabDrop(
+    void handleDragEnter(
             float currX, float lastX, boolean isSourceStrip, boolean draggedTabIncognito) {
         if (isSourceStrip) {
             // Tab drag started reorder - update reorder to handle drag onto strip.
@@ -4313,7 +4251,13 @@
         }
     }
 
-    void clearForTabDrop(boolean isSourceStrip, boolean draggedTabIncognito) {
+    void handleDragWithin(long time, float x, float y, float deltaX, boolean draggedTabIncognito) {
+        if (mIncognito == draggedTabIncognito) {
+            drag(time, x, y, deltaX);
+        }
+    }
+
+    void handleDragExit(boolean isSourceStrip, boolean draggedTabIncognito) {
         if (isSourceStrip) {
             // Tab drag started reorder - update reorder to handle drag out of strip.
             // endX is inaccurate but unused.
@@ -4329,7 +4273,21 @@
         }
     }
 
-    void sendMoveWindowBroadcast(View view, float startXInView, float startYInView) {
+    void maybeMergeToGroupOnDrop(int draggedTabId, int index) {
+        mReorderDelegate.handleTabDropForExternalView(mStripGroupTitles, draggedTabId, index);
+    }
+
+    void stopReorderMode() {
+        if (mReorderDelegate.getInReorderMode()) {
+            mReorderDelegate.stopReorderMode(mStripGroupTitles, mStripTabs);
+        }
+    }
+
+    private boolean isTabDraggingInProgress() {
+        return mTabDragSource != null && mTabDragSource.isTabDraggingInProgress();
+    }
+
+    private void sendMoveWindowBroadcast(View view, float startXInView, float startYInView) {
         if (!TabUiFeatureUtilities.isTabDragAsWindowEnabled()) return;
         if (mWindowAndroid.getActivity().get() == null) return;
 
@@ -4354,4 +4312,18 @@
         intent.putExtra("MOVE_WINDOW_START_Y", startYInScreen);
         mWindowAndroid.sendBroadcast(intent);
     }
+
+    void startDragAndDropTabForTesting(
+            @NonNull StripLayoutTab clickedTab, @NonNull PointF dragStartPointF) {
+        startReorderMode(
+                dragStartPointF.x, dragStartPointF.y, clickedTab, ReorderType.START_DRAG_DROP);
+    }
+
+    void setLastOffsetXForTesting(float lastOffsetX) {
+        mReorderDelegate.setDragLastOffsetXForTesting(lastOffsetX); // IN-TEST
+    }
+
+    float getLastOffsetXForTesting() {
+        return mReorderDelegate.getDragLastOffsetXForTesting(); // IN-TEST
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabDragSource.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabDragSource.java
index 3f5a4c5d..2e28265 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabDragSource.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabDragSource.java
@@ -363,7 +363,7 @@
         }
         mStripLayoutHelperSupplier
                 .get()
-                .prepareForTabDrop(xPx * mPxToDp, mLastXDp, isDragSource, isDraggedTabIncognito());
+                .handleDragEnter(xPx * mPxToDp, mLastXDp, isDragSource, isDraggedTabIncognito());
         return true;
     }
 
@@ -372,7 +372,7 @@
         float yDp = yPx * mPxToDp;
         mStripLayoutHelperSupplier
                 .get()
-                .dragForTabDrop(
+                .handleDragWithin(
                         LayoutManagerImpl.time(),
                         xDp,
                         yDp,
@@ -383,9 +383,7 @@
 
     private boolean onDrop(DragEvent dropEvent) {
         StripLayoutHelper helper = mStripLayoutHelperSupplier.get();
-        int destinationTabId = helper.getTabDropId();
         helper.stopReorderMode();
-
         if (isDragSource()) {
             DragDropMetricUtils.recordTabReorderStripWithDragDrop(mUmaState.mDragEverLeftStrip);
             return true;
@@ -413,10 +411,10 @@
                     mTabModelSelector.getModel(tabBeingDragged.isIncognito()).getCount());
             showDroppedDifferentModelToast(mWindowAndroid.getContext().get());
         } else {
+            // Reparent tab at drop index and merge to group on destination if needed.
             int tabIndex = helper.getTabIndexForTabDrop(dropEvent.getX() * mPxToDp);
             mMultiInstanceManager.moveTabToWindow(getActivity(), tabBeingDragged, tabIndex);
-            helper.mergeToGroupForTabDropIfNeeded(
-                    destinationTabId, tabBeingDragged.getId(), tabIndex);
+            helper.maybeMergeToGroupOnDrop(tabBeingDragged.getId(), tabIndex);
         }
         DragDropMetricUtils.recordTabDragDropType(
                 DragDropType.TAB_STRIP_TO_TAB_STRIP,
@@ -524,7 +522,7 @@
                 mShadowView.expand();
             }
         }
-        mStripLayoutHelperSupplier.get().clearForTabDrop(isDragSource(), isDraggedTabIncognito());
+        mStripLayoutHelperSupplier.get().handleDragExit(isDragSource(), isDraggedTabIncognito());
         return true;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
index b408e651..eb7813e3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuCoordinator.java
@@ -464,7 +464,7 @@
 
     private void dismissDialog() {
         if (mWebContentsObserver != null) {
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
         }
         if (mChipController != null) {
             mChipController.dismissChipIfShowing();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
index c65281b..b79573c6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dom_distiller/ReaderModeManager.java
@@ -181,7 +181,7 @@
     /** Clear the status map and references to other objects. */
     @Override
     public void destroy() {
-        if (mWebContentsObserver != null) mWebContentsObserver.destroy();
+        if (mWebContentsObserver != null) mWebContentsObserver.observe(null);
         mIsDestroyed = true;
     }
 
@@ -294,7 +294,7 @@
 
     /** Clear the reader mode state for this manager. */
     private void removeTabState() {
-        if (mWebContentsObserver != null) mWebContentsObserver.destroy();
+        if (mWebContentsObserver != null) mWebContentsObserver.observe(null);
         mDistillationStatus = DistillationStatus.POSSIBLE;
         mIsDismissed = false;
         mMessageRequestedForNavigation = false;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/feedback/ChromeFeedbackCollector.java b/chrome/android/java/src/org/chromium/chrome/browser/feedback/ChromeFeedbackCollector.java
index cc827cd2..0f4a8f3c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/feedback/ChromeFeedbackCollector.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/feedback/ChromeFeedbackCollector.java
@@ -62,6 +62,7 @@
         sources.add(new DeviceInfoFeedbackSource());
         sources.add(new UrlFeedbackSource(initParams.url));
         sources.add(new VariationsFeedbackSource(initParams.profile));
+        sources.add(new VariationsStateFeedbackSource(initParams.profile));
         sources.add(new HistogramFeedbackSource(initParams.profile));
         sources.add(new LowEndDeviceFeedbackSource());
         sources.add(new IMEFeedbackSource());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java
index b955a5d..ebaefc9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryActivity.java
@@ -72,11 +72,7 @@
                 (ViewGroup)
                         LayoutInflater.from(this).inflate(R.layout.bottom_sheet_container, null);
         ScrimCoordinator scrim =
-                new ScrimCoordinator(
-                        this,
-                        /* systemUiScrimDelegate= */ null,
-                        contentView,
-                        getColor(R.color.default_scrim_color));
+                new ScrimCoordinator(this, /* systemUiScrimDelegate= */ null, contentView);
         mBottomSheetController =
                 BottomSheetControllerFactory.createBottomSheetController(
                         () -> scrim,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfoControllerDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfoControllerDelegate.java
index 721135f..147af63 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfoControllerDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/page_info/ChromePageInfoControllerDelegate.java
@@ -359,22 +359,23 @@
     public void getFavicon(GURL url, Callback<Drawable> callback) {
         Resources resources = mContext.getResources();
         int size = resources.getDimensionPixelSize(R.dimen.page_info_favicon_size);
-        new FaviconHelper()
-                .getLocalFaviconImageForURL(
-                        mProfile,
-                        url,
-                        size,
-                        (image, iconUrl) -> {
-                            if (image != null) {
-                                callback.onResult(new BitmapDrawable(resources, image));
-                            } else if (UrlUtilities.isInternalScheme(url)) {
-                                callback.onResult(
-                                        TintedDrawable.constructTintedDrawable(
-                                                mContext, R.drawable.chromelogo16));
-                            } else {
-                                callback.onResult(null);
-                            }
-                        });
+        FaviconHelper faviconHelper = new FaviconHelper();
+        faviconHelper.getLocalFaviconImageForURL(
+                mProfile,
+                url,
+                size,
+                (image, iconUrl) -> {
+                    if (image != null) {
+                        callback.onResult(new BitmapDrawable(resources, image));
+                    } else if (UrlUtilities.isInternalScheme(url)) {
+                        callback.onResult(
+                                TintedDrawable.constructTintedDrawable(
+                                        mContext, R.drawable.chromelogo16));
+                    } else {
+                        callback.onResult(null);
+                    }
+                    faviconHelper.destroy();
+                });
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
index d9450a86..99fe0d7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/handler/PaymentHandlerMediator.java
@@ -67,6 +67,8 @@
     /** A token held while the payment sheet is obscuring all visible tabs. */
     private TabObscuringHandler.Token mTabObscuringToken;
 
+    private boolean mIsDestroyed;
+
     @IntDef({
         CloseReason.OTHERS,
         CloseReason.USER,
@@ -139,6 +141,48 @@
         ApplicationStatus.registerStateListenerForActivity(mActivityStateListener, activity);
     }
 
+    /** Destroy the dependencies of the Mediator. */
+    public void destroy() {
+        if (mIsDestroyed) return;
+        mIsDestroyed = true;
+
+        observe(null);
+
+        ApplicationStatus.unregisterActivityStateListener(mActivityStateListener);
+
+        switch (mCloseReason) {
+            case CloseReason.INSECURE_NAVIGATION:
+                ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(
+                        mPaymentRequestWebContents,
+                        PaymentEventResponseType.PAYMENT_HANDLER_INSECURE_NAVIGATION);
+                break;
+            case CloseReason.USER:
+                ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(
+                        mPaymentRequestWebContents,
+                        PaymentEventResponseType.PAYMENT_HANDLER_WINDOW_CLOSING);
+                break;
+            case CloseReason.FAIL_LOAD:
+                ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(
+                        mPaymentRequestWebContents,
+                        PaymentEventResponseType.PAYMENT_HANDLER_FAIL_TO_LOAD_MAIN_FRAME);
+                break;
+            case CloseReason.ACTIVITY_DIED:
+                ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(
+                        mPaymentRequestWebContents,
+                        PaymentEventResponseType.PAYMENT_HANDLER_ACTIVITY_DIED);
+                break;
+            case CloseReason.OTHERS:
+                // No need to notify ServiceWorkerPaymentAppBridge when merchant aborts the
+                // payment request (and thus {@link ChromePaymentRequestService} closes
+                // PaymentHandlerMediator). "OTHERS" category includes this cases.
+                // TODO(crbug.com/40134410): we should explicitly list merchant aborting payment
+                // request as a {@link CloseReason}, renames "OTHERS" as "UNKNOWN" and asserts
+                // that PaymentHandler wouldn't be closed for unknown reason.
+        }
+        mHandler.removeCallbacksAndMessages(null);
+        hideScrim();
+    }
+
     // Implement View.OnLayoutChangeListener:
     // This is the Tab View's layout change listener, invoked in response to phone rotation.
     // TODO(crbug.com/40120866): It should listen to the BottomSheet container's layout change
@@ -219,44 +263,6 @@
     @Override
     public void onSheetContentChanged(BottomSheetContent newContent) {}
 
-    // Implement WebContentsObserver:
-    @Override
-    public void onDestroy() {
-        ApplicationStatus.unregisterActivityStateListener(mActivityStateListener);
-
-        switch (mCloseReason) {
-            case CloseReason.INSECURE_NAVIGATION:
-                ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(
-                        mPaymentRequestWebContents,
-                        PaymentEventResponseType.PAYMENT_HANDLER_INSECURE_NAVIGATION);
-                break;
-            case CloseReason.USER:
-                ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(
-                        mPaymentRequestWebContents,
-                        PaymentEventResponseType.PAYMENT_HANDLER_WINDOW_CLOSING);
-                break;
-            case CloseReason.FAIL_LOAD:
-                ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(
-                        mPaymentRequestWebContents,
-                        PaymentEventResponseType.PAYMENT_HANDLER_FAIL_TO_LOAD_MAIN_FRAME);
-                break;
-            case CloseReason.ACTIVITY_DIED:
-                ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(
-                        mPaymentRequestWebContents,
-                        PaymentEventResponseType.PAYMENT_HANDLER_ACTIVITY_DIED);
-                break;
-            case CloseReason.OTHERS:
-                // No need to notify ServiceWorkerPaymentAppBridge when merchant aborts the
-                // payment request (and thus {@link ChromePaymentRequestService} closes
-                // PaymentHandlerMediator). "OTHERS" category includes this cases.
-                // TODO(crbug.com/40134410): we should explicitly list merchant aborting payment
-                // request as a {@link CloseReason}, renames "OTHERS" as "UNKNOWN" and asserts
-                // that PaymentHandler wouldn't be closed for unknown reason.
-        }
-        mHandler.removeCallbacksAndMessages(null);
-        hideScrim();
-    }
-
     private void hideScrim() {
         setObscureState(false);
 
@@ -268,6 +274,12 @@
 
     // Implement WebContentsObserver:
     @Override
+    public void webContentsDestroyed() {
+        destroy();
+    }
+
+    // Implement WebContentsObserver:
+    @Override
     public void didFinishNavigationInPrimaryMainFrame(NavigationHandle navigationHandle) {
         // Checking uncommitted navigations (e.g., Network errors) is unnecessary because
         // they have no chance to be loaded nor rendered.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
index 259b33a..cd4d5675 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
@@ -240,8 +240,7 @@
                 new ScrimCoordinator(
                         this,
                         /* systemUiScrimDelegate= */ null,
-                        (ViewGroup) sheetContainer.getParent(),
-                        getColor(R.color.default_scrim_color));
+                        (ViewGroup) sheetContainer.getParent());
 
         mManagedBottomSheetController =
                 BottomSheetControllerFactory.createBottomSheetController(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
index bb02dfb..384de252 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/TileGroup.java
@@ -14,11 +14,11 @@
 import android.view.View.OnCreateContextMenuListener;
 
 import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.base.Callback;
+import org.chromium.base.CancelableRunnable;
 import org.chromium.base.TimeUtils;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.PostTask;
@@ -515,31 +515,6 @@
 
     private class TileInteractionDelegateImpl
             implements TileInteractionDelegate, ContextMenuManager.Delegate, View.OnTouchListener {
-
-        /**
-         * CancelableRunnable is a Runnable class can be canceled. It is created here instead of
-         * making CallbackController.CancelableRunnable reusable is that this class is expected to
-         * be used on UI thread only, so locking mechanism is not required.
-         */
-        private static class CancelableRunnable implements Runnable {
-            private Runnable mRunnable;
-
-            private CancelableRunnable(@NonNull Runnable runnable) {
-                mRunnable = runnable;
-            }
-
-            public void cancel() {
-                mRunnable = null;
-            }
-
-            @Override
-            public void run() {
-                // This is run on UI thread only, so it is not necessary to guard this against
-                // another thread.
-                if (mRunnable != null) mRunnable.run();
-            }
-        }
-
         private final SiteSuggestion mSuggestion;
         private Runnable mOnClickRunnable;
         private Runnable mOnRemoveRunnable;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
index e3958f95..5ecd655 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsObserver.java
@@ -55,7 +55,7 @@
 
     private final TabImpl mTab;
     private final ObserverList<Callback<WebContents>> mInitObservers = new ObserverList<>();
-    private WebContentsObserver mObserver;
+    private Observer mObserver;
     private GURL mLastUrl;
 
     public static TabWebContentsObserver from(Tab tab) {
@@ -110,7 +110,8 @@
     @Override
     public void cleanupWebContents(WebContents webContents) {
         if (mObserver != null) {
-            mObserver.destroy();
+            mObserver.updateNotificationsForTab();
+            mObserver.observe(null);
             mObserver = null;
         }
     }
@@ -381,7 +382,11 @@
         }
 
         @Override
-        public void onDestroy() {
+        public void webContentsDestroyed() {
+            updateNotificationsForTab();
+        }
+
+        void updateNotificationsForTab() {
             MediaCaptureNotificationServiceImpl.updateMediaNotificationForTab(
                     ContextUtils.getApplicationContext(), mTab.getId(), null, mLastUrl);
             BluetoothNotificationManager.updateBluetoothNotificationForTab(
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserver.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserver.java
index ded0b645..31e78ab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserver.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserver.java
@@ -23,6 +23,7 @@
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionsVisualState;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeUtils;
+import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarStateProvider;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetContent;
 import org.chromium.components.browser_ui.bottomsheet.BottomSheetController;
@@ -142,7 +143,11 @@
         mBottomControlsStacker = bottomControlsStacker;
 
         mSnackbarStateProvider = snackbarStateProvider;
-        mSnackbarStateProvider.addObserver(this);
+        if (!SnackbarManager.isFloatingSnackbarEnabled()) {
+            // The floating snackbar appears to hover and isn't anchored to bottom UI, and thus
+            // should not impact the bottom attached color.
+            mSnackbarStateProvider.addObserver(this);
+        }
 
         mBottomSheetController = bottomSheetController;
         mBottomSheetController.addObserver(this);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
index e8b33a21..886166d 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tabbed_mode/TabbedRootUiCoordinator.java
@@ -922,13 +922,7 @@
                         controller.setNavigationBarScrimFraction(scrimFraction);
                     }
                 };
-        return new ScrimCoordinator(
-                mActivity,
-                delegate,
-                mCoordinator,
-                mCoordinator
-                        .getContext()
-                        .getColor(R.color.omnibox_focused_fading_background_color));
+        return new ScrimCoordinator(mActivity, delegate, mCoordinator);
     }
 
     // Package Private class methods
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
index fe4d7b7..59bab65 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -1706,11 +1706,7 @@
                         RootUiCoordinator.this.setStatusBarScrimFraction(scrimFraction);
                     }
                 };
-        return new ScrimCoordinator(
-                mActivity,
-                delegate,
-                coordinator,
-                coordinator.getContext().getColor(R.color.omnibox_focused_fading_background_color));
+        return new ScrimCoordinator(mActivity, delegate, coordinator);
     }
 
     protected void setStatusBarScrimFraction(float scrimFraction) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java
index dfd8fca..6459d4ff 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/TabsTest.java
@@ -852,7 +852,7 @@
                     WebContentsObserver observer =
                             new WebContentsObserver(tab.getWebContents()) {
                                 @Override
-                                public void onDestroy() {
+                                public void webContentsDestroyed() {
                                     webContentsDestroyed.notifyCalled();
                                 }
                             };
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/save_card/AutofillSaveCardBottomSheetRenderTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/save_card/AutofillSaveCardBottomSheetRenderTest.java
index 503cadf..82bef78 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/save_card/AutofillSaveCardBottomSheetRenderTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/save_card/AutofillSaveCardBottomSheetRenderTest.java
@@ -7,7 +7,6 @@
 import static org.chromium.base.ThreadUtils.runOnUiThreadBlocking;
 
 import android.app.Activity;
-import android.graphics.Color;
 import android.view.ViewGroup;
 
 import androidx.test.filters.LargeTest;
@@ -58,7 +57,7 @@
     @Rule
     public final RenderTestRule mRenderTestRule =
             RenderTestRule.Builder.withPublicCorpus()
-                    .setRevision(2)
+                    .setRevision(3)
                     .setBugComponent(Component.UI_BROWSER_AUTOFILL)
                     .build();
 
@@ -82,8 +81,7 @@
                             new ScrimCoordinator(
                                     mActivity,
                                     /* systemUiScrimDelegate= */ null,
-                                    activityContentView,
-                                    Color.WHITE);
+                                    activityContentView);
                     mBottomSheetController =
                             BottomSheetControllerFactory.createFullWidthBottomSheetController(
                                     () -> scrimCoordinator,
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java
index 770a1c1..eba4616 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/ntp/RecentlyClosedBridgeTest.java
@@ -1605,6 +1605,86 @@
         assertTabsAre(recentTabs, titles, urls);
     }
 
+    /** Tests opening the closed tab group when another group is pending closure. */
+    @Test
+    @MediumTest
+    public void testOpenRecentlyClosedTabGroupEntryWhenHasPendingTabGroupClosure() {
+        if (mTabGroupModelFilter == null) return;
+
+        // Tab order is inverted in RecentlyClosedEntry as most recent comes first so log data in
+        // reverse.
+        final String[] group1Urls = new String[] {getUrl(TEST_PAGE_C), getUrl(TEST_PAGE_B)};
+        final String[] group2Urls = new String[] {getUrl(TEST_PAGE_A)};
+        final Tab tabA = sActivityTestRule.loadUrlInNewTab(group2Urls[0], /* incognito= */ false);
+        final Tab tabB = sActivityTestRule.loadUrlInNewTab(group1Urls[1], /* incognito= */ false);
+        final Tab tabC = sActivityTestRule.loadUrlInNewTab(group1Urls[0], /* incognito= */ false);
+
+        final String[] group1Titles = new String[2];
+        final String[] group2Titles = new String[1];
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    mTabGroupModelFilter.mergeTabsToGroup(tabC.getId(), tabB.getId());
+                    mTabGroupModelFilter.setTabGroupTitle(tabB.getId(), "Group 1");
+                    mTabGroupModelFilter.createSingleTabGroup(tabA, true);
+                    mTabGroupModelFilter.setTabGroupTitle(tabA.getId(), "Group 2");
+                    group2Titles[0] = tabA.getTitle();
+                    group1Titles[1] = tabB.getTitle();
+                    group1Titles[0] = tabC.getTitle();
+                    closeTabs(
+                            TabClosureParams.closeTabs(List.of(tabB, tabC))
+                                    .hideTabGroups(false)
+                                    .build());
+                    mTabModel.commitAllTabClosures();
+                });
+        final List<RecentlyClosedEntry> recentEntries = new ArrayList<>();
+        int tabCount = getRecentEntriesAndReturnActiveTabCount(recentEntries);
+        Assert.assertEquals(2, tabCount);
+        Assert.assertEquals(1, recentEntries.size());
+        assertEntryIs(
+                recentEntries.get(0),
+                RecentlyClosedGroup.class,
+                new String[] {"Group 1"},
+                group1Titles,
+                group1Urls);
+
+        // Close Group 2 and restore Group 1.
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    closeTabs(
+                            TabClosureParams.closeTabs(List.of(tabA)).hideTabGroups(false).build());
+                    mRecentlyClosedBridge.openMostRecentlyClosedEntry(mTabModel);
+                });
+
+        // 1. Blank tab
+        // 2. Restored tabB
+        // 3. Restored tabC
+        final List<Tab> tabs = getAllTabs();
+        Assert.assertEquals(3, tabs.size());
+        Assert.assertEquals(group1Titles[1], ChromeTabUtils.getTitleOnUiThread(tabs.get(1)));
+        Assert.assertEquals(group1Urls[1], ChromeTabUtils.getUrlOnUiThread(tabs.get(1)).getSpec());
+        Assert.assertEquals(group1Titles[0], ChromeTabUtils.getTitleOnUiThread(tabs.get(2)));
+        Assert.assertEquals(group1Urls[0], ChromeTabUtils.getUrlOnUiThread(tabs.get(2)).getSpec());
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    Assert.assertEquals(
+                            "Group 1", mTabGroupModelFilter.getTabGroupTitle(tabs.get(1).getId()));
+                    Assert.assertTrue(mTabGroupModelFilter.isTabInTabGroup(tabs.get(1)));
+                    Assert.assertEquals(
+                            Arrays.asList(new Tab[] {tabs.get(1), tabs.get(2)}),
+                            mTabGroupModelFilter.getRelatedTabList(tabs.get(1).getId()));
+                });
+
+        tabCount = getRecentEntriesAndReturnActiveTabCount(recentEntries);
+        Assert.assertEquals(3, tabCount);
+        Assert.assertEquals(1, recentEntries.size());
+        assertEntryIs(
+                recentEntries.get(0),
+                RecentlyClosedGroup.class,
+                new String[] {"Group 2"},
+                group2Titles,
+                group2Urls);
+    }
+
     // TODO(crbug.com/40218713): Add a test a case where bulk closures remain in the native service,
     // but the flag state is flipped.
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillImageFetcherUtilsTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillImageFetcherUtilsTest.java
new file mode 100644
index 0000000..35e251fc
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/autofill/AutofillImageFetcherUtilsTest.java
@@ -0,0 +1,81 @@
+// Copyright 2025 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.chrome.browser.autofill;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+
+import androidx.annotation.Px;
+import androidx.core.content.ContextCompat;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+
+/** Unit tests for {@link AutofillImageFetcherUtils}. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class AutofillImageFetcherUtilsTest {
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+    }
+
+    @Test
+    @SmallTest
+    public void testTreatPixAccountImage_testAllEnhancementsApplied() {
+        @Px
+        int logoSize = AutofillImageFetcherUtils.getPixelSize(R.dimen.square_card_icon_side_length);
+        @Px
+        int iconCornerRadius =
+                AutofillImageFetcherUtils.getPixelSize(R.dimen.large_card_icon_corner_radius);
+        int borderColor = ContextCompat.getColor(mContext, R.color.baseline_neutral_90);
+        Bitmap testImage = Bitmap.createBitmap(logoSize, logoSize, Bitmap.Config.ARGB_8888);
+        // Expected treated image according to Pix specifications.
+        Bitmap expectedTreatedTestImage =
+                AutofillImageFetcherUtils.addBorder(
+                        AutofillImageFetcherUtils.roundCorners(
+                                AutofillImageFetcherUtils.addCenterAlignedBackground(
+                                        AutofillImageFetcherUtils.roundCorners(
+                                                testImage,
+                                                AutofillImageFetcherUtils.getPixelSize(
+                                                        R.dimen.square_card_icon_corner_radius)),
+                                        AutofillImageFetcherUtils.getPixelSize(
+                                                R.dimen.large_card_icon_width),
+                                        AutofillImageFetcherUtils.getPixelSize(
+                                                R.dimen.large_card_icon_height),
+                                        Color.WHITE),
+                                iconCornerRadius),
+                        iconCornerRadius,
+                        AutofillImageFetcherUtils.getPixelSize(R.dimen.card_icon_border_width),
+                        borderColor);
+
+        assertTrue(
+                AutofillImageFetcherUtils.treatPixAccountImage(testImage)
+                        .sameAs(expectedTreatedTestImage));
+    }
+
+    @Test
+    @SmallTest
+    public void testTreatPixAccountImage_testOutputImageDimensionsAreConstant() {
+        @Px int iconWidth = AutofillImageFetcherUtils.getPixelSize(R.dimen.large_card_icon_width);
+        @Px int iconHeight = AutofillImageFetcherUtils.getPixelSize(R.dimen.large_card_icon_height);
+        Bitmap testImage = Bitmap.createBitmap(500, 400, Bitmap.Config.ARGB_8888);
+
+        Bitmap treatedImage = AutofillImageFetcherUtils.treatPixAccountImage(testImage);
+
+        assertEquals(iconWidth, treatedImage.getWidth());
+        assertEquals(iconHeight, treatedImage.getHeight());
+    }
+}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/layouts/StaticLayoutUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/layouts/StaticLayoutUnitTest.java
index 46c89f8b..1e91127 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/layouts/StaticLayoutUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/layouts/StaticLayoutUnitTest.java
@@ -167,7 +167,8 @@
                         mTabContentManager,
                         mBrowserControlsStateProvider,
                         () -> mTopUiThemeColorProvider,
-                        mStaticTabSceneLayer);
+                        mStaticTabSceneLayer,
+                        false);
         mModel = mStaticLayout.getModelForTesting();
         doReturn(true).when(mUpdateHost).isActiveLayout(mStaticLayout);
 
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
index a0f703fa..474720f 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
@@ -2165,7 +2165,7 @@
         StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
 
         // Start reorder for tab drop between the 2nd and 3rd tab.
-        mStripLayoutHelper.prepareForTabDrop(/* currX= */ 300.f, /* lastX= */ 0f, false, false);
+        mStripLayoutHelper.handleDragEnter(/* currX= */ 300.f, /* lastX= */ 0f, false, false);
 
         // Test tab outline should show for the foregrounded tab in destination window during tab
         // drop.
@@ -2746,7 +2746,7 @@
         mStripLayoutHelper.updateLayout(TIMESTAMP);
 
         // Start reorder for tab drop between the 1st and 2nd tab.
-        mStripLayoutHelper.prepareForTabDrop(/* currX= */ 150.f, /* lastX= */ 0f, false, false);
+        mStripLayoutHelper.handleDragEnter(/* currX= */ 150.f, /* lastX= */ 0f, false, false);
 
         float expectedEndWidth =
                 expectedStartWidth
@@ -2768,7 +2768,7 @@
         ReorderDelegate reorderDelegateSpy = spy(mStripLayoutHelper.getReorderDelegateForTesting());
 
         // Start and stop reorder mode for tab drop.
-        mStripLayoutHelper.prepareForTabDrop(/* currX= */ 10.f, /* lastX= */ 0f, false, false);
+        mStripLayoutHelper.handleDragEnter(/* currX= */ 10.f, /* lastX= */ 0f, false, false);
         mStripLayoutHelper.stopReorderMode();
 
         // Verify: folio reattachment animation does not run for tab drop.
@@ -3610,8 +3610,8 @@
         // Start drag tab out of group or drag off strip.
         if (draggingTabOffStrip) {
             // Drag onto strip before dragged off.
-            mStripLayoutHelper.prepareForTabDrop(0f, 0f, true, false);
-            mStripLayoutHelper.clearForTabDrop(true, false);
+            mStripLayoutHelper.handleDragEnter(0f, 0f, true, false);
+            mStripLayoutHelper.handleDragExit(true, false);
         } else {
             float dragDistance =
                     ((tabs[0].getWidth() - TAB_OVERLAP_WIDTH_DP) / 2)
@@ -4374,7 +4374,7 @@
                 mStripLayoutHelper.getInReorderModeForTesting());
         assertTrue(
                 "Dragged Tab should match selected tab during drag action.",
-                mStripLayoutHelper.getReorderDelegateForTesting().getInteractingTab()
+                mStripLayoutHelper.getReorderDelegateForTesting().getInteractingTabForTesting()
                         == theClickedTab);
         mStripLayoutHelper.stopReorderMode();
         assertFalse(
@@ -4432,7 +4432,7 @@
         // Drag tab back onto strip.
         float expectedOffsetX = 123.45f;
         mStripLayoutHelper.setLastOffsetXForTesting(expectedOffsetX);
-        mStripLayoutHelper.prepareForTabDrop(0f, 0f, true, false);
+        mStripLayoutHelper.handleDragEnter(0f, 0f, true, false);
 
         // Verify we continue reorder mode with the correct x-offset.
         assertFalse(
@@ -4443,6 +4443,8 @@
                 expectedOffsetX,
                 mStripLayoutHelper.getInteractingTabForTesting().getOffsetX(),
                 EPSILON);
+
+        // Verify we continue reorder mode.
         assertTrue(
                 "Should re-enter reorder mode.", mStripLayoutHelper.getInReorderModeForTesting());
     }
@@ -4460,10 +4462,10 @@
 
         // Drag tab out of strip.
         float expectedOffsetX = 123.45f;
-        mStripLayoutHelper.prepareForTabDrop(0f, 0f, true, false);
+        mStripLayoutHelper.handleDragEnter(0f, 0f, true, false);
         StripLayoutTab draggedTab = mStripLayoutHelper.getInteractingTabForTesting();
         draggedTab.setOffsetX(expectedOffsetX);
-        mStripLayoutHelper.clearForTabDrop(true, false);
+        mStripLayoutHelper.handleDragExit(true, false);
 
         // Finish animations.
         assertNotNull(
@@ -4499,8 +4501,8 @@
 
         // Drag tab out of strip.
         mStripLayoutHelper.setTabAtPositionForTesting(draggedTab);
-        mStripLayoutHelper.prepareForTabDrop(0f, 0f, true, false);
-        mStripLayoutHelper.clearForTabDrop(true, false);
+        mStripLayoutHelper.handleDragEnter(0f, 0f, true, false);
+        mStripLayoutHelper.handleDragExit(true, false);
         mStripLayoutHelper.updateLayout(TIMESTAMP);
 
         // Verify 3rd, 4th and 5th tab's start divider is visible.
@@ -4573,7 +4575,7 @@
         mStripLayoutHelper.updateLayout(TIMESTAMP);
 
         // Prepare for tab drop.
-        mStripLayoutHelper.prepareForTabDrop(0.f, 0.f, false, false);
+        mStripLayoutHelper.handleDragEnter(0.f, 0.f, false, false);
         // Start gap will be tabWidth(265) / 2 = 132.5
         mStripLayoutHelper.setScrollOffsetForTesting(-132);
 
@@ -4611,7 +4613,7 @@
     }
 
     @Test
-    public void testPrepareForTabDrop() {
+    public void testHandleDragEnter() {
         // Setup with 5 tabs.
         initializeTest(false, false, 1, 5);
         mStripLayoutHelper.onSizeChanged(
@@ -4626,7 +4628,7 @@
         groupTabs(1, 3);
 
         // Prepare for tab drop.
-        mStripLayoutHelper.prepareForTabDrop(0.f, 0.f, false, false);
+        mStripLayoutHelper.handleDragEnter(0.f, 0.f, false, false);
 
         // Verify.
         StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
@@ -4662,7 +4664,7 @@
         mStripLayoutHelper.updateLayout(TIMESTAMP);
 
         // Prepare for tab drop.
-        mStripLayoutHelper.prepareForTabDrop(0.f, 0.f, false, false);
+        mStripLayoutHelper.handleDragEnter(0.f, 0.f, false, false);
         mStripLayoutHelper.finishAnimations();
         // Verify initial trailing margins before hover.
         StripLayoutTab[] tabs = mStripLayoutHelper.getStripLayoutTabsForTesting();
@@ -4705,7 +4707,7 @@
         mStripLayoutHelper.updateLayout(TIMESTAMP);
 
         // Prepare for tab drop.
-        mStripLayoutHelper.prepareForTabDrop(0.f, 0.f, false, false);
+        mStripLayoutHelper.handleDragEnter(0.f, 0.f, false, false);
         // Start gap will be tabWidth(265) / 2 = 132.5
         mStripLayoutHelper.setScrollOffsetForTesting(-132);
         mStripLayoutHelper.finishAnimations();
@@ -4747,14 +4749,14 @@
                 SCREEN_WIDTH, SCREEN_HEIGHT, false, TIMESTAMP, PADDING_LEFT, PADDING_RIGHT);
 
         // Prepare and verify no interaction.
-        mStripLayoutHelper.prepareForTabDrop(0.f, 0.f, false, !isIncognito);
+        mStripLayoutHelper.handleDragEnter(0.f, 0.f, false, !isIncognito);
         assertFalse(
                 "Shouldn't start reorder when dragged tab Incognito state is different.",
                 mStripLayoutHelper.getInReorderModeForTesting());
 
         // Drag and verify no interaction.
         float expectedOffset = mStripLayoutHelper.getScrollOffset();
-        mStripLayoutHelper.dragForTabDrop(TIMESTAMP, PADDING_LEFT, 0.f, 50.f, !isIncognito);
+        mStripLayoutHelper.handleDragWithin(TIMESTAMP, PADDING_LEFT, 0.f, 50.f, !isIncognito);
         assertEquals(
                 "Shouldn't have scrolled when dragged tab Incognito is different.",
                 expectedOffset,
@@ -4763,7 +4765,7 @@
 
         // Set reorder mode for testing, then clear for tab drop and verify no interaction.
         mStripLayoutHelper.startReorderModeAtIndexForTesting(0);
-        mStripLayoutHelper.clearForTabDrop(false, !isIncognito);
+        mStripLayoutHelper.handleDragExit(false, !isIncognito);
         assertTrue(
                 "Shouldn't stop reorder when dragged tab Incognito state is different.",
                 mStripLayoutHelper.getInReorderModeForTesting());
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabDragSourceTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabDragSourceTest.java
index 6c203f4..da2a9bf 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabDragSourceTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/TabDragSourceTest.java
@@ -540,7 +540,7 @@
         // Verify appropriate events are generated.
         // Strip prepares for drop on drag enter.
         verify(mSourceStripLayoutHelper, times(1))
-                .prepareForTabDrop(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
+                .handleDragEnter(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
         // Stop reorder on drop and drag end.
         verify(mSourceStripLayoutHelper, times(2)).stopReorderMode();
         // Verify tab is not moved.
@@ -576,9 +576,9 @@
         // Verify appropriate events are generated.
         // Strip prepares for drop on drag enter.
         verify(mSourceStripLayoutHelper, times(1))
-                .prepareForTabDrop(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
+                .handleDragEnter(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
         // Strip clears state for drop on drag exit.
-        verify(mSourceStripLayoutHelper, times(1)).clearForTabDrop(anyBoolean(), anyBoolean());
+        verify(mSourceStripLayoutHelper, times(1)).handleDragExit(anyBoolean(), anyBoolean());
         // Verify tab is not moved since drop is on source toolbar.
         verify(mSourceMultiInstanceManager, times(0)).moveTabToNewWindow(mTabBeingDragged);
         verify(mSourceMultiInstanceManager, times(0)).moveTabToWindow(any(), any(), anyInt());
@@ -606,9 +606,9 @@
         // Verify appropriate events are generated.
         // Strip prepares for drop on drag enter.
         verify(mSourceStripLayoutHelper, times(1))
-                .prepareForTabDrop(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
+                .handleDragEnter(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
         // Strip clears state for drop on drag exit.
-        verify(mSourceStripLayoutHelper, times(1)).clearForTabDrop(anyBoolean(), anyBoolean());
+        verify(mSourceStripLayoutHelper, times(1)).handleDragExit(anyBoolean(), anyBoolean());
         // Verify tab is not moved since drop is outside strip.
         verify(mSourceMultiInstanceManager, times(0)).moveTabToNewWindow(mTabBeingDragged);
         verify(mSourceMultiInstanceManager, times(0)).moveTabToWindow(any(), any(), anyInt());
@@ -656,9 +656,9 @@
         // Verify appropriate events are generated.
         // Strip prepares for drop on drag enter.
         verify(mSourceStripLayoutHelper, times(1))
-                .prepareForTabDrop(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
+                .handleDragEnter(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
         // Strip clears state for drop on drag exit.
-        verify(mSourceStripLayoutHelper, times(1)).clearForTabDrop(anyBoolean(), anyBoolean());
+        verify(mSourceStripLayoutHelper, times(1)).handleDragExit(anyBoolean(), anyBoolean());
         // Verify Since the drop is outside the TabToolbar area the tab will be move to a new
         // Chrome Window.
         verify(mSourceMultiInstanceManager, times(1)).moveTabToNewWindow(mTabBeingDragged);
@@ -810,7 +810,7 @@
         verify(mSourceStripLayoutHelper, times(1)).stopReorderMode();
         // Verify destination strip calls.
         verify(mDestStripLayoutHelper)
-                .prepareForTabDrop(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
+                .handleDragEnter(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
         verify(mDestStripLayoutHelper).stopReorderMode();
 
         assertNull(ShadowToast.getLatestToast());
@@ -896,14 +896,14 @@
         // Verify appropriate events are generated.
         // Source strip prepares for drop on drag enter.
         verify(mSourceStripLayoutHelper, times(1))
-                .prepareForTabDrop(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
+                .handleDragEnter(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
         // Source strip clears state for drop on drag exit.
-        verify(mSourceStripLayoutHelper, times(1)).clearForTabDrop(anyBoolean(), anyBoolean());
+        verify(mSourceStripLayoutHelper, times(1)).handleDragExit(anyBoolean(), anyBoolean());
         // Destination strip prepares for drop on drag enter.
         verify(mDestStripLayoutHelper, times(1))
-                .prepareForTabDrop(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
+                .handleDragEnter(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
         // Destination strip clears state for drop on drag exit.
-        verify(mDestStripLayoutHelper, times(1)).clearForTabDrop(anyBoolean(), anyBoolean());
+        verify(mDestStripLayoutHelper, times(1)).handleDragExit(anyBoolean(), anyBoolean());
         // Verify tab is not moved since drop is on source toolbar.
         verify(mSourceMultiInstanceManager, times(0)).moveTabToNewWindow(mTabBeingDragged);
         verify(mSourceMultiInstanceManager, times(0)).moveTabToWindow(any(), any(), anyInt());
@@ -936,7 +936,7 @@
         // Verify appropriate events are generated.
         // Strip prepares for drop on drag enter. Entered twice.
         verify(mSourceStripLayoutHelper, times(2))
-                .prepareForTabDrop(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
+                .handleDragEnter(anyFloat(), anyFloat(), anyBoolean(), anyBoolean());
         // Stop reorder on drop and drag end.
         verify(mSourceStripLayoutHelper, times(2)).stopReorderMode();
         // Verify tab is not moved.
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserverTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserverTest.java
index cfb9074..e4c28b4 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserverTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/tabbed_mode/BottomAttachedUiObserverTest.java
@@ -6,6 +6,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -350,6 +351,15 @@
         mColorChangeObserver.assertState(null, false, false);
     }
 
+    /*
+    Tests that when floating snackbar is enabled, we do not add BottomAttachedUiObserver.
+    */
+    @Test
+    @EnableFeatures(ChromeFeatureList.FLOATING_SNACKBAR)
+    public void testDoesNotAddBottomAttachedUiObserver() {
+        verify(mSnackbarManager, never()).addObserver(eq(mBottomAttachedUiObserver));
+    }
+
     @Test
     public void testSetOverlayPanelObserver() {
         verify(mOverlayPanelStateProvider).addObserver(eq(mBottomAttachedUiObserver));
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt
index 6a9a44f..537e580 100644
--- a/chrome/android/profiles/arm.newest.txt
+++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-134.0.6955.0_rc-r2-merged.afdo.bz2
+chromeos-chrome-arm-134.0.6962.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 01cdc4a..daf0aeb 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4174,7 +4174,8 @@
       "//chrome/browser/apps/link_capturing",
       "//chrome/browser/apps/link_capturing:features",
       "//chrome/browser/autofill_ai:util",
-      "//chrome/browser/contextual_cueing:contextual_cueing",
+      "//chrome/browser/contextual_cueing",
+      "//chrome/browser/contextual_cueing:impl",
       "//chrome/browser/enterprise/signals:utils",
       "//chrome/browser/error_reporting",
       "//chrome/browser/feedback:feedback_enum",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 2f9bd12..9a2e723d9 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1800,9 +1800,22 @@
 const FeatureEntry::FeatureParam kTabstripComboButtonNoBackground[] = {
     {"has_background", "false"}};
 
+const FeatureEntry::FeatureParam kTabstripComboButtonReverseButtonOrder[] = {
+    {"reverse_button_order", "true"}};
+
+const FeatureEntry::FeatureParam
+    kTabstripComboButtonReverseButtonOrderNoBackground[] = {
+        {"has_background", "false"},
+        {"reverse_button_order", "true"}};
+
 const FeatureEntry::FeatureVariation kTabstripComboButtonVariations[] = {
     {" - without background", kTabstripComboButtonNoBackground,
      std::size(kTabstripComboButtonNoBackground)},
+    {" - reverse button order", kTabstripComboButtonReverseButtonOrder,
+     std::size(kTabstripComboButtonReverseButtonOrder)},
+    {" - reverse button order & without background",
+     kTabstripComboButtonReverseButtonOrderNoBackground,
+     std::size(kTabstripComboButtonReverseButtonOrderNoBackground)},
 };
 
 #endif
@@ -4375,7 +4388,7 @@
     {"feedback-include-variations",
      flag_descriptions::kFeedbackIncludeVariationsName,
      flag_descriptions::kFeedbackIncludeVariationsDescription,
-     kOsWin | kOsLinux | kOsMac,
+     kOsWin | kOsLinux | kOsMac | kOsAndroid,
      FEATURE_VALUE_TYPE(variations::kFeedbackIncludeVariations)},
 #endif
     {"ui-disable-partial-swap", flag_descriptions::kUiPartialSwapName,
@@ -7159,6 +7172,11 @@
 #endif  // BUILDFLAG(ENABLE_PRINTING)
 
 #if BUILDFLAG(IS_WIN)
+    {"enable-windows-gaming-input-data-fetcher",
+     flag_descriptions::kEnableWindowsGamingInputDataFetcherName,
+     flag_descriptions::kEnableWindowsGamingInputDataFetcherDescription, kOsWin,
+     FEATURE_VALUE_TYPE(features::kEnableWindowsGamingInputDataFetcher)},
+
     {"windows11-mica-titlebar", flag_descriptions::kWindows11MicaTitlebarName,
      flag_descriptions::kWindows11MicaTitlebarDescription, kOsWin,
      FEATURE_VALUE_TYPE(kWindows11MicaTitlebar)},
diff --git a/chrome/browser/accessibility/embedded_a11y_extension_loader.cc b/chrome/browser/accessibility/embedded_a11y_extension_loader.cc
index 3f97ad7..e2eb44e 100644
--- a/chrome/browser/accessibility/embedded_a11y_extension_loader.cc
+++ b/chrome/browser/accessibility/embedded_a11y_extension_loader.cc
@@ -73,7 +73,7 @@
 
 EmbeddedA11yExtensionLoader::ExtensionInfo::ExtensionInfo(
     const std::string& extension_id,
-    const std::string& extension_path,
+    const base::FilePath& extension_path,
     const base::FilePath::CharType* extension_manifest_file,
     bool should_localize)
     : extension_id(extension_id),
@@ -119,7 +119,42 @@
 
 void EmbeddedA11yExtensionLoader::InstallExtensionWithId(
     const std::string& extension_id,
-    const std::string& extension_path,
+    const std::string& extension_resource_directory,
+    const base::FilePath::CharType* manifest_name,
+    bool should_localize) {
+  if (extension_map_.contains(extension_id)) {
+    return;
+  }
+
+  base::FilePath resources_path;
+#if BUILDFLAG(IS_MAC)
+  base::FilePath root_path;
+  CHECK(base::PathService::Get(base::DIR_MODULE, &root_path));
+  resources_path = root_path.Append("resources");
+#else
+  if (!base::PathService::Get(chrome::DIR_RESOURCES, &resources_path)) {
+    NOTREACHED();
+  }
+#endif
+
+  base::FilePath::StringType common_extension_directory;
+#if BUILDFLAG(IS_WIN)
+  common_extension_directory = base::UTF8ToWide(extension_resource_directory);
+#else
+  common_extension_directory = extension_resource_directory;
+#endif
+
+  auto path = resources_path.Append(common_extension_directory);
+
+  ExtensionInfo new_extension = {extension_id, path, manifest_name,
+                                 should_localize};
+  extension_map_.insert({extension_id, new_extension});
+  UpdateAllProfiles(extension_id);
+}
+
+void EmbeddedA11yExtensionLoader::InstallExtensionWithIdAndPath(
+    const std::string& extension_id,
+    const base::FilePath& extension_path,
     const base::FilePath::CharType* manifest_name,
     bool should_localize) {
   if (extension_map_.contains(extension_id)) {
@@ -214,7 +249,7 @@
 void EmbeddedA11yExtensionLoader::MaybeInstallExtension(
     Profile* profile,
     const std::string& extension_id,
-    const std::string& extension_path,
+    const base::FilePath& extension_path,
     const base::FilePath::CharType* manifest_name,
     bool should_localize) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
@@ -223,33 +258,13 @@
     return;
   }
 
-  base::FilePath resources_path;
-#if BUILDFLAG(IS_MAC)
-  base::FilePath root_path;
-  CHECK(base::PathService::Get(base::DIR_MODULE, &root_path));
-  resources_path = root_path.Append("resources");
-#else
-  if (!base::PathService::Get(chrome::DIR_RESOURCES, &resources_path)) {
-    NOTREACHED();
-  }
-#endif
-
-  base::FilePath::StringType common_path;
-#if BUILDFLAG(IS_WIN)
-  common_path = base::UTF8ToWide(extension_path);
-#else
-  common_path = extension_path;
-#endif
-
-  auto path = resources_path.Append(common_path);
-
   extensions::GetExtensionFileTaskRunner()->PostTaskAndReplyWithResult(
       FROM_HERE,
-      base::BindOnce(&LoadManifestOnFileThread, path, manifest_name,
+      base::BindOnce(&LoadManifestOnFileThread, extension_path, manifest_name,
                      /*localize=*/should_localize),
       base::BindOnce(&EmbeddedA11yExtensionLoader::InstallExtension,
-                     weak_ptr_factory_.GetWeakPtr(), component_loader, path,
-                     extension_id));
+                     weak_ptr_factory_.GetWeakPtr(), component_loader,
+                     extension_path, extension_id));
 }
 
 void EmbeddedA11yExtensionLoader::InstallExtension(
diff --git a/chrome/browser/accessibility/embedded_a11y_extension_loader.h b/chrome/browser/accessibility/embedded_a11y_extension_loader.h
index b6ffbdd..60a9414 100644
--- a/chrome/browser/accessibility/embedded_a11y_extension_loader.h
+++ b/chrome/browser/accessibility/embedded_a11y_extension_loader.h
@@ -35,7 +35,7 @@
   // profiles.
   struct ExtensionInfo {
     ExtensionInfo(const std::string& extension_id,
-                  const std::string& extension_path,
+                  const base::FilePath& extension_path,
                   const base::FilePath::CharType* extension_manifest_file,
                   bool should_localize);
     ExtensionInfo(const ExtensionInfo& other);
@@ -48,7 +48,7 @@
     const std::string extension_id;
 
     // The path to the extension manifest file.
-    const std::string extension_path;
+    const base::FilePath extension_path;
 
     // The name of the extension manifest file.
     const base::FilePath::CharType* extension_manifest_file;
@@ -72,9 +72,14 @@
   // `manifest_name` must live for the duration of the program. (e.g. be
   // statically allocated)
   void InstallExtensionWithId(const std::string& extension_id,
-                              const std::string& extension_path,
+                              const std::string& extension_resource_directory,
                               const base::FilePath::CharType* manifest_name,
                               bool should_localize);
+  void InstallExtensionWithIdAndPath(
+      const std::string& extension_id,
+      const base::FilePath& extension_path,
+      const base::FilePath::CharType* manifest_name,
+      bool should_localize);
   void RemoveExtensionWithId(const std::string& extension_id);
 
   // We can't use extensions::ExtensionHostTestHelper as those require a
@@ -104,7 +109,7 @@
   // if it isn't yet installed.
   void MaybeInstallExtension(Profile* profile,
                              const std::string& extension_id,
-                             const std::string& extension_path,
+                             const base::FilePath& extension_path,
                              const base::FilePath::CharType* manifest_name,
                              bool should_localize);
 
diff --git a/chrome/browser/accessibility/embedded_a11y_extension_loader_browsertest.cc b/chrome/browser/accessibility/embedded_a11y_extension_loader_browsertest.cc
index 78b2232..2f15adf 100644
--- a/chrome/browser/accessibility/embedded_a11y_extension_loader_browsertest.cc
+++ b/chrome/browser/accessibility/embedded_a11y_extension_loader_browsertest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/accessibility/embedded_a11y_extension_loader.h"
 
+#include "base/path_service.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/extensions/component_loader.h"
@@ -120,6 +121,30 @@
       profile, extension_misc::kReadingModeGDocsHelperExtensionId);
 }
 
+IN_PROC_BROWSER_TEST_F(EmbeddedA11yExtensionLoaderTest,
+                       InstallExtensionWithIdAndPath) {
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  const auto& profiles = profile_manager->GetLoadedProfiles();
+  ASSERT_GT(profiles.size(), 0u);
+  Profile* profile = profiles[0];
+
+  char manifest_id[] = "cjlaeehoipngghikfjogbdkpbdgebppb";
+  base::FilePath source_root_dir;
+  base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &source_root_dir);
+  base::FilePath extension_path = source_root_dir.AppendASCII("chrome")
+                                      .AppendASCII("test")
+                                      .AppendASCII("data")
+                                      .AppendASCII("accessibility")
+                                      .AppendASCII("extension");
+  base::FilePath::CharType manifest_name[] = FILE_PATH_LITERAL("manifest.json");
+  auto* embedded_a11y_extension_loader =
+      EmbeddedA11yExtensionLoader::GetInstance();
+  embedded_a11y_extension_loader->InstallExtensionWithIdAndPath(
+      manifest_id, extension_path, manifest_name, /*should_localize=*/false);
+  WaitForExtensionLoaded(profile, manifest_id);
+  RemoveAndWaitForExtensionUnloaded(profile, manifest_id);
+}
+
 #if !BUILDFLAG(IS_CHROMEOS)
 IN_PROC_BROWSER_TEST_F(EmbeddedA11yExtensionLoaderTest,
                        InstallsOnMultipleProfiles) {
diff --git a/chrome/browser/ai/ai_manager.cc b/chrome/browser/ai/ai_manager.cc
index b9d33f04..a2f3ec5 100644
--- a/chrome/browser/ai/ai_manager.cc
+++ b/chrome/browser/ai/ai_manager.cc
@@ -391,55 +391,41 @@
 void AIManager::CanCreateWriter(blink::mojom::AIWriterCreateOptionsPtr options,
                                 CanCreateWriterCallback callback) {
   // TODO(crbug.com/382596381): Check Options.
-  // TODO(crbug.com/382325795): Use kWritingAssistanceApi instead of kCompose.
-  CanCreateSession(optimization_guide::ModelBasedCapabilityKey::kCompose,
-                   std::move(callback));
+  CanCreateSession(
+      optimization_guide::ModelBasedCapabilityKey::kWritingAssistanceApi,
+      std::move(callback));
 }
 
 void AIManager::CreateWriter(
     mojo::PendingRemote<blink::mojom::AIManagerCreateWriterClient> client,
     blink::mojom::AIWriterCreateOptionsPtr options) {
-  // TODO(crbug.com/382325795): Use kWritingAssistanceApi instead of kCompose.
   CreateContextBoundObjectTask<AIWriter, blink::mojom::AIWriter,
                                blink::mojom::AIManagerCreateWriterClient,
                                blink::mojom::AIWriterCreateOptionsPtr>::
-      CreateAndStart(browser_context_,
-                     optimization_guide::ModelBasedCapabilityKey::kCompose,
-                     context_bound_object_set_, std::move(options),
-                     std::move(client));
+      CreateAndStart(
+          browser_context_,
+          optimization_guide::ModelBasedCapabilityKey::kWritingAssistanceApi,
+          context_bound_object_set_, std::move(options), std::move(client));
 }
 
 void AIManager::CanCreateRewriter(
     blink::mojom::AIRewriterCreateOptionsPtr options,
     CanCreateRewriterCallback callback) {
-  // TODO(crbug.com/382615217): Check Options.
-  // TODO(crbug.com/382325795): Use kWritingAssistanceApi instead of kCompose.
-  CanCreateSession(optimization_guide::ModelBasedCapabilityKey::kCompose,
-                   std::move(callback));
+  CanCreateSession(
+      optimization_guide::ModelBasedCapabilityKey::kWritingAssistanceApi,
+      std::move(callback));
 }
 
 void AIManager::CreateRewriter(
     mojo::PendingRemote<blink::mojom::AIManagerCreateRewriterClient> client,
     blink::mojom::AIRewriterCreateOptionsPtr options) {
-  if (options->tone != blink::mojom::AIRewriterTone::kAsIs &&
-      options->length != blink::mojom::AIRewriterLength::kAsIs) {
-    // TODO(crbug.com/358214322): Currently the combination of the tone and the
-    // length option is not supported.
-    // TODO(crbug.com/358214322): Return an error enum and throw a clear
-    // exception from the blink side.
-    mojo::Remote<blink::mojom::AIManagerCreateRewriterClient> client_remote(
-        std::move(client));
-    client_remote->OnResult(mojo::PendingRemote<blink::mojom::AIRewriter>());
-    return;
-  }
-  // TODO(crbug.com/382325795): Use kWritingAssistanceApi instead of kCompose.
   CreateContextBoundObjectTask<AIRewriter, blink::mojom::AIRewriter,
                                blink::mojom::AIManagerCreateRewriterClient,
                                blink::mojom::AIRewriterCreateOptionsPtr>::
-      CreateAndStart(browser_context_,
-                     optimization_guide::ModelBasedCapabilityKey::kCompose,
-                     context_bound_object_set_, std::move(options),
-                     std::move(client));
+      CreateAndStart(
+          browser_context_,
+          optimization_guide::ModelBasedCapabilityKey::kWritingAssistanceApi,
+          context_bound_object_set_, std::move(options), std::move(client));
 }
 
 void AIManager::CanCreateSession(
diff --git a/chrome/browser/ai/ai_rewriter.cc b/chrome/browser/ai/ai_rewriter.cc
index fb8f13fa..34724b6f 100644
--- a/chrome/browser/ai/ai_rewriter.cc
+++ b/chrome/browser/ai/ai_rewriter.cc
@@ -10,9 +10,61 @@
 #include "chrome/browser/ai/ai_utils.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
 #include "components/optimization_guide/proto/common_types.pb.h"
-#include "components/optimization_guide/proto/features/compose.pb.h"
 #include "third_party/blink/public/mojom/ai/model_streaming_responder.mojom.h"
 
+namespace {
+
+optimization_guide::proto::WritingAssistanceApiOutputTone ToProtoTone(
+    blink::mojom::AIRewriterTone type) {
+  switch (type) {
+    case blink::mojom::AIRewriterTone::kAsIs:
+      // Rewriter config handles neutral tone semantically like "as-is".
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_TONE_NEUTRAL;
+    case blink::mojom::AIRewriterTone::kMoreFormal:
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_TONE_FORMAL;
+    case blink::mojom::AIRewriterTone::kMoreCasual:
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_TONE_CASUAL;
+  }
+}
+
+optimization_guide::proto::WritingAssistanceApiOutputFormat ToProtoFormat(
+    blink::mojom::AIRewriterFormat format) {
+  switch (format) {
+    case blink::mojom::AIRewriterFormat::kAsIs:
+      // Rewriter config handles unspecified format by omitting instructions.
+      NOTIMPLEMENTED() << "TODO: Improve AIRewriterFormat::kAsIs support";
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_FORMAT_NOT_SPECIFIED;
+    case blink::mojom::AIRewriterFormat::kPlainText:
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_FORMAT_PLAIN_TEXT;
+    case blink::mojom::AIRewriterFormat::kMarkdown:
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_FORMAT_MARKDOWN;
+  }
+}
+
+optimization_guide::proto::WritingAssistanceApiOutputLength ToProtoLength(
+    blink::mojom::AIRewriterLength length) {
+  switch (length) {
+    case blink::mojom::AIRewriterLength::kAsIs:
+      // Rewriter config handles medium length semantically like "as-is".
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_LENGTH_MEDIUM;
+    case blink::mojom::AIRewriterLength::kShorter:
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_LENGTH_SHORT;
+    case blink::mojom::AIRewriterLength::kLonger:
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_LENGTH_LONG;
+  }
+}
+
+}  // namespace
+
 AIRewriter::AIRewriter(
     AIContextBoundObjectSet& context_bound_object_set,
     std::unique_ptr<optimization_guide::OptimizationGuideModelExecutor::Session>
@@ -34,46 +86,31 @@
   }
 }
 
+// static
+std::unique_ptr<optimization_guide::proto::WritingAssistanceApiOptions>
+AIRewriter::ToProtoOptions(
+    const blink::mojom::AIRewriterCreateOptionsPtr& options) {
+  auto proto_options = std::make_unique<
+      optimization_guide::proto::WritingAssistanceApiOptions>();
+  proto_options->set_output_tone(ToProtoTone(options->tone));
+  proto_options->set_output_format(ToProtoFormat(options->format));
+  proto_options->set_output_length(ToProtoLength(options->length));
+  return proto_options;
+}
+
 void AIRewriter::Rewrite(
     const std::string& input,
     const std::optional<std::string>& context,
     mojo::PendingRemote<blink::mojom::ModelStreamingResponder>
         pending_responder) {
-  optimization_guide::proto::ComposePageMetadata page_metadata;
-  std::string context_string = base::JoinString(
-      {options_->shared_context.value_or(""), context.value_or("")}, "\n");
-  base::TrimString(context_string, "\n", &context_string);
-  page_metadata.set_trimmed_page_inner_text(
-      context_string.substr(0, AIUtils::kTrimmedInnerTextMaxChars));
-  page_metadata.set_page_inner_text(context_string);
-
-  optimization_guide::proto::ComposeRequest context_request;
-  *context_request.mutable_page_metadata() = std::move(page_metadata);
-
-  session_->AddContext(context_request);
-
-  optimization_guide::proto::ComposeRequest execute_request;
-  // TODO(crbug.com/358214322): We don't support the combination of tone and
-  // length.
-  if (options_->tone == blink::mojom::AIRewriterTone::kMoreFormal) {
-    execute_request.mutable_rewrite_params()->set_tone(
-        optimization_guide::proto::ComposeTone::COMPOSE_FORMAL);
-  } else if (options_->tone == blink::mojom::AIRewriterTone::kMoreCasual) {
-    execute_request.mutable_rewrite_params()->set_tone(
-        optimization_guide::proto::ComposeTone::COMPOSE_INFORMAL);
-  } else if (options_->length == blink::mojom::AIRewriterLength::kLonger) {
-    execute_request.mutable_rewrite_params()->set_length(
-        optimization_guide::proto::ComposeLength::COMPOSE_LONGER);
-  } else if (options_->length == blink::mojom::AIRewriterLength::kShorter) {
-    execute_request.mutable_rewrite_params()->set_length(
-        optimization_guide::proto::ComposeLength::COMPOSE_SHORTER);
-  } else {
-    execute_request.mutable_rewrite_params()->set_regenerate(true);
-  }
-  execute_request.mutable_rewrite_params()->set_previous_response(input);
-
+  optimization_guide::proto::WritingAssistanceApiRequest request;
+  request.set_context(context.value_or(std::string()));
+  request.set_allocated_options(ToProtoOptions(options_).release());
+  request.set_rewrite_text(input);
+  // TODO(crbug.com/390006887): Pass shared context with session creation.
+  request.set_shared_context(options_->shared_context.value_or(std::string()));
   session_->ExecuteModel(
-      execute_request,
+      request,
       base::BindRepeating(&AIRewriter::ModelExecutionCallback,
                           weak_ptr_factory_.GetWeakPtr(),
                           responder_set_.Add(std::move(pending_responder))));
@@ -93,11 +130,12 @@
     return;
   }
 
-  auto compose_response = optimization_guide::ParsedAnyMetadata<
-      optimization_guide::proto::ComposeResponse>(result.response->response);
-  if (compose_response) {
+  auto response = optimization_guide::ParsedAnyMetadata<
+      optimization_guide::proto::WritingAssistanceApiResponse>(
+      result.response->response);
+  if (response) {
     responder->OnStreaming(
-        compose_response->output(),
+        response->output(),
         blink::mojom::ModelStreamingResponderAction::kReplace);
   }
   if (result.response->is_complete) {
diff --git a/chrome/browser/ai/ai_rewriter.h b/chrome/browser/ai/ai_rewriter.h
index 11b4319..66ff935 100644
--- a/chrome/browser/ai/ai_rewriter.h
+++ b/chrome/browser/ai/ai_rewriter.h
@@ -10,6 +10,7 @@
 
 #include "chrome/browser/ai/ai_context_bound_object.h"
 #include "components/optimization_guide/core/optimization_guide_model_executor.h"
+#include "components/optimization_guide/proto/features/writing_assistance_api.pb.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
@@ -30,9 +31,11 @@
       mojo::PendingReceiver<blink::mojom::AIRewriter> receiver);
   AIRewriter(const AIRewriter&) = delete;
   AIRewriter& operator=(const AIRewriter&) = delete;
-
   ~AIRewriter() override;
 
+  static std::unique_ptr<optimization_guide::proto::WritingAssistanceApiOptions>
+  ToProtoOptions(const blink::mojom::AIRewriterCreateOptionsPtr& options);
+
   // `blink::mojom::AIRewriter` implementation.
   void Rewrite(const std::string& input,
                const std::optional<std::string>& context,
diff --git a/chrome/browser/ai/ai_rewriter_unittest.cc b/chrome/browser/ai/ai_rewriter_unittest.cc
index d3c8b46..593032a 100644
--- a/chrome/browser/ai/ai_rewriter_unittest.cc
+++ b/chrome/browser/ai/ai_rewriter_unittest.cc
@@ -14,6 +14,7 @@
 #include "components/optimization_guide/core/optimization_guide_proto_util.h"
 #include "components/optimization_guide/core/optimization_guide_switches.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
+#include "components/optimization_guide/proto/features/writing_assistance_api.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/ai/ai_manager.mojom.h"
@@ -25,8 +26,6 @@
 
 constexpr char kSharedContextString[] = "test shared context";
 constexpr char kContextString[] = "test context";
-constexpr char kConcatenatedContextString[] =
-    "test shared context\ntest context";
 constexpr char kInputString[] = "input string";
 
 class MockCreateRewriterClient
@@ -53,7 +52,7 @@
 
 optimization_guide::OptimizationGuideModelStreamingExecutionResult
 CreateExecutionResult(std::string_view output, bool is_complete) {
-  optimization_guide::proto::ComposeResponse response;
+  optimization_guide::proto::WritingAssistanceApiResponse response;
   *response.mutable_output() = output;
   return optimization_guide::OptimizationGuideModelStreamingExecutionResult(
       optimization_guide::StreamingResponse{
@@ -71,169 +70,93 @@
       /*provided_by_on_device=*/true);
 }
 
-void CheckComposeRequestContext(
-    const google::protobuf::MessageLite& request_metadata,
-    const std::string& expected_context_string) {
-  const optimization_guide::proto::ComposeRequest* request =
-      static_cast<const optimization_guide::proto::ComposeRequest*>(
-          &request_metadata);
-  EXPECT_THAT(request->page_metadata().page_inner_text(),
-              expected_context_string);
-  EXPECT_THAT(request->page_metadata().trimmed_page_inner_text(),
-              expected_context_string);
+blink::mojom::AIRewriterCreateOptionsPtr GetDefaultOptions() {
+  return blink::mojom::AIRewriterCreateOptions::New(
+      kSharedContextString, blink::mojom::AIRewriterTone::kAsIs,
+      blink::mojom::AIRewriterFormat::kAsIs,
+      blink::mojom::AIRewriterLength::kAsIs);
 }
 
-void CheckComposeRequestRewriteParamsPreviousResponse(
-    const google::protobuf::MessageLite& request_metadata,
-    const std::string& previous_response) {
-  const optimization_guide::proto::ComposeRequest* request =
-      static_cast<const optimization_guide::proto::ComposeRequest*>(
-          &request_metadata);
-  EXPECT_THAT(request->rewrite_params().previous_response(), previous_response);
-}
-
-void CheckComposeRequestRewriteParamsTone(
-    const google::protobuf::MessageLite& request_metadata,
-    optimization_guide::proto::ComposeTone tone) {
-  const optimization_guide::proto::ComposeRequest* request =
-      static_cast<const optimization_guide::proto::ComposeRequest*>(
-          &request_metadata);
-  EXPECT_EQ(request->rewrite_params().tone(), tone);
-}
-
-void CheckComposeRequestRewriteParamsLength(
-    const google::protobuf::MessageLite& request_metadata,
-    optimization_guide::proto::ComposeLength length) {
-  const optimization_guide::proto::ComposeRequest* request =
-      static_cast<const optimization_guide::proto::ComposeRequest*>(
-          &request_metadata);
-  EXPECT_EQ(request->rewrite_params().length(), length);
-}
-
-void CheckComposeRequestRewriteParamsRegenerateFlag(
-    const google::protobuf::MessageLite& request_metadata) {
-  const optimization_guide::proto::ComposeRequest* request =
-      static_cast<const optimization_guide::proto::ComposeRequest*>(
-          &request_metadata);
-  EXPECT_TRUE(request->rewrite_params().regenerate());
+std::unique_ptr<optimization_guide::proto::WritingAssistanceApiOptions>
+GetDefaultExpectedOptions() {
+  return AIRewriter::ToProtoOptions(GetDefaultOptions());
 }
 
 }  // namespace
 
 class AIRewriterTest : public AITestUtils::AITestBase {
  protected:
-  void RunSimpleRewriteTest(
-      blink::mojom::AIRewriterTone tone,
-      blink::mojom::AIRewriterFormat format,
-      blink::mojom::AIRewriterLength length,
-      base::OnceCallback<void(const google::protobuf::MessageLite&
-                                  request_metadata)> request_check_callback);
-  void RunRewriteOptionCombinationFailureTest(
-      blink::mojom::AIRewriterTone tone,
-      blink::mojom::AIRewriterFormat format,
-      blink::mojom::AIRewriterLength length);
-};
+  void RunSimpleRewriteTest(blink::mojom::AIRewriterTone tone,
+                            blink::mojom::AIRewriterFormat format,
+                            blink::mojom::AIRewriterLength length) {
+    const auto options = blink::mojom::AIRewriterCreateOptions::New(
+        kSharedContextString, tone, format, length);
 
-void AIRewriterTest::RunSimpleRewriteTest(
-    blink::mojom::AIRewriterTone tone,
-    blink::mojom::AIRewriterFormat format,
-    blink::mojom::AIRewriterLength length,
-    base::OnceCallback<void(const google::protobuf::MessageLite&
-                                request_metadata)> request_check_callback) {
-  SetupMockOptimizationGuideKeyedService();
-  EXPECT_CALL(*mock_optimization_guide_keyed_service_, StartSession(_, _))
-      .WillOnce(testing::Invoke([&](optimization_guide::ModelBasedCapabilityKey
-                                        feature,
-                                    const std::optional<
-                                        optimization_guide::
-                                            SessionConfigParams>&
-                                        config_params) {
-        auto session = std::make_unique<optimization_guide::MockSession>();
+    EXPECT_CALL(*mock_optimization_guide_keyed_service_, StartSession(_, _))
+        .WillOnce(testing::Invoke([&](optimization_guide::
+                                          ModelBasedCapabilityKey feature,
+                                      const std::optional<
+                                          optimization_guide::
+                                              SessionConfigParams>&
+                                          config_params) {
+          auto session = std::make_unique<optimization_guide::MockSession>();
+          EXPECT_CALL(*session, ExecuteModel(_, _))
+              .WillOnce(testing::Invoke(
+                  [&](const google::protobuf::MessageLite& request_metadata,
+                      optimization_guide::
+                          OptimizationGuideModelExecutionResultStreamingCallback
+                              callback) {
+                    AITestUtils::CheckWritingAssistanceApiRequest(
+                        request_metadata, kSharedContextString, kContextString,
+                        *AIRewriter::ToProtoOptions(options), kInputString);
+                    callback.Run(CreateExecutionResult("Result text",
+                                                       /*is_complete=*/true));
+                  }));
+          return session;
+        }));
 
-        EXPECT_CALL(*session, AddContext(_))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata) {
-                  CheckComposeRequestContext(request_metadata,
-                                             kConcatenatedContextString);
-                }));
-        EXPECT_CALL(*session, ExecuteModel(_, _))
-            .WillOnce(testing::Invoke(
-                [&](const google::protobuf::MessageLite& request_metadata,
-                    optimization_guide::
-                        OptimizationGuideModelExecutionResultStreamingCallback
-                            callback) {
-                  CheckComposeRequestRewriteParamsPreviousResponse(
-                      request_metadata, kInputString);
-                  std::move(request_check_callback).Run(request_metadata);
-                  callback.Run(CreateExecutionResult("Result text",
-                                                     /*is_complete=*/true));
-                }));
-        return session;
-      }));
+    mojo::Remote<blink::mojom::AIRewriter> rewriter_remote;
+    {
+      MockCreateRewriterClient mock_create_rewriter_client;
+      base::RunLoop run_loop;
+      EXPECT_CALL(mock_create_rewriter_client, OnResult(_))
+          .WillOnce(testing::Invoke(
+              [&](mojo::PendingRemote<::blink::mojom::AIRewriter> rewriter) {
+                EXPECT_TRUE(rewriter);
+                rewriter_remote =
+                    mojo::Remote<blink::mojom::AIRewriter>(std::move(rewriter));
+                run_loop.Quit();
+              }));
 
-  mojo::Remote<blink::mojom::AIRewriter> rewriter_remote;
-  {
-    MockCreateRewriterClient mock_create_rewriter_client;
+      mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
+      ai_manager->CreateRewriter(
+          mock_create_rewriter_client.BindNewPipeAndPassRemote(),
+          options.Clone());
+      run_loop.Run();
+    }
+    AITestUtils::MockModelStreamingResponder mock_responder;
+
     base::RunLoop run_loop;
-    EXPECT_CALL(mock_create_rewriter_client, OnResult(_))
+    EXPECT_CALL(mock_responder, OnStreaming(_, _))
         .WillOnce(testing::Invoke(
-            [&](mojo::PendingRemote<::blink::mojom::AIRewriter> rewriter) {
-              EXPECT_TRUE(rewriter);
-              rewriter_remote =
-                  mojo::Remote<blink::mojom::AIRewriter>(std::move(rewriter));
+            [&](const std::string& text,
+                blink::mojom::ModelStreamingResponderAction action) {
+              EXPECT_THAT(text, "Result text");
+              EXPECT_EQ(action,
+                        blink::mojom::ModelStreamingResponderAction::kReplace);
+            }));
+
+    EXPECT_CALL(mock_responder, OnCompletion(_))
+        .WillOnce(testing::Invoke(
+            [&](blink::mojom::ModelExecutionContextInfoPtr context_info) {
               run_loop.Quit();
             }));
 
-    mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
-    ai_manager->CreateRewriter(
-        mock_create_rewriter_client.BindNewPipeAndPassRemote(),
-        blink::mojom::AIRewriterCreateOptions::New(kSharedContextString, tone,
-                                                   format, length));
+    rewriter_remote->Rewrite(kInputString, kContextString,
+                             mock_responder.BindNewPipeAndPassRemote());
     run_loop.Run();
   }
-  AITestUtils::MockModelStreamingResponder mock_responder;
-
-  base::RunLoop run_loop;
-  EXPECT_CALL(mock_responder, OnStreaming(_, _))
-      .WillOnce(testing::Invoke(
-          [&](const std::string& text,
-              blink::mojom::ModelStreamingResponderAction action) {
-            EXPECT_THAT(text, "Result text");
-            EXPECT_EQ(action,
-                      blink::mojom::ModelStreamingResponderAction::kReplace);
-          }));
-
-  EXPECT_CALL(mock_responder, OnCompletion(_))
-      .WillOnce(testing::Invoke(
-          [&](blink::mojom::ModelExecutionContextInfoPtr context_info) {
-            run_loop.Quit();
-          }));
-
-  rewriter_remote->Rewrite(kInputString, kContextString,
-                           mock_responder.BindNewPipeAndPassRemote());
-  run_loop.Run();
-}
-
-void AIRewriterTest::RunRewriteOptionCombinationFailureTest(
-    blink::mojom::AIRewriterTone tone,
-    blink::mojom::AIRewriterFormat format,
-    blink::mojom::AIRewriterLength length) {
-  MockCreateRewriterClient mock_create_rewriter_client;
-  base::RunLoop run_loop;
-  EXPECT_CALL(mock_create_rewriter_client, OnResult(_))
-      .WillOnce(testing::Invoke(
-          [&](mojo::PendingRemote<::blink::mojom::AIRewriter> rewriter) {
-            EXPECT_FALSE(rewriter);
-            run_loop.Quit();
-          }));
-
-  mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
-  ai_manager->CreateRewriter(
-      mock_create_rewriter_client.BindNewPipeAndPassRemote(),
-      blink::mojom::AIRewriterCreateOptions::New(kSharedContextString, tone,
-                                                 format, length));
-  run_loop.Run();
-}
+};
 
 TEST_F(AIRewriterTest, CreateRewriterNoService) {
   SetupNullOptimizationGuideKeyedService();
@@ -250,10 +173,7 @@
   mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
   ai_manager->CreateRewriter(
       mock_create_rewriter_client.BindNewPipeAndPassRemote(),
-      blink::mojom::AIRewriterCreateOptions::New(
-          kSharedContextString, blink::mojom::AIRewriterTone::kAsIs,
-          blink::mojom::AIRewriterFormat::kAsIs,
-          blink::mojom::AIRewriterLength::kAsIs));
+      GetDefaultOptions());
   run_loop.Run();
 }
 
@@ -284,10 +204,7 @@
   mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
   ai_manager->CreateRewriter(
       mock_create_rewriter_client.BindNewPipeAndPassRemote(),
-      blink::mojom::AIRewriterCreateOptions::New(
-          kSharedContextString, blink::mojom::AIRewriterTone::kAsIs,
-          blink::mojom::AIRewriterFormat::kAsIs,
-          blink::mojom::AIRewriterLength::kAsIs));
+      GetDefaultOptions());
   run_loop.Run();
 }
 
@@ -346,22 +263,19 @@
   mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
   ai_manager->CreateRewriter(
       mock_create_rewriter_client.BindNewPipeAndPassRemote(),
-      blink::mojom::AIRewriterCreateOptions::New(
-          kSharedContextString, blink::mojom::AIRewriterTone::kAsIs,
-          blink::mojom::AIRewriterFormat::kAsIs,
-          blink::mojom::AIRewriterLength::kAsIs));
+      GetDefaultOptions());
 
   run_loop_for_add_observer.Run();
   CHECK(availability_observer);
   // Send `kConfigNotAvailableForFeature` first to the observer.
   availability_observer->OnDeviceModelAvailabilityChanged(
-      optimization_guide::ModelBasedCapabilityKey::kCompose,
+      optimization_guide::ModelBasedCapabilityKey::kWritingAssistanceApi,
       optimization_guide::OnDeviceModelEligibilityReason::
           kConfigNotAvailableForFeature);
 
   // And then send `kConfigNotAvailableForFeature` to the observer.
   availability_observer->OnDeviceModelAvailabilityChanged(
-      optimization_guide::ModelBasedCapabilityKey::kCompose,
+      optimization_guide::ModelBasedCapabilityKey::kWritingAssistanceApi,
       optimization_guide::OnDeviceModelEligibilityReason::kSuccess);
 
   // OnResult() should be called.
@@ -412,10 +326,7 @@
   mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
   ai_manager->CreateRewriter(
       mock_create_rewriter_client->BindNewPipeAndPassRemote(),
-      blink::mojom::AIRewriterCreateOptions::New(
-          kSharedContextString, blink::mojom::AIRewriterTone::kAsIs,
-          blink::mojom::AIRewriterFormat::kAsIs,
-          blink::mojom::AIRewriterLength::kAsIs));
+      GetDefaultOptions());
 
   run_loop_for_add_observer.Run();
   CHECK(availability_observer);
@@ -427,101 +338,37 @@
   run_loop_for_remove_observer.Run();
 }
 
-TEST_F(AIRewriterTest, RewriteRegenerate) {
-  RunSimpleRewriteTest(
-      blink::mojom::AIRewriterTone::kAsIs,
-      blink::mojom::AIRewriterFormat::kAsIs,
-      blink::mojom::AIRewriterLength::kAsIs,
-      base::BindLambdaForTesting(
-          [&](const google::protobuf::MessageLite& request_metadata) {
-            CheckComposeRequestRewriteParamsRegenerateFlag(request_metadata);
-          }));
+TEST_F(AIRewriterTest, RewriteDefault) {
+  SetupMockOptimizationGuideKeyedService();
+  RunSimpleRewriteTest(blink::mojom::AIRewriterTone::kAsIs,
+                       blink::mojom::AIRewriterFormat::kAsIs,
+                       blink::mojom::AIRewriterLength::kAsIs);
 }
 
-TEST_F(AIRewriterTest, RewriteMoreCasual) {
-  RunSimpleRewriteTest(
-      blink::mojom::AIRewriterTone::kMoreCasual,
-      blink::mojom::AIRewriterFormat::kAsIs,
-      blink::mojom::AIRewriterLength::kAsIs,
-      base::BindLambdaForTesting(
-          [&](const google::protobuf::MessageLite& request_metadata) {
-            CheckComposeRequestRewriteParamsTone(
-                request_metadata,
-                optimization_guide::proto::ComposeTone::COMPOSE_INFORMAL);
-          }));
-}
-
-TEST_F(AIRewriterTest, RewriteMoreFormal) {
-  RunSimpleRewriteTest(
-      blink::mojom::AIRewriterTone::kMoreFormal,
-      blink::mojom::AIRewriterFormat::kAsIs,
-      blink::mojom::AIRewriterLength::kAsIs,
-      base::BindLambdaForTesting(
-          [&](const google::protobuf::MessageLite& request_metadata) {
-            CheckComposeRequestRewriteParamsTone(
-                request_metadata,
-                optimization_guide::proto::ComposeTone::COMPOSE_FORMAL);
-          }));
-}
-
-TEST_F(AIRewriterTest, RewriteLonger) {
-  RunSimpleRewriteTest(
-      blink::mojom::AIRewriterTone::kAsIs,
-      blink::mojom::AIRewriterFormat::kAsIs,
-      blink::mojom::AIRewriterLength::kLonger,
-      base::BindLambdaForTesting(
-          [&](const google::protobuf::MessageLite& request_metadata) {
-            CheckComposeRequestRewriteParamsLength(
-                request_metadata,
-                optimization_guide::proto::ComposeLength::COMPOSE_LONGER);
-          }));
-}
-
-TEST_F(AIRewriterTest, RewriteShorter) {
-  RunSimpleRewriteTest(
-      blink::mojom::AIRewriterTone::kAsIs,
-      blink::mojom::AIRewriterFormat::kAsIs,
-      blink::mojom::AIRewriterLength::kShorter,
-      base::BindLambdaForTesting(
-          [&](const google::protobuf::MessageLite& request_metadata) {
-            CheckComposeRequestRewriteParamsLength(
-                request_metadata,
-                optimization_guide::proto::ComposeLength::COMPOSE_SHORTER);
-          }));
-}
-
-TEST_F(AIRewriterTest, RewriteOptionCombinationFailureTest) {
+TEST_F(AIRewriterTest, RewriteWithOptions) {
   SetupMockOptimizationGuideKeyedService();
   blink::mojom::AIRewriterTone tones[]{
-      blink::mojom::AIRewriterTone::kMoreCasual,
+      blink::mojom::AIRewriterTone::kAsIs,
       blink::mojom::AIRewriterTone::kMoreFormal,
+      blink::mojom::AIRewriterTone::kMoreCasual,
   };
   blink::mojom::AIRewriterFormat formats[]{
+      blink::mojom::AIRewriterFormat::kAsIs,
       blink::mojom::AIRewriterFormat::kPlainText,
       blink::mojom::AIRewriterFormat::kMarkdown,
   };
   blink::mojom::AIRewriterLength lengths[]{
-      blink::mojom::AIRewriterLength::kLonger,
+      blink::mojom::AIRewriterLength::kAsIs,
       blink::mojom::AIRewriterLength::kShorter,
+      blink::mojom::AIRewriterLength::kLonger,
   };
-  // Any combination with more than one non-kAsIs value fails.
   for (const auto& tone : tones) {
     for (const auto& format : formats) {
       for (const auto& length : lengths) {
-        RunRewriteOptionCombinationFailureTest(tone, format, length);
+        SCOPED_TRACE(testing::Message()
+                     << tone << " " << format << " " << length);
+        RunSimpleRewriteTest(tone, format, length);
       }
-      RunRewriteOptionCombinationFailureTest(
-          tone, format, blink::mojom::AIRewriterLength::kAsIs);
-    }
-    for (const auto& length : lengths) {
-      RunRewriteOptionCombinationFailureTest(
-          tone, blink::mojom::AIRewriterFormat::kAsIs, length);
-    }
-  }
-  for (const auto& format : formats) {
-    for (const auto& length : lengths) {
-      RunRewriteOptionCombinationFailureTest(
-          blink::mojom::AIRewriterTone::kAsIs, format, length);
     }
   }
 }
@@ -536,20 +383,15 @@
                                             SessionConfigParams>&
                                         config_params) {
         auto session = std::make_unique<optimization_guide::MockSession>();
-        EXPECT_CALL(*session, AddContext(_))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata) {
-                  CheckComposeRequestContext(request_metadata,
-                                             kConcatenatedContextString);
-                }));
         EXPECT_CALL(*session, ExecuteModel(_, _))
             .WillOnce(testing::Invoke(
                 [](const google::protobuf::MessageLite& request_metadata,
                    optimization_guide::
                        OptimizationGuideModelExecutionResultStreamingCallback
                            callback) {
-                  CheckComposeRequestRewriteParamsPreviousResponse(
-                      request_metadata, kInputString);
+                  AITestUtils::CheckWritingAssistanceApiRequest(
+                      request_metadata, kSharedContextString, kContextString,
+                      *GetDefaultExpectedOptions(), kInputString);
                   callback.Run(CreateExecutionErrorResult(
                       optimization_guide::OptimizationGuideModelExecutionError::
                           FromModelExecutionError(
@@ -609,22 +451,15 @@
                                             SessionConfigParams>&
                                         config_params) {
         auto session = std::make_unique<optimization_guide::MockSession>();
-
-        EXPECT_CALL(*session, AddContext(_))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata) {
-                  CheckComposeRequestContext(request_metadata,
-                                             kConcatenatedContextString);
-                }));
         EXPECT_CALL(*session, ExecuteModel(_, _))
             .WillOnce(testing::Invoke(
                 [](const google::protobuf::MessageLite& request_metadata,
                    optimization_guide::
                        OptimizationGuideModelExecutionResultStreamingCallback
                            callback) {
-                  CheckComposeRequestRewriteParamsPreviousResponse(
-                      request_metadata, kInputString);
-
+                  AITestUtils::CheckWritingAssistanceApiRequest(
+                      request_metadata, kSharedContextString, kContextString,
+                      *GetDefaultExpectedOptions(), kInputString);
                   callback.Run(
                       CreateExecutionResult("Result ", /*is_complete=*/false));
                   callback.Run(CreateExecutionResult("Result text",
@@ -649,10 +484,7 @@
     mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
     ai_manager->CreateRewriter(
         mock_create_rewriter_client.BindNewPipeAndPassRemote(),
-        blink::mojom::AIRewriterCreateOptions::New(
-            kSharedContextString, blink::mojom::AIRewriterTone::kAsIs,
-            blink::mojom::AIRewriterFormat::kAsIs,
-            blink::mojom::AIRewriterLength::kAsIs));
+        GetDefaultOptions());
     run_loop.Run();
   }
   AITestUtils::MockModelStreamingResponder mock_responder;
@@ -695,26 +527,15 @@
                                             SessionConfigParams>&
                                         config_params) {
         auto session = std::make_unique<optimization_guide::MockSession>();
-
-        EXPECT_CALL(*session, AddContext(_))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata) {
-                  CheckComposeRequestContext(request_metadata,
-                                             kConcatenatedContextString);
-                }))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata) {
-                  CheckComposeRequestContext(
-                      request_metadata, "test shared context\ntest context 2");
-                }));
         EXPECT_CALL(*session, ExecuteModel(_, _))
             .WillOnce(testing::Invoke(
                 [](const google::protobuf::MessageLite& request_metadata,
                    optimization_guide::
                        OptimizationGuideModelExecutionResultStreamingCallback
                            callback) {
-                  CheckComposeRequestRewriteParamsPreviousResponse(
-                      request_metadata, kInputString);
+                  AITestUtils::CheckWritingAssistanceApiRequest(
+                      request_metadata, kSharedContextString, kContextString,
+                      *GetDefaultExpectedOptions(), kInputString);
                   callback.Run(CreateExecutionResult("Result text",
                                                      /*is_complete=*/true));
                 }))
@@ -723,8 +544,9 @@
                    optimization_guide::
                        OptimizationGuideModelExecutionResultStreamingCallback
                            callback) {
-                  CheckComposeRequestRewriteParamsPreviousResponse(
-                      request_metadata, "input string 2");
+                  AITestUtils::CheckWritingAssistanceApiRequest(
+                      request_metadata, kSharedContextString, "test context 2",
+                      *GetDefaultExpectedOptions(), "input string 2");
                   callback.Run(CreateExecutionResult("Result text 2",
                                                      /*is_complete=*/true));
                 }));
@@ -747,10 +569,7 @@
     mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
     ai_manager->CreateRewriter(
         mock_create_rewriter_client.BindNewPipeAndPassRemote(),
-        blink::mojom::AIRewriterCreateOptions::New(
-            kSharedContextString, blink::mojom::AIRewriterTone::kAsIs,
-            blink::mojom::AIRewriterFormat::kAsIs,
-            blink::mojom::AIRewriterLength::kAsIs));
+        GetDefaultOptions());
     run_loop.Run();
   }
   {
@@ -813,21 +632,15 @@
                                             SessionConfigParams>&
                                         config_params) {
         auto session = std::make_unique<optimization_guide::MockSession>();
-
-        EXPECT_CALL(*session, AddContext(_))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata) {
-                  CheckComposeRequestContext(request_metadata,
-                                             kConcatenatedContextString);
-                }));
         EXPECT_CALL(*session, ExecuteModel(_, _))
             .WillOnce(testing::Invoke(
                 [&](const google::protobuf::MessageLite& request_metadata,
                     optimization_guide::
                         OptimizationGuideModelExecutionResultStreamingCallback
                             callback) {
-                  CheckComposeRequestRewriteParamsPreviousResponse(
-                      request_metadata, kInputString);
+                  AITestUtils::CheckWritingAssistanceApiRequest(
+                      request_metadata, kSharedContextString, kContextString,
+                      *GetDefaultExpectedOptions(), kInputString);
                   streaming_callback = std::move(callback);
                   run_loop_for_callback.Quit();
                 }));
@@ -850,10 +663,7 @@
     mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
     ai_manager->CreateRewriter(
         mock_create_rewriter_client.BindNewPipeAndPassRemote(),
-        blink::mojom::AIRewriterCreateOptions::New(
-            kSharedContextString, blink::mojom::AIRewriterTone::kAsIs,
-            blink::mojom::AIRewriterFormat::kAsIs,
-            blink::mojom::AIRewriterLength::kAsIs));
+        GetDefaultOptions());
     run_loop.Run();
   }
   std::unique_ptr<AITestUtils::MockModelStreamingResponder> mock_responder =
@@ -886,21 +696,15 @@
                                             SessionConfigParams>&
                                         config_params) {
         auto session = std::make_unique<optimization_guide::MockSession>();
-
-        EXPECT_CALL(*session, AddContext(_))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata) {
-                  CheckComposeRequestContext(request_metadata,
-                                             kConcatenatedContextString);
-                }));
         EXPECT_CALL(*session, ExecuteModel(_, _))
             .WillOnce(testing::Invoke(
                 [&](const google::protobuf::MessageLite& request_metadata,
                     optimization_guide::
                         OptimizationGuideModelExecutionResultStreamingCallback
                             callback) {
-                  CheckComposeRequestRewriteParamsPreviousResponse(
-                      request_metadata, kInputString);
+                  AITestUtils::CheckWritingAssistanceApiRequest(
+                      request_metadata, kSharedContextString, kContextString,
+                      *GetDefaultExpectedOptions(), kInputString);
                   streaming_callback = std::move(callback);
                   run_loop_for_callback.Quit();
                 }));
@@ -923,10 +727,7 @@
     mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
     ai_manager->CreateRewriter(
         mock_create_rewriter_client.BindNewPipeAndPassRemote(),
-        blink::mojom::AIRewriterCreateOptions::New(
-            kSharedContextString, blink::mojom::AIRewriterTone::kAsIs,
-            blink::mojom::AIRewriterFormat::kAsIs,
-            blink::mojom::AIRewriterLength::kAsIs));
+        GetDefaultOptions());
     run_loop.Run();
   }
 
diff --git a/chrome/browser/ai/ai_test_utils.cc b/chrome/browser/ai/ai_test_utils.cc
index 074c3db..a414792 100644
--- a/chrome/browser/ai/ai_test_utils.cc
+++ b/chrome/browser/ai/ai_test_utils.cc
@@ -110,3 +110,25 @@
   static base::NoDestructor<optimization_guide::proto::Any> data;
   return *data;
 }
+
+// static
+void AITestUtils::CheckWritingAssistanceApiRequest(
+    const google::protobuf::MessageLite& request_metadata,
+    const std::string& expected_shared_context,
+    const std::string& expected_context,
+    const optimization_guide::proto::WritingAssistanceApiOptions&
+        expected_options,
+    const std::string& expected_input) {
+  const optimization_guide::proto::WritingAssistanceApiRequest* request =
+      static_cast<
+          const optimization_guide::proto::WritingAssistanceApiRequest*>(
+          &request_metadata);
+  EXPECT_EQ(request->shared_context(), expected_shared_context);
+  EXPECT_EQ(request->context(), expected_context);
+  EXPECT_EQ(request->options().output_tone(), expected_options.output_tone());
+  EXPECT_EQ(request->options().output_format(),
+            expected_options.output_format());
+  EXPECT_EQ(request->options().output_length(),
+            expected_options.output_length());
+  EXPECT_EQ(request->rewrite_text(), expected_input);
+}
diff --git a/chrome/browser/ai/ai_test_utils.h b/chrome/browser/ai/ai_test_utils.h
index e645fa7..102125a 100644
--- a/chrome/browser/ai/ai_test_utils.h
+++ b/chrome/browser/ai/ai_test_utils.h
@@ -9,6 +9,7 @@
 #include "chrome/browser/ai/ai_manager.h"
 #include "chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "components/optimization_guide/proto/features/writing_assistance_api.pb.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -131,6 +132,14 @@
 
   static const optimization_guide::TokenLimits& GetFakeTokenLimits();
   static const optimization_guide::proto::Any& GetFakeFeatureMetadata();
+
+  static void CheckWritingAssistanceApiRequest(
+      const google::protobuf::MessageLite& request_metadata,
+      const std::string& expected_shared_context,
+      const std::string& expected_context,
+      const optimization_guide::proto::WritingAssistanceApiOptions&
+          expected_options,
+      const std::string& expected_input);
 };
 
 #endif  // CHROME_BROWSER_AI_AI_TEST_UTILS_H_
diff --git a/chrome/browser/ai/ai_writer.cc b/chrome/browser/ai/ai_writer.cc
index 3a9feca5..ebe345ec 100644
--- a/chrome/browser/ai/ai_writer.cc
+++ b/chrome/browser/ai/ai_writer.cc
@@ -10,9 +10,54 @@
 #include "chrome/browser/ai/ai_utils.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
 #include "components/optimization_guide/proto/common_types.pb.h"
-#include "components/optimization_guide/proto/features/compose.pb.h"
 #include "third_party/blink/public/mojom/ai/model_streaming_responder.mojom.h"
 
+namespace {
+
+optimization_guide::proto::WritingAssistanceApiOutputTone ToProtoTone(
+    blink::mojom::AIWriterTone type) {
+  switch (type) {
+    case blink::mojom::AIWriterTone::kFormal:
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_TONE_FORMAL;
+    case blink::mojom::AIWriterTone::kNeutral:
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_TONE_NEUTRAL;
+    case blink::mojom::AIWriterTone::kCasual:
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_TONE_CASUAL;
+  }
+}
+
+optimization_guide::proto::WritingAssistanceApiOutputFormat ToProtoFormat(
+    blink::mojom::AIWriterFormat format) {
+  switch (format) {
+    case blink::mojom::AIWriterFormat::kPlainText:
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_FORMAT_PLAIN_TEXT;
+    case blink::mojom::AIWriterFormat::kMarkdown:
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_FORMAT_MARKDOWN;
+  }
+}
+
+optimization_guide::proto::WritingAssistanceApiOutputLength ToProtoLength(
+    blink::mojom::AIWriterLength length) {
+  switch (length) {
+    case blink::mojom::AIWriterLength::kShort:
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_LENGTH_SHORT;
+    case blink::mojom::AIWriterLength::kMedium:
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_LENGTH_MEDIUM;
+    case blink::mojom::AIWriterLength::kLong:
+      return optimization_guide::proto::
+          WRITING_ASSISTANCE_API_OUTPUT_LENGTH_LONG;
+  }
+}
+
+}  // namespace
+
 AIWriter::AIWriter(
     AIContextBoundObjectSet& context_bound_object_set,
     std::unique_ptr<optimization_guide::OptimizationGuideModelExecutor::Session>
@@ -34,28 +79,30 @@
   }
 }
 
+// static
+std::unique_ptr<optimization_guide::proto::WritingAssistanceApiOptions>
+AIWriter::ToProtoOptions(
+    const blink::mojom::AIWriterCreateOptionsPtr& options) {
+  auto proto_options = std::make_unique<
+      optimization_guide::proto::WritingAssistanceApiOptions>();
+  proto_options->set_output_tone(ToProtoTone(options->tone));
+  proto_options->set_output_format(ToProtoFormat(options->format));
+  proto_options->set_output_length(ToProtoLength(options->length));
+  return proto_options;
+}
+
 void AIWriter::Write(const std::string& input,
                      const std::optional<std::string>& context,
                      mojo::PendingRemote<blink::mojom::ModelStreamingResponder>
                          pending_responder) {
-  optimization_guide::proto::ComposePageMetadata page_metadata;
-  std::string context_string = base::JoinString(
-      {options_->shared_context.value_or(""), context.value_or("")}, "\n");
-  base::TrimString(context_string, "\n", &context_string);
-  page_metadata.set_trimmed_page_inner_text(
-      context_string.substr(0, AIUtils::kTrimmedInnerTextMaxChars));
-  page_metadata.set_page_inner_text(context_string);
-
-  optimization_guide::proto::ComposeRequest context_request;
-  *context_request.mutable_page_metadata() = std::move(page_metadata);
-
-  session_->AddContext(context_request);
-
-  optimization_guide::proto::ComposeRequest execute_request;
-  execute_request.mutable_generate_params()->set_user_input(input);
-
+  optimization_guide::proto::WritingAssistanceApiRequest request;
+  request.set_context(context.value_or(std::string()));
+  request.set_allocated_options(ToProtoOptions(options_).release());
+  request.set_rewrite_text(input);
+  // TODO(crbug.com/390006887): Pass shared context with session creation.
+  request.set_shared_context(options_->shared_context.value_or(std::string()));
   session_->ExecuteModel(
-      execute_request,
+      request,
       base::BindRepeating(&AIWriter::ModelExecutionCallback,
                           weak_ptr_factory_.GetWeakPtr(),
                           responder_set_.Add(std::move(pending_responder))));
@@ -75,11 +122,12 @@
     return;
   }
 
-  auto compose_response = optimization_guide::ParsedAnyMetadata<
-      optimization_guide::proto::ComposeResponse>(result.response->response);
-  if (compose_response) {
+  auto response = optimization_guide::ParsedAnyMetadata<
+      optimization_guide::proto::WritingAssistanceApiResponse>(
+      result.response->response);
+  if (response) {
     responder->OnStreaming(
-        compose_response->output(),
+        response->output(),
         blink::mojom::ModelStreamingResponderAction::kReplace);
   }
   if (result.response->is_complete) {
diff --git a/chrome/browser/ai/ai_writer.h b/chrome/browser/ai/ai_writer.h
index bf3c6624..6766d4ef 100644
--- a/chrome/browser/ai/ai_writer.h
+++ b/chrome/browser/ai/ai_writer.h
@@ -10,6 +10,7 @@
 
 #include "chrome/browser/ai/ai_context_bound_object.h"
 #include "components/optimization_guide/core/optimization_guide_model_executor.h"
+#include "components/optimization_guide/proto/features/writing_assistance_api.pb.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
@@ -28,9 +29,11 @@
       mojo::PendingReceiver<blink::mojom::AIWriter> receiver);
   AIWriter(const AIWriter&) = delete;
   AIWriter& operator=(const AIWriter&) = delete;
-
   ~AIWriter() override;
 
+  static std::unique_ptr<optimization_guide::proto::WritingAssistanceApiOptions>
+  ToProtoOptions(const blink::mojom::AIWriterCreateOptionsPtr& options);
+
   // `blink::mojom::AIWriter` implementation.
   void Write(const std::string& input,
              const std::optional<std::string>& context,
diff --git a/chrome/browser/ai/ai_writer_unittest.cc b/chrome/browser/ai/ai_writer_unittest.cc
index 5c9a8192..e4c31d4 100644
--- a/chrome/browser/ai/ai_writer_unittest.cc
+++ b/chrome/browser/ai/ai_writer_unittest.cc
@@ -14,6 +14,7 @@
 #include "components/optimization_guide/core/optimization_guide_proto_util.h"
 #include "components/optimization_guide/core/optimization_guide_switches.h"
 #include "components/optimization_guide/core/optimization_guide_util.h"
+#include "components/optimization_guide/proto/features/writing_assistance_api.pb.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/ai/ai_manager.mojom.h"
@@ -25,8 +26,6 @@
 
 constexpr char kSharedContextString[] = "test shared context";
 constexpr char kContextString[] = "test context";
-constexpr char kConcatenatedContextString[] =
-    "test shared context\ntest context";
 constexpr char kInputString[] = "input string";
 
 class MockCreateWriterClient
@@ -53,7 +52,7 @@
 
 optimization_guide::OptimizationGuideModelStreamingExecutionResult
 CreateExecutionResult(std::string_view output, bool is_complete) {
-  optimization_guide::proto::ComposeResponse response;
+  optimization_guide::proto::WritingAssistanceApiResponse response;
   *response.mutable_output() = output;
   return optimization_guide::OptimizationGuideModelStreamingExecutionResult(
       optimization_guide::StreamingResponse{
@@ -71,30 +70,93 @@
       /*provided_by_on_device=*/true);
 }
 
-void CheckComposeRequestContext(
-    const google::protobuf::MessageLite& request_metadata,
-    const std::string& expected_context_string) {
-  const optimization_guide::proto::ComposeRequest* request =
-      static_cast<const optimization_guide::proto::ComposeRequest*>(
-          &request_metadata);
-  EXPECT_THAT(request->page_metadata().page_inner_text(),
-              expected_context_string);
-  EXPECT_THAT(request->page_metadata().trimmed_page_inner_text(),
-              expected_context_string);
+blink::mojom::AIWriterCreateOptionsPtr GetDefaultOptions() {
+  return blink::mojom::AIWriterCreateOptions::New(
+      kSharedContextString, blink::mojom::AIWriterTone::kNeutral,
+      blink::mojom::AIWriterFormat::kPlainText,
+      blink::mojom::AIWriterLength::kMedium);
 }
 
-void CheckComposeRequestUserInput(
-    const google::protobuf::MessageLite& request_metadata,
-    const std::string& expected_user_input) {
-  const optimization_guide::proto::ComposeRequest* request =
-      static_cast<const optimization_guide::proto::ComposeRequest*>(
-          &request_metadata);
-  EXPECT_THAT(request->generate_params().user_input(), expected_user_input);
+std::unique_ptr<optimization_guide::proto::WritingAssistanceApiOptions>
+GetDefaultExpectedOptions() {
+  return AIWriter::ToProtoOptions(GetDefaultOptions());
 }
 
 }  // namespace
 
-class AIWriterTest : public AITestUtils::AITestBase {};
+class AIWriterTest : public AITestUtils::AITestBase {
+ protected:
+  void RunSimpleWriteTest(blink::mojom::AIWriterTone tone,
+                          blink::mojom::AIWriterFormat format,
+                          blink::mojom::AIWriterLength length) {
+    const auto options = blink::mojom::AIWriterCreateOptions::New(
+        kSharedContextString, tone, format, length);
+
+    EXPECT_CALL(*mock_optimization_guide_keyed_service_, StartSession(_, _))
+        .WillOnce(testing::Invoke([&](optimization_guide::
+                                          ModelBasedCapabilityKey feature,
+                                      const std::optional<
+                                          optimization_guide::
+                                              SessionConfigParams>&
+                                          config_params) {
+          auto session = std::make_unique<optimization_guide::MockSession>();
+          EXPECT_CALL(*session, ExecuteModel(_, _))
+              .WillOnce(testing::Invoke(
+                  [&](const google::protobuf::MessageLite& request_metadata,
+                      optimization_guide::
+                          OptimizationGuideModelExecutionResultStreamingCallback
+                              callback) {
+                    AITestUtils::CheckWritingAssistanceApiRequest(
+                        request_metadata, kSharedContextString, kContextString,
+                        *AIWriter::ToProtoOptions(options), kInputString);
+                    callback.Run(CreateExecutionResult("Result text",
+                                                       /*is_complete=*/true));
+                  }));
+          return session;
+        }));
+
+    mojo::Remote<blink::mojom::AIWriter> writer_remote;
+    {
+      MockCreateWriterClient mock_create_writer_client;
+      base::RunLoop run_loop;
+      EXPECT_CALL(mock_create_writer_client, OnResult(_))
+          .WillOnce(testing::Invoke(
+              [&](mojo::PendingRemote<::blink::mojom::AIWriter> writer) {
+                EXPECT_TRUE(writer);
+                writer_remote =
+                    mojo::Remote<blink::mojom::AIWriter>(std::move(writer));
+                run_loop.Quit();
+              }));
+
+      mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
+      ai_manager->CreateWriter(
+          mock_create_writer_client.BindNewPipeAndPassRemote(),
+          options.Clone());
+      run_loop.Run();
+    }
+    AITestUtils::MockModelStreamingResponder mock_responder;
+
+    base::RunLoop run_loop;
+    EXPECT_CALL(mock_responder, OnStreaming(_, _))
+        .WillOnce(testing::Invoke(
+            [&](const std::string& text,
+                blink::mojom::ModelStreamingResponderAction action) {
+              EXPECT_THAT(text, "Result text");
+              EXPECT_EQ(action,
+                        blink::mojom::ModelStreamingResponderAction::kReplace);
+            }));
+
+    EXPECT_CALL(mock_responder, OnCompletion(_))
+        .WillOnce(testing::Invoke(
+            [&](blink::mojom::ModelExecutionContextInfoPtr context_info) {
+              run_loop.Quit();
+            }));
+
+    writer_remote->Write(kInputString, kContextString,
+                         mock_responder.BindNewPipeAndPassRemote());
+    run_loop.Run();
+  }
+};
 
 TEST_F(AIWriterTest, CreateWriterNoService) {
   SetupNullOptimizationGuideKeyedService();
@@ -109,12 +171,8 @@
           }));
 
   mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
-  ai_manager->CreateWriter(
-      mock_create_writer_client.BindNewPipeAndPassRemote(),
-      blink::mojom::AIWriterCreateOptions::New(
-          kSharedContextString, blink::mojom::AIWriterTone::kNeutral,
-          blink::mojom::AIWriterFormat::kPlainText,
-          blink::mojom::AIWriterLength::kMedium));
+  ai_manager->CreateWriter(mock_create_writer_client.BindNewPipeAndPassRemote(),
+                           GetDefaultOptions());
   run_loop.Run();
 }
 
@@ -143,12 +201,8 @@
           }));
 
   mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
-  ai_manager->CreateWriter(
-      mock_create_writer_client.BindNewPipeAndPassRemote(),
-      blink::mojom::AIWriterCreateOptions::New(
-          kSharedContextString, blink::mojom::AIWriterTone::kNeutral,
-          blink::mojom::AIWriterFormat::kPlainText,
-          blink::mojom::AIWriterLength::kMedium));
+  ai_manager->CreateWriter(mock_create_writer_client.BindNewPipeAndPassRemote(),
+                           GetDefaultOptions());
   run_loop.Run();
 }
 
@@ -205,24 +259,20 @@
           }));
 
   mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
-  ai_manager->CreateWriter(
-      mock_create_writer_client.BindNewPipeAndPassRemote(),
-      blink::mojom::AIWriterCreateOptions::New(
-          kSharedContextString, blink::mojom::AIWriterTone::kNeutral,
-          blink::mojom::AIWriterFormat::kPlainText,
-          blink::mojom::AIWriterLength::kMedium));
+  ai_manager->CreateWriter(mock_create_writer_client.BindNewPipeAndPassRemote(),
+                           GetDefaultOptions());
 
   run_loop_for_add_observer.Run();
   CHECK(availability_observer);
   // Send `kConfigNotAvailableForFeature` first to the observer.
   availability_observer->OnDeviceModelAvailabilityChanged(
-      optimization_guide::ModelBasedCapabilityKey::kCompose,
+      optimization_guide::ModelBasedCapabilityKey::kWritingAssistanceApi,
       optimization_guide::OnDeviceModelEligibilityReason::
           kConfigNotAvailableForFeature);
 
   // And then send `kConfigNotAvailableForFeature` to the observer.
   availability_observer->OnDeviceModelAvailabilityChanged(
-      optimization_guide::ModelBasedCapabilityKey::kCompose,
+      optimization_guide::ModelBasedCapabilityKey::kWritingAssistanceApi,
       optimization_guide::OnDeviceModelEligibilityReason::kSuccess);
 
   // OnResult() should be called.
@@ -272,10 +322,7 @@
   mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
   ai_manager->CreateWriter(
       mock_create_writer_client->BindNewPipeAndPassRemote(),
-      blink::mojom::AIWriterCreateOptions::New(
-          kSharedContextString, blink::mojom::AIWriterTone::kNeutral,
-          blink::mojom::AIWriterFormat::kPlainText,
-          blink::mojom::AIWriterLength::kMedium));
+      GetDefaultOptions());
 
   run_loop_for_add_observer.Run();
   CHECK(availability_observer);
@@ -287,80 +334,38 @@
   run_loop_for_remove_observer.Run();
 }
 
-TEST_F(AIWriterTest, SimpleWrite) {
+TEST_F(AIWriterTest, WriteDefault) {
   SetupMockOptimizationGuideKeyedService();
-  EXPECT_CALL(*mock_optimization_guide_keyed_service_, StartSession(_, _))
-      .WillOnce(testing::Invoke([&](optimization_guide::ModelBasedCapabilityKey
-                                        feature,
-                                    const std::optional<
-                                        optimization_guide::
-                                            SessionConfigParams>&
-                                        config_params) {
-        EXPECT_EQ(feature,
-                  optimization_guide::ModelBasedCapabilityKey::kCompose);
-        auto session = std::make_unique<optimization_guide::MockSession>();
-        EXPECT_CALL(*session, AddContext(_))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata) {
-                  CheckComposeRequestContext(request_metadata,
-                                             kConcatenatedContextString);
-                }));
-        EXPECT_CALL(*session, ExecuteModel(_, _))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata,
-                   optimization_guide::
-                       OptimizationGuideModelExecutionResultStreamingCallback
-                           callback) {
-                  CheckComposeRequestUserInput(request_metadata, kInputString);
-                  callback.Run(CreateExecutionResult("Result text",
-                                                     /*is_complete=*/true));
-                }));
-        return session;
-      }));
+  RunSimpleWriteTest(blink::mojom::AIWriterTone::kNeutral,
+                     blink::mojom::AIWriterFormat::kPlainText,
+                     blink::mojom::AIWriterLength::kMedium);
+}
 
-  mojo::Remote<blink::mojom::AIWriter> writer_remote;
-  {
-    MockCreateWriterClient mock_create_writer_client;
-    base::RunLoop run_loop;
-    EXPECT_CALL(mock_create_writer_client, OnResult(_))
-        .WillOnce(testing::Invoke(
-            [&](mojo::PendingRemote<::blink::mojom::AIWriter> writer) {
-              EXPECT_TRUE(writer);
-              writer_remote =
-                  mojo::Remote<blink::mojom::AIWriter>(std::move(writer));
-              run_loop.Quit();
-            }));
-
-    mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
-    ai_manager->CreateWriter(
-        mock_create_writer_client.BindNewPipeAndPassRemote(),
-        blink::mojom::AIWriterCreateOptions::New(
-            kSharedContextString, blink::mojom::AIWriterTone::kNeutral,
-            blink::mojom::AIWriterFormat::kPlainText,
-            blink::mojom::AIWriterLength::kMedium));
-    run_loop.Run();
+TEST_F(AIWriterTest, WriteWithOptions) {
+  SetupMockOptimizationGuideKeyedService();
+  blink::mojom::AIWriterTone tones[]{
+      blink::mojom::AIWriterTone::kFormal,
+      blink::mojom::AIWriterTone::kNeutral,
+      blink::mojom::AIWriterTone::kCasual,
+  };
+  blink::mojom::AIWriterFormat formats[]{
+      blink::mojom::AIWriterFormat::kPlainText,
+      blink::mojom::AIWriterFormat::kMarkdown,
+  };
+  blink::mojom::AIWriterLength lengths[]{
+      blink::mojom::AIWriterLength::kShort,
+      blink::mojom::AIWriterLength::kMedium,
+      blink::mojom::AIWriterLength::kLong,
+  };
+  for (const auto& tone : tones) {
+    for (const auto& format : formats) {
+      for (const auto& length : lengths) {
+        SCOPED_TRACE(testing::Message()
+                     << tone << " " << format << " " << length);
+        RunSimpleWriteTest(tone, format, length);
+      }
+    }
   }
-  AITestUtils::MockModelStreamingResponder mock_responder;
-
-  base::RunLoop run_loop;
-  EXPECT_CALL(mock_responder, OnStreaming(_, _))
-      .WillOnce(testing::Invoke(
-          [&](const std::string& text,
-              blink::mojom::ModelStreamingResponderAction action) {
-            EXPECT_THAT(text, "Result text");
-            EXPECT_EQ(action,
-                      blink::mojom::ModelStreamingResponderAction::kReplace);
-          }));
-
-  EXPECT_CALL(mock_responder, OnCompletion(_))
-      .WillOnce(testing::Invoke(
-          [&](blink::mojom::ModelExecutionContextInfoPtr context_info) {
-            run_loop.Quit();
-          }));
-
-  writer_remote->Write(kInputString, kContextString,
-                       mock_responder.BindNewPipeAndPassRemote());
-  run_loop.Run();
 }
 
 TEST_F(AIWriterTest, WriteError) {
@@ -374,19 +379,15 @@
                                         config_params) {
         auto session = std::make_unique<optimization_guide::MockSession>();
 
-        EXPECT_CALL(*session, AddContext(_))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata) {
-                  CheckComposeRequestContext(request_metadata,
-                                             kConcatenatedContextString);
-                }));
         EXPECT_CALL(*session, ExecuteModel(_, _))
             .WillOnce(testing::Invoke(
                 [](const google::protobuf::MessageLite& request_metadata,
                    optimization_guide::
                        OptimizationGuideModelExecutionResultStreamingCallback
                            callback) {
-                  CheckComposeRequestUserInput(request_metadata, kInputString);
+                  AITestUtils::CheckWritingAssistanceApiRequest(
+                      request_metadata, kSharedContextString, kContextString,
+                      *GetDefaultExpectedOptions(), kInputString);
                   callback.Run(CreateExecutionErrorResult(
                       optimization_guide::OptimizationGuideModelExecutionError::
                           FromModelExecutionError(
@@ -413,10 +414,7 @@
     mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
     ai_manager->CreateWriter(
         mock_create_writer_client.BindNewPipeAndPassRemote(),
-        blink::mojom::AIWriterCreateOptions::New(
-            kSharedContextString, blink::mojom::AIWriterTone::kNeutral,
-            blink::mojom::AIWriterFormat::kPlainText,
-            blink::mojom::AIWriterLength::kMedium));
+        GetDefaultOptions());
     run_loop.Run();
   }
   AITestUtils::MockModelStreamingResponder mock_responder;
@@ -447,19 +445,15 @@
                                         config_params) {
         auto session = std::make_unique<optimization_guide::MockSession>();
 
-        EXPECT_CALL(*session, AddContext(_))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata) {
-                  CheckComposeRequestContext(request_metadata,
-                                             kConcatenatedContextString);
-                }));
         EXPECT_CALL(*session, ExecuteModel(_, _))
             .WillOnce(testing::Invoke(
                 [](const google::protobuf::MessageLite& request_metadata,
                    optimization_guide::
                        OptimizationGuideModelExecutionResultStreamingCallback
                            callback) {
-                  CheckComposeRequestUserInput(request_metadata, kInputString);
+                  AITestUtils::CheckWritingAssistanceApiRequest(
+                      request_metadata, kSharedContextString, kContextString,
+                      *GetDefaultExpectedOptions(), kInputString);
 
                   callback.Run(
                       CreateExecutionResult("Result ", /*is_complete=*/false));
@@ -485,10 +479,7 @@
     mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
     ai_manager->CreateWriter(
         mock_create_writer_client.BindNewPipeAndPassRemote(),
-        blink::mojom::AIWriterCreateOptions::New(
-            kSharedContextString, blink::mojom::AIWriterTone::kNeutral,
-            blink::mojom::AIWriterFormat::kPlainText,
-            blink::mojom::AIWriterLength::kMedium));
+        GetDefaultOptions());
     run_loop.Run();
   }
   AITestUtils::MockModelStreamingResponder mock_responder;
@@ -532,24 +523,15 @@
                                         config_params) {
         auto session = std::make_unique<optimization_guide::MockSession>();
 
-        EXPECT_CALL(*session, AddContext(_))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata) {
-                  CheckComposeRequestContext(request_metadata,
-                                             kConcatenatedContextString);
-                }))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata) {
-                  CheckComposeRequestContext(
-                      request_metadata, "test shared context\ntest context 2");
-                }));
         EXPECT_CALL(*session, ExecuteModel(_, _))
             .WillOnce(testing::Invoke(
                 [](const google::protobuf::MessageLite& request_metadata,
                    optimization_guide::
                        OptimizationGuideModelExecutionResultStreamingCallback
                            callback) {
-                  CheckComposeRequestUserInput(request_metadata, kInputString);
+                  AITestUtils::CheckWritingAssistanceApiRequest(
+                      request_metadata, kSharedContextString, kContextString,
+                      *GetDefaultExpectedOptions(), kInputString);
                   callback.Run(CreateExecutionResult("Result text",
                                                      /*is_complete=*/true));
                 }))
@@ -558,8 +540,9 @@
                    optimization_guide::
                        OptimizationGuideModelExecutionResultStreamingCallback
                            callback) {
-                  CheckComposeRequestUserInput(request_metadata,
-                                               "input string 2");
+                  AITestUtils::CheckWritingAssistanceApiRequest(
+                      request_metadata, kSharedContextString, "test context 2",
+                      *GetDefaultExpectedOptions(), "input string 2");
                   callback.Run(CreateExecutionResult("Result text 2",
                                                      /*is_complete=*/true));
                 }));
@@ -582,10 +565,7 @@
     mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
     ai_manager->CreateWriter(
         mock_create_writer_client.BindNewPipeAndPassRemote(),
-        blink::mojom::AIWriterCreateOptions::New(
-            kSharedContextString, blink::mojom::AIWriterTone::kNeutral,
-            blink::mojom::AIWriterFormat::kPlainText,
-            blink::mojom::AIWriterLength::kMedium));
+        GetDefaultOptions());
     run_loop.Run();
   }
   {
@@ -648,20 +628,15 @@
                                             SessionConfigParams>&
                                         config_params) {
         auto session = std::make_unique<optimization_guide::MockSession>();
-
-        EXPECT_CALL(*session, AddContext(_))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata) {
-                  CheckComposeRequestContext(request_metadata,
-                                             kConcatenatedContextString);
-                }));
         EXPECT_CALL(*session, ExecuteModel(_, _))
             .WillOnce(testing::Invoke(
                 [&](const google::protobuf::MessageLite& request_metadata,
                     optimization_guide::
                         OptimizationGuideModelExecutionResultStreamingCallback
                             callback) {
-                  CheckComposeRequestUserInput(request_metadata, kInputString);
+                  AITestUtils::CheckWritingAssistanceApiRequest(
+                      request_metadata, kSharedContextString, kContextString,
+                      *GetDefaultExpectedOptions(), kInputString);
                   streaming_callback = std::move(callback);
                   run_loop_for_callback.Quit();
                 }));
@@ -684,10 +659,7 @@
     mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
     ai_manager->CreateWriter(
         mock_create_writer_client.BindNewPipeAndPassRemote(),
-        blink::mojom::AIWriterCreateOptions::New(
-            kSharedContextString, blink::mojom::AIWriterTone::kNeutral,
-            blink::mojom::AIWriterFormat::kPlainText,
-            blink::mojom::AIWriterLength::kMedium));
+        GetDefaultOptions());
     run_loop.Run();
   }
   std::unique_ptr<AITestUtils::MockModelStreamingResponder> mock_responder =
@@ -720,20 +692,15 @@
                                             SessionConfigParams>&
                                         config_params) {
         auto session = std::make_unique<optimization_guide::MockSession>();
-
-        EXPECT_CALL(*session, AddContext(_))
-            .WillOnce(testing::Invoke(
-                [](const google::protobuf::MessageLite& request_metadata) {
-                  CheckComposeRequestContext(request_metadata,
-                                             kConcatenatedContextString);
-                }));
         EXPECT_CALL(*session, ExecuteModel(_, _))
             .WillOnce(testing::Invoke(
                 [&](const google::protobuf::MessageLite& request_metadata,
                     optimization_guide::
                         OptimizationGuideModelExecutionResultStreamingCallback
                             callback) {
-                  CheckComposeRequestUserInput(request_metadata, kInputString);
+                  AITestUtils::CheckWritingAssistanceApiRequest(
+                      request_metadata, kSharedContextString, kContextString,
+                      *GetDefaultExpectedOptions(), kInputString);
                   streaming_callback = std::move(callback);
                   run_loop_for_callback.Quit();
                 }));
@@ -756,10 +723,7 @@
     mojo::Remote<blink::mojom::AIManager> ai_manager = GetAIManagerRemote();
     ai_manager->CreateWriter(
         mock_create_writer_client.BindNewPipeAndPassRemote(),
-        blink::mojom::AIWriterCreateOptions::New(
-            kSharedContextString, blink::mojom::AIWriterTone::kNeutral,
-            blink::mojom::AIWriterFormat::kPlainText,
-            blink::mojom::AIWriterLength::kMedium));
+        GetDefaultOptions());
     run_loop.Run();
   }
 
diff --git a/chrome/browser/ash/apps/apk_web_app_service.cc b/chrome/browser/ash/apps/apk_web_app_service.cc
index a36a6f0..6d17042 100644
--- a/chrome/browser/ash/apps/apk_web_app_service.cc
+++ b/chrome/browser/ash/apps/apk_web_app_service.cc
@@ -221,15 +221,10 @@
   if (!web_app_provider) {
     return std::nullopt;
   }
-  // TODO(crbug.com/340952100): Evaluate call sites of FindBestAppWithUrlInScope
-  // for correctness.
+  // Which capability check (if any) would fit best here?
   std::optional<webapps::AppId> app_id =
       web_app_provider->registrar_unsafe().FindBestAppWithUrlInScope(
-          url,
-          {
-              web_app::proto::InstallState::INSTALLED_WITH_OS_INTEGRATION,
-              web_app::proto::InstallState::INSTALLED_WITHOUT_OS_INTEGRATION,
-          });
+          url, web_app::WebAppFilter::InstalledInChrome());
   if (!app_id) {
     return std::nullopt;
   }
diff --git a/chrome/browser/ash/arc/extensions/arc_support_message_host.cc b/chrome/browser/ash/arc/extensions/arc_support_message_host.cc
index b81a569b..03205b3 100644
--- a/chrome/browser/ash/arc/extensions/arc_support_message_host.cc
+++ b/chrome/browser/ash/arc/extensions/arc_support_message_host.cc
@@ -15,13 +15,6 @@
 namespace arc {
 
 // static
-const char ArcSupportMessageHost::kHostName[] = "com.google.arc_support";
-
-// static
-const char* const ArcSupportMessageHost::kHostOrigin[] = {
-    "chrome-extension://cnbgggchhmkkdmeppjobngjoejnihlei/"};
-
-// static
 std::unique_ptr<extensions::NativeMessageHost> ArcSupportMessageHost::Create(
     content::BrowserContext* browser_context) {
   return std::unique_ptr<NativeMessageHost>(new ArcSupportMessageHost());
diff --git a/chrome/browser/ash/arc/extensions/arc_support_message_host.h b/chrome/browser/ash/arc/extensions/arc_support_message_host.h
index 519b796f..b6f47983 100644
--- a/chrome/browser/ash/arc/extensions/arc_support_message_host.h
+++ b/chrome/browser/ash/arc/extensions/arc_support_message_host.h
@@ -32,8 +32,9 @@
   ArcSupportMessageHost(const ArcSupportMessageHost&) = delete;
   ArcSupportMessageHost& operator=(const ArcSupportMessageHost&) = delete;
 
-  static const char kHostName[];
-  static const char* const kHostOrigin[];
+  static constexpr char kHostName[] = "com.google.arc_support";
+  static constexpr const char* kHostOrigin[] = {
+      "chrome-extension://cnbgggchhmkkdmeppjobngjoejnihlei/"};
 
   // Called when the arc_support connects the "port". Returns the
   // instance of ArcSupportMessageHost.
diff --git a/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc b/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
index 53bc015b..0533f59 100644
--- a/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
+++ b/chrome/browser/ash/arc/session/arc_session_manager_unittest.cc
@@ -409,6 +409,7 @@
 
   void TearDown() override {
     resourced_client_ = nullptr;
+    ash::DlcserviceClient::Shutdown();
     ash::ResourcedClient::Shutdown();
     ArcSessionManagerTestBase::TearDown();
   }
diff --git a/chrome/browser/ash/guest_os/vm_sk_forwarding_native_message_host.cc b/chrome/browser/ash/guest_os/vm_sk_forwarding_native_message_host.cc
index 7fc9ce9..64a58b93 100644
--- a/chrome/browser/ash/guest_os/vm_sk_forwarding_native_message_host.cc
+++ b/chrome/browser/ash/guest_os/vm_sk_forwarding_native_message_host.cc
@@ -36,25 +36,12 @@
 namespace guest_os {
 
 // static
-const char* const VmSKForwardingNativeMessageHost::kHostName =
-    "com.google.vm_sk_forwarding";
-
-// static
-const char* const VmSKForwardingNativeMessageHost::kOrigins[] = {
-    "chrome-extension://lehkgnicackihfeppclgiffgbgbhmbdp/",
-    "chrome-extension://lcooaekmckohjjnpaaokodoepajbnill/"};
-
-// static
 const char* const
     VmSKForwardingNativeMessageHost::kHostCreatedByExtensionNotSupportedError =
         "{\"error\":\"Communication initiated by extension is not "
         "supported.\"}";
 
 // static
-const size_t VmSKForwardingNativeMessageHost::kOriginCount =
-    std::size(kOrigins);
-
-// static
 std::unique_ptr<extensions::NativeMessageHost>
 VmSKForwardingNativeMessageHost::CreateFromExtension(
     content::BrowserContext* browser_context) {
diff --git a/chrome/browser/ash/guest_os/vm_sk_forwarding_native_message_host.h b/chrome/browser/ash/guest_os/vm_sk_forwarding_native_message_host.h
index 115e821e..2869ee1 100644
--- a/chrome/browser/ash/guest_os/vm_sk_forwarding_native_message_host.h
+++ b/chrome/browser/ash/guest_os/vm_sk_forwarding_native_message_host.h
@@ -35,10 +35,11 @@
 // opened and closes the channel once a response is received.
 class VmSKForwardingNativeMessageHost : public extensions::NativeMessageHost {
  public:
-  static const char* const kHostName;
-  static const char* const kOrigins[];
+  static constexpr char kHostName[] = "com.google.vm_sk_forwarding";
+  static constexpr const char* kOrigins[] = {
+      "chrome-extension://lehkgnicackihfeppclgiffgbgbhmbdp/",
+      "chrome-extension://lcooaekmckohjjnpaaokodoepajbnill/"};
   static const char* const kHostCreatedByExtensionNotSupportedError;
-  static const size_t kOriginCount;
 
   using ResponseCallback =
       base::OnceCallback<void(const std::string& response)>;
diff --git a/chrome/browser/ash/mahi/web_contents/mahi_web_contents_manager_impl.cc b/chrome/browser/ash/mahi/web_contents/mahi_web_contents_manager_impl.cc
index c91116a..d7af805f 100644
--- a/chrome/browser/ash/mahi/web_contents/mahi_web_contents_manager_impl.cc
+++ b/chrome/browser/ash/mahi/web_contents/mahi_web_contents_manager_impl.cc
@@ -111,7 +111,7 @@
   // Pick the plugin frame host if `contents` is a PDF viewer guest. If using
   // OOPIF PDF viewer, pick the PDF extension frame host.
   content::RenderFrameHost* full_page_pdf_embedder_host =
-      base::FeatureList::IsEnabled(chrome_pdf::features::kPdfOopif)
+      chrome_pdf::features::IsOopifPdfEnabled()
           ? pdf_frame_util::FindFullPagePdfExtensionHost(contents)
           : printing::GetFullPagePlugin(contents);
   content::RenderFrameHost* pdf_rfh = pdf_frame_util::FindPdfChildFrame(
@@ -418,7 +418,7 @@
   // If OOPIF PDF is enabled, we need to observe the focused web contents for
   // a11y changes. Otherwise, we need to observe the inner web contents.
   content::WebContents* web_contents_to_observe = focused_web_contents_;
-  if (!base::FeatureList::IsEnabled(chrome_pdf::features::kPdfOopif)) {
+  if (!chrome_pdf::features::IsOopifPdfEnabled()) {
     std::vector<content::WebContents*> inner_contents =
         focused_web_contents_ ? focused_web_contents_->GetInnerWebContents()
                               : std::vector<content::WebContents*>();
diff --git a/chrome/browser/ash/system_web_apps/system_web_app_manager.cc b/chrome/browser/ash/system_web_apps/system_web_app_manager.cc
index 05a886aa..96f1acac 100644
--- a/chrome/browser/ash/system_web_apps/system_web_app_manager.cc
+++ b/chrome/browser/ash/system_web_apps/system_web_app_manager.cc
@@ -546,15 +546,9 @@
     return std::nullopt;
   }
 
-  // TODO(crbug.com/379827962): Evaluate call sites of FindBestAppWithUrlInScope
-  // for correctness.
   std::optional<webapps::AppId> app_id =
       provider_->registrar_unsafe().FindBestAppWithUrlInScope(
-          url,
-          {
-              web_app::proto::InstallState::INSTALLED_WITH_OS_INTEGRATION,
-              web_app::proto::InstallState::INSTALLED_WITHOUT_OS_INTEGRATION,
-          });
+          url, web_app::WebAppFilter::InstalledInChrome());
   if (!app_id.has_value()) {
     return std::nullopt;
   }
diff --git a/chrome/browser/autocomplete/enterprise_search_aggregator_suggestions_service_factory.cc b/chrome/browser/autocomplete/enterprise_search_aggregator_suggestions_service_factory.cc
index 68ee786..e615bd5 100644
--- a/chrome/browser/autocomplete/enterprise_search_aggregator_suggestions_service_factory.cc
+++ b/chrome/browser/autocomplete/enterprise_search_aggregator_suggestions_service_factory.cc
@@ -9,6 +9,7 @@
 #include "base/no_destructor.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_selections.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
 #include "components/omnibox/browser/enterprise_search_aggregator_suggestions_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
@@ -36,9 +37,11 @@
         content::BrowserContext* context) const {
   Profile* profile = Profile::FromBrowserContext(context);
 
+  signin::IdentityManager* identity_manager =
+      IdentityManagerFactory::GetForProfile(profile);
   return std::make_unique<EnterpriseSearchAggregatorSuggestionsService>(
-      profile->GetDefaultStoragePartition()
-          ->GetURLLoaderFactoryForBrowserProcess());
+      identity_manager, profile->GetDefaultStoragePartition()
+                            ->GetURLLoaderFactoryForBrowserProcess());
 }
 
 EnterpriseSearchAggregatorSuggestionsServiceFactory::
@@ -51,7 +54,9 @@
               // TODO(crbug.com/41488885): Check if this service is needed for
               //   Ash Internals.
               .WithAshInternals(ProfileSelection::kOriginalOnly)
-              .Build()) {}
+              .Build()) {
+  DependsOn(IdentityManagerFactory::GetInstance());
+}
 
 EnterpriseSearchAggregatorSuggestionsServiceFactory::
     ~EnterpriseSearchAggregatorSuggestionsServiceFactory() = default;
diff --git a/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.cc b/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.cc
index ceb0ec0..5d2da15 100644
--- a/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.cc
+++ b/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.cc
@@ -21,9 +21,13 @@
 
 namespace {
 constexpr auto kReservedGroupIdMap =
-    base::MakeFixedFlatMap<int, omnibox::GroupId>(
+    base::MakeFixedFlatMap<size_t, omnibox::GroupId>(
         {{0, omnibox::GROUP_UNSCOPED_EXTENSION_1},
          {1, omnibox::GROUP_UNSCOPED_EXTENSION_2}});
+constexpr auto kReservedSectionMap =
+    base::MakeFixedFlatMap<size_t, omnibox::GroupSection>(
+        {{0, omnibox::SECTION_UNSCOPED_EXTENSION_1},
+         {1, omnibox::SECTION_UNSCOPED_EXTENSION_2}});
 }  // namespace
 
 UnscopedExtensionProviderDelegateImpl::UnscopedExtensionProviderDelegateImpl(
@@ -47,8 +51,9 @@
     const AutocompleteInput& input,
     bool minimal_changes,
     std::set<std::string> unscoped_mode_extension_ids) {
-  // Reset last suggest matches.
-  extension_suggest_matches_.clear();
+  CHECK(extension_suggest_matches_.empty());
+  CHECK(extension_id_to_group_id_map_.empty());
+
   provider_->set_done(false);
 
   for (const std::string& extension_id : unscoped_mode_extension_ids) {
@@ -58,10 +63,11 @@
   }
 }
 
-void UnscopedExtensionProviderDelegateImpl::IncrementRequestId() {
+void UnscopedExtensionProviderDelegateImpl::Stop(bool clear_cached_results) {
   current_request_id_++;
-  // Reset suggestions grouping map any time a new request is made.
-  ResetSuggestionGroupsMap();
+  if (clear_cached_results) {
+    ClearSuggestions();
+  }
 }
 
 void UnscopedExtensionProviderDelegateImpl::OnOmniboxSuggestionsReady(
@@ -69,8 +75,8 @@
     const std::string& extension_id) {
   CHECK(suggestions);
 
+  // Discard suggestions with a stale request ID.
   if (suggestions->request_id != current_request_id_) {
-    // This is an old result, so just ignore.
     return;
   }
 
@@ -78,7 +84,7 @@
   const TemplateURL* template_url = turl_service->FindTemplateURLForExtension(
       extension_id, TemplateURL::OMNIBOX_API_EXTENSION);
 
-  if (!base::Contains(extension_id_group_map_, extension_id)) {
+  if (!base::Contains(extension_id_to_group_id_map_, extension_id)) {
     if (next_available_group_index_ == kReservedGroupIdMap.size()) {
       // Reached max number of groups that can be assigned to an extension.
       // Discard suggestions from this extension.
@@ -88,11 +94,19 @@
     // next available groupId, and give the group the corresponding header for
     // the extension. If the max number of extensions have been assigned a
     // header, don't assign headers to further extensions.
-    omnibox::GroupId current_group =
+    const omnibox::GroupId current_group_id =
         kReservedGroupIdMap.at(next_available_group_index_++);
-    provider_->AddToSuggestionGroupsMap(
-        current_group, base::UTF16ToUTF8(template_url->keyword()));
-    extension_id_group_map_[extension_id] = current_group;
+    extension_id_to_group_id_map_[extension_id] = current_group_id;
+
+    CHECK_LT(next_available_section_index_, kReservedSectionMap.size());
+    const omnibox::GroupSection current_section =
+        kReservedSectionMap.at(next_available_section_index_++);
+
+    omnibox::GroupConfig group;
+    group.set_section(current_section);
+    group.set_render_type(omnibox::GroupConfig_RenderType_DEFAULT_VERTICAL);
+    group.set_header_text(base::UTF16ToUTF8(template_url->keyword()));
+    provider_->AddToSuggestionGroupsMap(current_group_id, std::move(group));
   }
 
   int first_relevance = 10000000;
@@ -112,13 +126,11 @@
   provider_->NotifyListeners(!extension_suggest_matches_.empty());
 }
 
-// Input has been accepted, so we're done with this input session. Ensure
-// we don't send the OnInputCancelled event, or handle any more stray
-// suggestions_ready events.
 void UnscopedExtensionProviderDelegateImpl::OnOmniboxInputEntered() {
-  // TODO(378538411): make sure this called when a match created by this class
-  // is selected.
-  IncrementRequestId();
+  // Input has been accepted, clear the current list of suggestions and ensure
+  // any suggestions that may be incoming later with a stale request ID are
+  // discarded.
+  Stop(/*clear_cached_results=*/true);
 }
 
 AutocompleteMatch
@@ -153,12 +165,13 @@
 
   match.contents_class =
       extensions::StyleTypesToACMatchClassifications(suggestion);
-  match.suggestion_group_id = extension_id_group_map_[extension_id];
+  match.suggestion_group_id = extension_id_to_group_id_map_[extension_id];
   return match;
 }
 
-void UnscopedExtensionProviderDelegateImpl::ResetSuggestionGroupsMap() {
-  provider_->ClearSuggestionGroupsMap();
-  extension_id_group_map_.clear();
+void UnscopedExtensionProviderDelegateImpl::ClearSuggestions() {
+  extension_suggest_matches_.clear();
+  extension_id_to_group_id_map_.clear();
   next_available_group_index_ = 0;
+  next_available_section_index_ = 0;
 }
diff --git a/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.h b/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.h
index 1b6c4e20..c699b52 100644
--- a/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.h
+++ b/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.h
@@ -47,7 +47,7 @@
   void Start(const AutocompleteInput& input,
              bool minimal_changes,
              std::set<std::string> unscoped_mode_extension_ids) override;
-  void IncrementRequestId() override;
+  void Stop(bool clear_cached_results) override;
 
   // OmniboxInputWatcher::Observer:
   void OnOmniboxInputEntered() override;
@@ -63,26 +63,29 @@
       int relevance,
       const std::string& extension_id);
 
-  // Resets all state related to suggestion group mapping.
-  void ResetSuggestionGroupsMap();
+  // Clears the current list of cached matches and suggestion group information.
+  void ClearSuggestions();
 
-  // Identifies the current input state. This is incremented each time the
-  // autocomplete edit's input changes in any way. It is used to tell
-  // whether suggest results from the extension are current.
+  // Incremented each time a new request for suggestions is sent to extensions
+  // or when the input is accepted. Used to discard any suggestions that may be
+  // incoming later with a stale request ID.
   int current_request_id_ = 0;
 
-  // TODO(378538411): populate this once the suggestions logic is implemented.
-  //  Saved suggestions that were received from the extension used
-  //  for resetting matches without asking the extension again.
+  // Current list of matches received from the extensions. Used to update the
+  // list of matches in the provider.
   std::vector<AutocompleteMatch> extension_suggest_matches_;
 
   // Next group available to be given to a set of extension suggestions.
   // Possible groups are defined in `kReservedGroupIdMap`.
-  int next_available_group_index_ = 0;
+  size_t next_available_group_index_ = 0;
+  // Next section available to be given to a set of extension suggestions.
+  // Possible sections are defined in `kReservedSectionMap`.
+  size_t next_available_section_index_ = 0;
 
-  // Maps extension id to a group. Allows extensions to have distinct headers.
+  // Maps extension IDs to group IDs. Allows suggestions from different
+  // extensions to have distinct headers.
   std::unordered_map<extensions::ExtensionId, omnibox::GroupId>
-      extension_id_group_map_;
+      extension_id_to_group_id_map_;
 
   raw_ptr<Profile> profile_;
 
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillImageFetcherUtils.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillImageFetcherUtils.java
index d2c19dc..7858279 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillImageFetcherUtils.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/AutofillImageFetcherUtils.java
@@ -8,9 +8,9 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.Path;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
 import android.graphics.RectF;
 
 import androidx.annotation.Px;
@@ -35,6 +35,82 @@
     }
 
     /**
+     * Adds corner radius to a bitmap.
+     *
+     * @param bitmap The input bitmap whose corners are to be rounded.
+     * @param cornerRadius Corner radius in Px.
+     * @return A copy of the input bitmap with rounded corners.
+     */
+    static Bitmap roundCorners(Bitmap bitmap, @Px int cornerRadius) {
+        Bitmap outputBitmap =
+                Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(outputBitmap);
+        Path path = new Path();
+        RectF rect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
+        path.addRoundRect(rect, cornerRadius, cornerRadius, Path.Direction.CW);
+        canvas.clipPath(path);
+        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        canvas.drawBitmap(bitmap, 0, 0, paint);
+
+        return outputBitmap;
+    }
+
+    /**
+     * Adds a background and center aligns the input bitmap.
+     *
+     * <p>The input bitmap dimensions should not be larger than the background dimensions. If it is
+     * larger, the input bitmap will be returned without any modifications.
+     *
+     * @param bitmap The input bitmap to which the background has to be added.
+     * @param backgroundWidth Background width in Px.
+     * @param backgroundHeight Background height in Px.
+     * @param backgroundColor Background color.
+     * @return A new bitmap which is a composite of the input bitmap placed on a background.
+     */
+    static Bitmap addCenterAlignedBackground(
+            Bitmap bitmap, @Px int backgroundWidth, @Px int backgroundHeight, int backgroundColor) {
+        if (bitmap.getWidth() > backgroundWidth || bitmap.getHeight() > backgroundHeight) {
+            return bitmap;
+        }
+        Bitmap outputBitmap =
+                Bitmap.createBitmap(backgroundWidth, backgroundHeight, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(outputBitmap);
+        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        paint.setColor(backgroundColor);
+        canvas.drawRect(0, 0, backgroundWidth, backgroundHeight, paint);
+        float left = (backgroundWidth - bitmap.getWidth()) / 2f;
+        float top = (backgroundHeight - bitmap.getHeight()) / 2f;
+        canvas.drawBitmap(bitmap, left, top, /* paint= */ null);
+
+        return outputBitmap;
+    }
+
+    /**
+     * Adds border to a bitmap.
+     *
+     * @param bitmap The input to which a border is to be added.
+     * @param cornerRadius Corner radius of the bitmap in Px. The border will be added with the same
+     *     corner radius.
+     * @param borderThickness Border thickness in Px.
+     * @param borderColor Border color.
+     * @return A copy of the input bitmap with border.
+     */
+    static Bitmap addBorder(
+            Bitmap bitmap, @Px int cornerRadius, @Px int borderThickness, int borderColor) {
+        Bitmap outputBitmap = bitmap.copy(bitmap.getConfig(), /* isMutable= */ true);
+        Canvas canvas = new Canvas(outputBitmap);
+        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        paint.setColor(borderColor);
+        paint.setStyle(Paint.Style.STROKE);
+        paint.setStrokeWidth(borderThickness);
+        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+        RectF rectF = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
+        canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint);
+
+        return outputBitmap;
+    }
+
+    /**
      * Treats Pix account image as per specifications:
      *
      * <p>Resizes the logo to 18dp x 18dp if the logo is of a different dimension, and adds a corner
@@ -52,60 +128,39 @@
      */
     static Bitmap treatPixAccountImage(Bitmap bitmap) {
         @Px int logoSize = getPixelSize(R.dimen.square_card_icon_side_length);
-        @Px int logoCornerRadius = getPixelSize(R.dimen.square_card_icon_corner_radius);
-        @Px int iconWidth = getPixelSize(R.dimen.large_card_icon_width);
-        @Px int iconHeight = getPixelSize(R.dimen.large_card_icon_height);
         @Px int iconCornerRadius = getPixelSize(R.dimen.large_card_icon_corner_radius);
-        @Px int iconBorderWidth = getPixelSize(R.dimen.card_icon_border_width);
 
         if (bitmap.getWidth() != logoSize || bitmap.getHeight() != logoSize) {
             bitmap = Bitmap.createScaledBitmap(bitmap, logoSize, logoSize, /* filter= */ true);
         }
 
         // Round the corners of the square bitmap.
-        Bitmap squareBitmap = Bitmap.createBitmap(logoSize, logoSize, Bitmap.Config.ARGB_8888);
-        Canvas squareCanvas = new Canvas(squareBitmap);
-        Paint squarePaint = new Paint();
-        squarePaint.setAntiAlias(true);
-        RectF squareRectF = new RectF(0, 0, logoSize, logoSize);
-        squareCanvas.drawRoundRect(squareRectF, logoCornerRadius, logoCornerRadius, squarePaint);
-        squarePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
-        squareCanvas.drawBitmap(bitmap, 0, 0, squarePaint);
+        Bitmap logoWithRoundCorners =
+                roundCorners(bitmap, getPixelSize(R.dimen.square_card_icon_corner_radius));
 
         // Create a white background and place the square logo in the center.
-        Bitmap backgroundBitmap =
-                Bitmap.createBitmap(iconWidth, iconHeight, Bitmap.Config.ARGB_8888);
-        Canvas backgroundCanvas = new Canvas(backgroundBitmap);
-        Paint backgroundPaint = new Paint();
-        backgroundPaint.setColor(Color.WHITE);
-        backgroundPaint.setAntiAlias(true);
-        backgroundCanvas.drawRect(0, 0, iconWidth, iconHeight, backgroundPaint);
-        int left = (iconWidth - logoSize) / 2;
-        int top = (iconHeight - logoSize) / 2;
-        backgroundCanvas.drawBitmap(squareBitmap, left, top, null);
+        Bitmap compositeBitmap =
+                addCenterAlignedBackground(
+                        logoWithRoundCorners,
+                        getPixelSize(R.dimen.large_card_icon_width),
+                        getPixelSize(R.dimen.large_card_icon_height),
+                        Color.WHITE);
 
-        // Round the corners of the composite image.
-        Bitmap bitmapWithEnhancements =
-                Bitmap.createBitmap(iconWidth, iconHeight, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmapWithEnhancements);
-        Paint paint = new Paint();
-        paint.setAntiAlias(true);
-        Rect rect = new Rect(0, 0, iconWidth, iconHeight);
-        RectF rectF = new RectF(rect);
-        canvas.drawRoundRect(rectF, iconCornerRadius, iconCornerRadius, paint);
-        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
-        canvas.drawBitmap(backgroundBitmap, rect, rect, paint);
+        // Round the corners of the composite bitmap.
+        Bitmap compositeBitmapWithRoundCorners = roundCorners(compositeBitmap, iconCornerRadius);
 
         // Add the grey border.
         // TODO(crbug.com/389947287): Verify the border color.
         int greyColor =
                 ContextCompat.getColor(
                         ContextUtils.getApplicationContext(), R.color.baseline_neutral_90);
-        paint.setColor(greyColor);
-        paint.setStyle(Paint.Style.STROKE);
-        paint.setStrokeWidth(iconBorderWidth);
-        canvas.drawRoundRect(rectF, iconCornerRadius, iconCornerRadius, paint);
+        Bitmap compositeBitmapWithRoundCornersAndGreyBorder =
+                addBorder(
+                        compositeBitmapWithRoundCorners,
+                        iconCornerRadius,
+                        getPixelSize(R.dimen.card_icon_border_width),
+                        greyColor);
 
-        return bitmapWithEnhancements;
+        return compositeBitmapWithRoundCornersAndGreyBorder;
     }
 }
diff --git a/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchDonor.java b/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchDonor.java
index e09d80b..b4483bad 100644
--- a/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchDonor.java
+++ b/chrome/browser/auxiliary_search/java/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchDonor.java
@@ -39,6 +39,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.ResettersForTesting;
+import org.chromium.base.shared_preferences.SharedPreferencesManager;
 import org.chromium.base.task.AsyncTask;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
@@ -82,6 +83,7 @@
     private Callback<Boolean> mPendingCallback;
     private boolean mSharedTabsWithOsState;
     private Boolean mIsDeviceCompatible;
+    private boolean mIsCreatedSessionAndInitForTesting;
 
     /** Static class that implements the initialization-on-demand holder idiom. */
     private static class LazyHolder {
@@ -99,7 +101,9 @@
         mSkipSchemaCheck = AuxiliarySearchUtils.SKIP_SCHEMA_CHECK.getValue();
 
         mSharedTabsWithOsState = AuxiliarySearchUtils.isShareTabsWithOsEnabled();
-        if (mSharedTabsWithOsState) {
+        boolean shouldInit = mSharedTabsWithOsState || !isShareTabsWithOsEnabledKeyExist();
+        if (shouldInit) {
+            mIsCreatedSessionAndInitForTesting = true;
             createSessionAndInit();
         }
     }
@@ -671,6 +675,12 @@
                 UI_THREAD_EXECUTOR);
     }
 
+    @VisibleForTesting
+    boolean isShareTabsWithOsEnabledKeyExist() {
+        SharedPreferencesManager prefsManager = ChromeSharedPreferences.getInstance();
+        return prefsManager.contains(ChromePreferenceKeys.SHARING_TABS_WITH_OS);
+    }
+
     public boolean getIsSchemaSetForTesting() {
         return mIsSchemaSet;
     }
@@ -714,4 +724,12 @@
         mAppSearchSession = sessionForTesting;
         ResettersForTesting.register(() -> mAppSearchSession = oldSession);
     }
+
+    public static AuxiliarySearchDonor createDonorForTesting() {
+        return new AuxiliarySearchDonor();
+    }
+
+    public boolean getIsCreatedSessionAndInitForTesting() {
+        return mIsCreatedSessionAndInitForTesting;
+    }
 }
diff --git a/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchDonorUnitTest.java b/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchDonorUnitTest.java
index 938f143..088cd032 100644
--- a/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchDonorUnitTest.java
+++ b/chrome/browser/auxiliary_search/junit/src/org/chromium/chrome/browser/auxiliary_search/AuxiliarySearchDonorUnitTest.java
@@ -72,7 +72,7 @@
         assertTrue(AuxiliarySearchUtils.isShareTabsWithOsEnabled());
 
         AuxiliarySearchDonor.setSkipInitializationForTesting(true);
-        mAuxiliarySearchDonor = AuxiliarySearchDonor.getInstance();
+        mAuxiliarySearchDonor = AuxiliarySearchDonor.createDonorForTesting();
         try {
             when(mAppSearchSession.get()).thenReturn(mSession);
             mAuxiliarySearchDonor.setAppSearchSessionForTesting(mAppSearchSession);
@@ -87,6 +87,17 @@
         // #createSessionAndInit() has been called in AuxiliarySearchDonor's constructor.
         // Verifies that calling createSessionAndInit() again will early exit.
         assertFalse(mAuxiliarySearchDonor.createSessionAndInit());
+        assertTrue(mAuxiliarySearchDonor.getIsCreatedSessionAndInitForTesting());
+    }
+
+    @Test
+    @SmallTest
+    public void testCreateSessionAndInit_DefaultDisabled() {
+        when(mHooks.isSettingDefaultEnabledByOs()).thenReturn(false);
+        assertFalse(AuxiliarySearchControllerFactory.getInstance().isSettingDefaultEnabledByOs());
+
+        mAuxiliarySearchDonor = AuxiliarySearchDonor.createDonorForTesting();
+        assertTrue(mAuxiliarySearchDonor.getIsCreatedSessionAndInitForTesting());
     }
 
     @Test
@@ -295,6 +306,21 @@
         verify(mCallback).onResult(eq(true));
     }
 
+    @Test
+    @SmallTest
+    public void testIsShareTabsWithOsEnabledKeyExist() {
+        SharedPreferencesManager prefsManager = ChromeSharedPreferences.getInstance();
+        prefsManager.removeKey(ChromePreferenceKeys.SHARING_TABS_WITH_OS);
+
+        assertFalse(mAuxiliarySearchDonor.isShareTabsWithOsEnabledKeyExist());
+
+        prefsManager.writeBoolean(ChromePreferenceKeys.SHARING_TABS_WITH_OS, true);
+        assertTrue(mAuxiliarySearchDonor.isShareTabsWithOsEnabledKeyExist());
+
+        prefsManager.writeBoolean(ChromePreferenceKeys.SHARING_TABS_WITH_OS, false);
+        assertTrue(mAuxiliarySearchDonor.isShareTabsWithOsEnabledKeyExist());
+    }
+
     private SearchResult createSearchResult(int applicationType) {
         GlobalSearchApplicationInfo appInfo =
                 new GlobalSearchApplicationInfo.Builder("namespace", "id", applicationType)
diff --git a/chrome/browser/background/background_mode_manager.cc b/chrome/browser/background/background_mode_manager.cc
index 6ec4745..e11b774 100644
--- a/chrome/browser/background/background_mode_manager.cc
+++ b/chrome/browser/background/background_mode_manager.cc
@@ -988,10 +988,14 @@
 }
 
 void BackgroundModeManager::RemoveStatusTrayIcon() {
-  if (status_icon_)
-    status_tray_->RemoveStatusIcon(status_icon_);
-  status_icon_ = nullptr;
-  context_menu_ = nullptr;
+  if (status_icon_) {
+    std::unique_ptr<StatusIcon> removed_icon =
+        status_tray_->RemoveStatusIcon(status_icon_);
+    context_menu_ = nullptr;
+    status_icon_ = nullptr;
+    removed_icon.reset();
+  }
+  status_tray_ = nullptr;
 }
 
 BackgroundModeManager::BackgroundModeData*
diff --git a/chrome/browser/background/background_mode_manager.h b/chrome/browser/background/background_mode_manager.h
index ad9b08d..b78f5c7 100644
--- a/chrome/browser/background/background_mode_manager.h
+++ b/chrome/browser/background/background_mode_manager.h
@@ -434,14 +434,14 @@
 
   // Reference to our status tray. If null, the platform doesn't support status
   // icons.
-  raw_ptr<StatusTray, DanglingUntriaged> status_tray_ = nullptr;
+  raw_ptr<StatusTray> status_tray_ = nullptr;
 
   // Reference to our status icon (if any) - owned by the StatusTray.
-  raw_ptr<StatusIcon, DanglingUntriaged> status_icon_ = nullptr;
+  raw_ptr<StatusIcon> status_icon_ = nullptr;
 
   // Reference to our status icon's context menu (if any) - owned by the
   // status_icon_.
-  raw_ptr<StatusIconMenuModel, DanglingUntriaged> context_menu_ = nullptr;
+  raw_ptr<StatusIconMenuModel> context_menu_ = nullptr;
 
   // Set to true when we are running in background mode. Allows us to track our
   // current background state so we can take the appropriate action when the
diff --git a/chrome/browser/background/background_mode_manager_unittest.cc b/chrome/browser/background/background_mode_manager_unittest.cc
index b76fc50..7d2afac 100644
--- a/chrome/browser/background/background_mode_manager_unittest.cc
+++ b/chrome/browser/background/background_mode_manager_unittest.cc
@@ -259,7 +259,6 @@
   void TearDown() override {
     // Clean up the status icon. If this is not done before profile deletes,
     // the context menu updates will DCHECK with the now deleted profiles.
-    delete manager_->status_icon_;
     manager_->status_icon_ = nullptr;
 
     // We're getting ready to shutdown the message loop. Clear everything out!
@@ -770,7 +769,9 @@
   service2->AddExtension(build_regular_extension().get());
   service2->AddExtension(build_regular_extension_with_options().get());
 
-  manager_->status_icon_ = new TestStatusIcon();
+  std::unique_ptr<StatusIcon> test_status_icon =
+      std::make_unique<TestStatusIcon>();
+  manager_->status_icon_ = test_status_icon.get();
   manager_->UpdateStatusTrayIconContextMenu();
   StatusIconMenuModel* context_menu = manager_->context_menu_;
   EXPECT_TRUE(context_menu);
@@ -846,6 +847,9 @@
   EXPECT_TRUE(IsCommandEnabled(context_menu, 6));   // P2 - RE
   EXPECT_TRUE(IsCommandEnabled(context_menu, 7));   // P2 - REO
   EXPECT_TRUE(IsCommandEnabled(context_menu, 8));   // P2
+
+  manager_->context_menu_ = nullptr;
+  manager_->status_icon_ = nullptr;
 }
 
 TEST_F(BackgroundModeManagerWithExtensionsTest, BalloonDisplay) {
@@ -891,7 +895,9 @@
   run_loop.Run();
 
   ASSERT_TRUE(system->is_ready());
-  manager_->status_icon_ = new TestStatusIcon();
+  std::unique_ptr<StatusIcon> test_status_icon =
+      std::make_unique<TestStatusIcon>();
+  manager_->status_icon_ = test_status_icon.get();
   manager_->UpdateStatusTrayIconContextMenu();
 
   // Adding a background extension should show the balloon.
@@ -921,6 +927,9 @@
   // show the balloon.
   service->AddExtension(upgraded_no_bg_ext_has_bg.get());
   EXPECT_TRUE(manager_->HasShownBalloon());
+
+  manager_->context_menu_ = nullptr;
+  manager_->status_icon_ = nullptr;
 }
 
 TEST_F(BackgroundModeManagerTest, TransientBackgroundApp) {
diff --git a/chrome/browser/bookmarks/bookmark_html_writer.cc b/chrome/browser/bookmarks/bookmark_html_writer.cc
index 3dcdf595..ab15626 100644
--- a/chrome/browser/bookmarks/bookmark_html_writer.cc
+++ b/chrome/browser/bookmarks/bookmark_html_writer.cc
@@ -249,9 +249,7 @@
   }
 
   // Increments the indent.
-  void IncrementIndent() {
-    indent_.resize(indent_.size() + kIndentSize, ' ');
-  }
+  void IncrementIndent() { indent_.resize(indent_.size() + kIndentSize, ' '); }
 
   // Decrements the indent.
   void DecrementIndent() {
@@ -304,9 +302,7 @@
   }
 
   // Indents the current line.
-  bool WriteIndent() {
-    return Write(indent_);
-  }
+  bool WriteIndent() { return Write(indent_); }
 
   // Converts a time string written to the JSON codec into a time_t string
   // (used by bookmarks.html) and writes it.
@@ -377,18 +373,15 @@
         return false;
       }
       if (folder_type == BookmarkNode::BOOKMARK_BAR) {
-        if (!Write(kBookmarkBar))
+        if (!Write(kBookmarkBar)) {
           return false;
+        }
         title = l10n_util::GetStringUTF8(IDS_BOOKMARK_BAR_FOLDER_NAME);
       } else if (!Write(kFolderAttributeEnd)) {
         return false;
       }
-      if (!Write(title, CONTENT) ||
-          !Write(kFolderEnd) ||
-          !Write(kNewline) ||
-          !WriteIndent() ||
-          !Write(kFolderChildren) ||
-          !Write(kNewline)) {
+      if (!Write(title, CONTENT) || !Write(kFolderEnd) || !Write(kNewline) ||
+          !WriteIndent() || !Write(kFolderChildren) || !Write(kNewline)) {
         return false;
       }
       IncrementIndent();
@@ -407,9 +400,7 @@
         folder_type != BookmarkNode::MOBILE) {
       // Close out the folder.
       DecrementIndent();
-      if (!WriteIndent() ||
-          !Write(kFolderChildrenEnd) ||
-          !Write(kNewline)) {
+      if (!WriteIndent() || !Write(kFolderChildrenEnd) || !Write(kNewline)) {
         return false;
       }
     }
@@ -443,9 +434,7 @@
     Profile* profile,
     const base::FilePath& path,
     BookmarksExportObserver* observer)
-    : profile_(profile),
-      path_(path),
-      observer_(observer) {
+    : profile_(profile), path_(path), observer_(observer) {
   DCHECK(!profile->IsOffTheRecord());
   favicons_map_ = std::make_unique<URLFaviconMap>();
 }
@@ -457,20 +446,23 @@
       BookmarkModelFactory::GetForBrowserContext(profile_)->other_node());
   ExtractUrls(
       BookmarkModelFactory::GetForBrowserContext(profile_)->mobile_node());
-  if (!bookmark_urls_.empty())
+  if (!bookmark_urls_.empty()) {
     FetchNextFavicon();
-  else
+  } else {
     ExecuteWriter();
+  }
 }
 
 void BookmarkFaviconFetcher::ExtractUrls(const BookmarkNode* node) {
   if (node->is_url()) {
     std::string url = node->url().spec();
-    if (!url.empty())
+    if (!url.empty()) {
       bookmark_urls_.push_back(url);
+    }
   } else {
-    for (const auto& child : node->children())
+    for (const auto& child : node->children()) {
       ExtractUrls(child.get());
+    }
   }
 }
 
@@ -519,8 +511,7 @@
     bookmark_urls_.pop_front();
   }
   if (bitmap_result.is_valid() && !url.is_empty()) {
-    favicons_map_->insert(
-        make_pair(url.spec(), bitmap_result.bitmap_data));
+    favicons_map_->insert(make_pair(url.spec(), bitmap_result.bitmap_data));
   }
 
   if (FetchNextFavicon()) {
@@ -535,8 +526,9 @@
                     const base::FilePath& path,
                     BookmarksExportObserver* observer) {
   // We allow only one concurrent bookmark export operation per profile.
-  if (profile->GetUserData(kBookmarkFaviconFetcherKey))
+  if (profile->GetUserData(kBookmarkFaviconFetcherKey)) {
     return;
+  }
 
   auto fetcher =
       std::make_unique<BookmarkFaviconFetcher>(profile, path, observer);
diff --git a/chrome/browser/bookmarks/bookmark_html_writer_unittest.cc b/chrome/browser/bookmarks/bookmark_html_writer_unittest.cc
index 1b310274..797f09a 100644
--- a/chrome/browser/bookmarks/bookmark_html_writer_unittest.cc
+++ b/chrome/browser/bookmarks/bookmark_html_writer_unittest.cc
@@ -75,17 +75,19 @@
   std::u16string BookmarkEntryToString(const ImportedBookmarkEntry& entry) {
     std::u16string result;
     result.append(u"on_toolbar=");
-    if (entry.in_toolbar)
+    if (entry.in_toolbar) {
       result.append(u"true");
-    else
+    } else {
       result.append(u"false");
+    }
 
     result.append(u" url=" + base::UTF8ToUTF16(entry.url.spec()));
 
     result.append(u" path=");
     for (size_t i = 0; i < entry.path.size(); ++i) {
-      if (i != 0)
+      if (i != 0) {
         result.append(u"/");
+      }
       result.append(entry.path[i]);
     }
 
@@ -112,8 +114,9 @@
       entry.path.push_back(f1);
       if (!f2.empty()) {
         entry.path.push_back(f2);
-        if (!f3.empty())
+        if (!f3.empty()) {
           entry.path.push_back(f3);
+        }
       }
     }
     entry.title = title;
@@ -129,8 +132,8 @@
                                  const std::u16string& f1,
                                  const std::u16string& f2,
                                  const std::u16string& f3) {
-    EXPECT_EQ(BookmarkValuesToString(on_toolbar, url, title, creation_time,
-                                     f1, f2, f3),
+    EXPECT_EQ(BookmarkValuesToString(on_toolbar, url, title, creation_time, f1,
+                                     f2, f3),
               BookmarkEntryToString(entry));
   }
 
@@ -218,8 +221,8 @@
   base::Time t2(t1 + base::Hours(1));
   base::Time t3(t1 + base::Hours(1));
   base::Time t4(t1 + base::Hours(1));
-  const BookmarkNode* f1 = model->AddFolder(
-      model->bookmark_bar_node(), 0, f1_title);
+  const BookmarkNode* f1 =
+      model->AddFolder(model->bookmark_bar_node(), 0, f1_title);
   model->AddURL(f1, 0, url1_title, url1, nullptr, t1);
   HistoryServiceFactory::GetForProfile(profile.get(),
                                        ServiceAccessType::EXPLICIT_ACCESS)
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index dd324aa9..fe7816cc 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2430,15 +2430,9 @@
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
   auto& registrar =
       web_app::WebAppProvider::GetForWebApps(profile)->registrar_unsafe();
-  // TODO(crbug.com/379916273): Evaluate call sites of FindBestAppWithUrlInScope
-  // for correctness.
   std::vector<webapps::AppId> app_ids_for_origin =
       registrar.FindAllAppsNestedInUrl(
-          app_origin.GetURL(),
-          {
-              web_app::proto::InstallState::INSTALLED_WITH_OS_INTEGRATION,
-              web_app::proto::InstallState::INSTALLED_WITHOUT_OS_INTEGRATION,
-          });
+          app_origin.GetURL(), web_app::WebAppFilter::InstalledInChrome());
   if (app_ids_for_origin.empty()) {
     return blink::ParsedPermissionsPolicy();
   }
diff --git a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
index 1a6bcf4..8716ecf 100644
--- a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
+++ b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImpl.java
@@ -228,8 +228,9 @@
      */
     @CalledByNative
     void promoteTabGroup(String collaborationId, long resultCallback) {
+        mDataSharingTabManager.promoteTabGroup(collaborationId);
         CollaborationControllerDelegateImplJni.get()
-                .runResultCallback(Outcome.FAILURE, resultCallback);
+                .runResultCallback(Outcome.SUCCESS, resultCallback);
     }
 
     /** Focus and show the current flow screen. */
diff --git a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
index bb7f7c0..8594565 100644
--- a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
+++ b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationControllerDelegateImplUnitTest.java
@@ -180,4 +180,16 @@
         // Exit callback should be ran.
         verify(mCollaborationControllerDelegateImplNativeMock).runExitCallback(eq(exitCallback));
     }
+
+    @Test
+    public void testPromoteTabGroup() {
+        createDelegate(FlowType.JOIN);
+        long resultCallback = 1;
+        String collaborationId = "collaboration";
+
+        mCollaborationControllerDelegateImpl.promoteTabGroup(collaborationId, resultCallback);
+        verify(mDataSharingTabManager).promoteTabGroup(collaborationId);
+        verify(mCollaborationControllerDelegateImplNativeMock)
+                .runResultCallback(eq(Outcome.SUCCESS), eq(resultCallback));
+    }
 }
diff --git a/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustBottomSheetMediator.java b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustBottomSheetMediator.java
index ba35d39..dcc9f28 100644
--- a/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustBottomSheetMediator.java
+++ b/chrome/browser/commerce/merchant_viewer/android/java/src/org/chromium/chrome/browser/merchant_viewer/MerchantTrustBottomSheetMediator.java
@@ -228,7 +228,7 @@
 
     void destroyWebContents() {
         if (mWebContentsObserver != null) {
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
             mWebContentsObserver = null;
         }
         if (mWebContents != null) {
diff --git a/chrome/browser/commerce/price_change/android/java/src/org/chromium/chrome/browser/price_change/PriceChangeModuleMediator.java b/chrome/browser/commerce/price_change/android/java/src/org/chromium/chrome/browser/price_change/PriceChangeModuleMediator.java
index 3e8822f7d..c1e5c53 100644
--- a/chrome/browser/commerce/price_change/android/java/src/org/chromium/chrome/browser/price_change/PriceChangeModuleMediator.java
+++ b/chrome/browser/commerce/price_change/android/java/src/org/chromium/chrome/browser/price_change/PriceChangeModuleMediator.java
@@ -197,6 +197,7 @@
         mSharedPreferences.unregisterOnSharedPreferenceChangeListener(
                 mPriceAnnotationsPrefListener);
         mTabModelSelector.removeObserver(this);
+        mFaviconHelper.destroy();
     }
 
     int getModuleType() {
diff --git a/chrome/browser/content_settings/page_specific_content_settings_delegate.cc b/chrome/browser/content_settings/page_specific_content_settings_delegate.cc
index b0a5dd53..01ddb8d 100644
--- a/chrome/browser/content_settings/page_specific_content_settings_delegate.cc
+++ b/chrome/browser/content_settings/page_specific_content_settings_delegate.cc
@@ -317,15 +317,6 @@
   return false;
 }
 
-bool PageSpecificContentSettingsDelegate::IsPiPWindow(
-    content::WebContents* web_contents) {
-  DCHECK(web_contents);
-  content::WebContents* child_web_contents =
-      PictureInPictureWindowManager::GetInstance()->GetChildWebContents();
-
-  return child_web_contents == web_contents;
-}
-
 void PageSpecificContentSettingsDelegate::PrimaryPageChanged(
     content::Page& page) {
   ClearPendingProtocolHandler();
diff --git a/chrome/browser/content_settings/page_specific_content_settings_delegate.h b/chrome/browser/content_settings/page_specific_content_settings_delegate.h
index 674e6ba..3d15a5a 100644
--- a/chrome/browser/content_settings/page_specific_content_settings_delegate.h
+++ b/chrome/browser/content_settings/page_specific_content_settings_delegate.h
@@ -93,7 +93,6 @@
   bool IsBlockedOnSystemLevel(ContentSettingsType type) override;
   bool IsFrameAllowlistedForJavaScript(
       content::RenderFrameHost* render_frame_host) override;
-  bool IsPiPWindow(content::WebContents* web_contents) override;
 
   // content::WebContentsObserver:
   void PrimaryPageChanged(content::Page& page) override;
diff --git a/chrome/browser/contextual_cueing/BUILD.gn b/chrome/browser/contextual_cueing/BUILD.gn
index 3e775cc..6553b039 100644
--- a/chrome/browser/contextual_cueing/BUILD.gn
+++ b/chrome/browser/contextual_cueing/BUILD.gn
@@ -2,22 +2,35 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//chrome/common/features.gni")
+
 source_set("contextual_cueing") {
   sources = [
-    "contextual_cueing_features.cc",
     "contextual_cueing_features.h",
-    "contextual_cueing_helper.cc",
     "contextual_cueing_helper.h",
   ]
   public_deps = [
     "//base",
     "//content/public/browser",
   ]
+}
+
+source_set("impl") {
+  sources = [
+    "contextual_cueing_features.cc",
+    "contextual_cueing_helper.cc",
+  ]
   deps = [
+    "//chrome/browser/contextual_cueing",
     "//chrome/browser/optimization_guide",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui",
+    "//chrome/browser/ui/browser_window",
     "//components/optimization_guide/core:core",
     "//components/optimization_guide/proto:optimization_guide_proto",
     "//url",
   ]
+  if (enable_glic) {
+    deps += [ "//chrome/browser/glic:enabling" ]
+  }
 }
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_helper.cc b/chrome/browser/contextual_cueing/contextual_cueing_helper.cc
index 8b942a3..024f0b2 100644
--- a/chrome/browser/contextual_cueing/contextual_cueing_helper.cc
+++ b/chrome/browser/contextual_cueing/contextual_cueing_helper.cc
@@ -9,7 +9,12 @@
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
+#include "chrome/browser/ui/tabs/glic_nudge_controller.h"
 #include "components/optimization_guide/core/model_execution/model_execution_features_controller.h"
+#include "components/optimization_guide/core/optimization_guide_decider.h"
 #include "components/optimization_guide/core/optimization_metadata.h"
 #include "components/optimization_guide/proto/icon_view_metadata.pb.h"
 #include "content/public/browser/navigation_handle.h"
@@ -17,6 +22,10 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
+#if BUILDFLAG(ENABLE_GLIC)
+#include "chrome/browser/glic/glic_enabling.h"
+#endif
+
 namespace contextual_cueing {
 
 ContextualCueingHelper::ContextualCueingHelper(
@@ -33,40 +42,59 @@
 
 // content::WebContentsObserver
 void ContextualCueingHelper::DocumentOnLoadCompletedInPrimaryMainFrame() {
+  last_navigation_cue_label_.clear();
+  Browser* browser = chrome::FindBrowserWithTab(web_contents());
+  if (!browser) {
+    return;
+  }
+  auto* glic_nudge_controller =
+      browser->browser_window_features()->glic_nudge_controller();
+  if (!glic_nudge_controller) {
+    return;
+  }
+
   optimization_guide::OptimizationMetadata metadata;
   auto decision = optimization_guide_keyed_service_->CanApplyOptimization(
       web_contents()->GetLastCommittedURL(),
       optimization_guide::proto::OPTIMIZATION_GUIDE_ICON_VIEW, &metadata);
-  if (decision != optimization_guide::OptimizationGuideDecision::kTrue ||
-      metadata.empty()) {
-    return;
+  if (decision == optimization_guide::OptimizationGuideDecision::kTrue &&
+      !metadata.empty()) {
+    auto parsed = metadata.ParsedMetadata<
+        optimization_guide::proto::OptimizationGuideIconViewMetadata>();
+    if (parsed->has_cue_label()) {
+      last_navigation_cue_label_ = parsed->cue_label();
+    }
   }
 
-  auto parsed = metadata.ParsedMetadata<
-      optimization_guide::proto::OptimizationGuideIconViewMetadata>();
-  // TODO(crbug.com/388305688): trigger cueing UI.
+  glic_nudge_controller->UpdateNudgeLabel(web_contents(),
+                                          last_navigation_cue_label_);
 }
 
 // static
-std::unique_ptr<ContextualCueingHelper>
-ContextualCueingHelper::MaybeCreateForWebContents(
+void ContextualCueingHelper::MaybeCreateForWebContents(
     content::WebContents* web_contents) {
   if (!base::FeatureList::IsEnabled(contextual_cueing::kContextualCueing)) {
-    return nullptr;
+    return;
   }
 
+#if BUILDFLAG(ENABLE_GLIC)
   Profile* profile =
       Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  auto* optimization_guide_keyed_service_ =
-      OptimizationGuideKeyedServiceFactory::GetForProfile(profile);
-  if (!optimization_guide_keyed_service_ ||
-      !optimization_guide_keyed_service_->GetModelExecutionFeaturesController()
-           ->ShouldModelExecutionBeAllowedForUser()) {
-    return nullptr;
+  if (!GlicEnabling::IsProfileEligible(profile)) {
+    return;
   }
 
-  return base::WrapUnique<ContextualCueingHelper>(new ContextualCueingHelper(
-      web_contents, optimization_guide_keyed_service_));
+  auto* optimization_guide_keyed_service =
+      OptimizationGuideKeyedServiceFactory::GetForProfile(profile);
+  if (!optimization_guide_keyed_service ||
+      !optimization_guide_keyed_service->GetModelExecutionFeaturesController()
+           ->ShouldModelExecutionBeAllowedForUser()) {
+    return;
+  }
+
+  ContextualCueingHelper::CreateForWebContents(
+      web_contents, optimization_guide_keyed_service);
+#endif  // BUILDFLAG(ENABLE_GLIC)
 }
 
 WEB_CONTENTS_USER_DATA_KEY_IMPL(ContextualCueingHelper);
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_helper.h b/chrome/browser/contextual_cueing/contextual_cueing_helper.h
index 3d83040e..6616261 100644
--- a/chrome/browser/contextual_cueing/contextual_cueing_helper.h
+++ b/chrome/browser/contextual_cueing/contextual_cueing_helper.h
@@ -19,8 +19,7 @@
  public:
   // Creates ContextualCueingHelper and attaches it the `web_contents` if
   // contextual cueing is enabled.
-  [[nodiscard]] static std::unique_ptr<ContextualCueingHelper>
-  MaybeCreateForWebContents(content::WebContents* web_contents);
+  static void MaybeCreateForWebContents(content::WebContents* web_contents);
 
   ContextualCueingHelper(const ContextualCueingHelper&) = delete;
   ContextualCueingHelper& operator=(const ContextualCueingHelper&) = delete;
@@ -29,6 +28,10 @@
   // content::WebContentsObserver
   void DocumentOnLoadCompletedInPrimaryMainFrame() override;
 
+  const std::string& last_navigation_cue_label() const {
+    return last_navigation_cue_label_;
+  }
+
  private:
   ContextualCueingHelper(content::WebContents* contents,
                          OptimizationGuideKeyedService* ogks);
@@ -36,6 +39,9 @@
   raw_ptr<OptimizationGuideKeyedService> optimization_guide_keyed_service_ =
       nullptr;
 
+  // Holds the cue label for the last navigation in `this`.
+  std::string last_navigation_cue_label_;
+
   friend WebContentsUserData<ContextualCueingHelper>;
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 };
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_helper_interactive_uitest.cc b/chrome/browser/contextual_cueing/contextual_cueing_helper_interactive_uitest.cc
new file mode 100644
index 0000000..dde800b0
--- /dev/null
+++ b/chrome/browser/contextual_cueing/contextual_cueing_helper_interactive_uitest.cc
@@ -0,0 +1,162 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/contextual_cueing/contextual_cueing_features.h"
+#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
+#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_features.h"
+#include "chrome/browser/ui/tabs/glic_nudge_controller.h"
+#include "chrome/browser/ui/tabs/glic_nudge_observer.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/optimization_guide/core/optimization_metadata.h"
+#include "components/optimization_guide/proto/icon_view_metadata.pb.h"
+#include "components/signin/public/identity_manager/account_capabilities_test_mutator.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+
+#if BUILDFLAG(ENABLE_GLIC)
+
+class FakeGlicNudgeObserver : public GlicNudgeObserver {
+ public:
+  void OnTriggerGlicNudgeUI(std::string label) override {
+    last_nudge_label_ = label;
+  }
+  std::string last_nudge_label_;
+};
+
+class ContextualCueingHelperBrowserTest : public InProcessBrowserTest {
+ public:
+  ContextualCueingHelperBrowserTest() {
+    scoped_feature_list_.InitWithFeatures(
+        {features::kGlic, features::kTabstripComboButton,
+         contextual_cueing::kContextualCueing},
+        {});
+  }
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    identity_test_env_adaptor_ =
+        std::make_unique<IdentityTestEnvironmentProfileAdaptor>(
+            browser()->profile());
+  }
+
+  void SetUpInProcessBrowserTestFixture() override {
+    create_services_subscription_ =
+        BrowserContextDependencyManager::GetInstance()
+            ->RegisterCreateServicesCallbackForTesting(
+                base::BindRepeating(&ContextualCueingHelperBrowserTest::
+                                        OnWillCreateBrowserContextServices,
+                                    base::Unretained(this)));
+  }
+
+  void SetUpEnabledHints() {
+    optimization_guide::proto::OptimizationGuideIconViewMetadata
+        icon_view_metadata;
+    icon_view_metadata.set_cue_label("test label");
+    optimization_guide::OptimizationMetadata metadata;
+    metadata.SetAnyMetadataForTesting(icon_view_metadata);
+    OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile())
+        ->AddHintForTesting(
+            GURL("https://enabled.com/"),
+            optimization_guide::proto::OPTIMIZATION_GUIDE_ICON_VIEW, metadata);
+  }
+
+  void EnableSignIn() {
+    auto account_info =
+        identity_test_env_adaptor_->identity_test_env()
+            ->MakePrimaryAccountAvailable("user@gmail.com",
+                                          signin::ConsentLevel::kSignin);
+    AccountCapabilitiesTestMutator mutator(&account_info.capabilities);
+    mutator.set_can_use_model_execution_features(true);
+    identity_test_env_adaptor_->identity_test_env()
+        ->UpdateAccountInfoForAccount(account_info);
+  }
+
+  tabs::GlicNudgeController* glic_nudge_controller() {
+    return browser()->browser_window_features()->glic_nudge_controller();
+  }
+
+ private:
+  void OnWillCreateBrowserContextServices(content::BrowserContext* context) {
+    IdentityTestEnvironmentProfileAdaptor::
+        SetIdentityTestEnvironmentFactoriesOnBrowserContext(context);
+  }
+
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  // Identity test support.
+  std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
+      identity_test_env_adaptor_;
+  base::CallbackListSubscription create_services_subscription_;
+};
+
+IN_PROC_BROWSER_TEST_F(ContextualCueingHelperBrowserTest,
+                       TestCueLabelDisplayed) {
+  EnableSignIn();
+  SetUpEnabledHints();
+
+  FakeGlicNudgeObserver nudge_observer;
+  glic_nudge_controller()->AddObserver(&nudge_observer);
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL("https://enabled.com/"),
+      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+  EXPECT_EQ("test label", nudge_observer.last_nudge_label_);
+}
+
+IN_PROC_BROWSER_TEST_F(ContextualCueingHelperBrowserTest,
+                       TestCueLabelNotDisplayed) {
+  EnableSignIn();
+  SetUpEnabledHints();
+
+  FakeGlicNudgeObserver nudge_observer;
+  glic_nudge_controller()->AddObserver(&nudge_observer);
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL("https://disabled.com/"),
+      WindowOpenDisposition::CURRENT_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+  EXPECT_TRUE(nudge_observer.last_nudge_label_.empty());
+}
+
+IN_PROC_BROWSER_TEST_F(ContextualCueingHelperBrowserTest,
+                       TestCueLabelDisplayedForActiveTab) {
+  EnableSignIn();
+  SetUpEnabledHints();
+
+  FakeGlicNudgeObserver nudge_observer;
+  glic_nudge_controller()->AddObserver(&nudge_observer);
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL("https://enabled.com/"),
+      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+  EXPECT_EQ("test label", nudge_observer.last_nudge_label_);
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL("https://disabled.com/"),
+      WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP));
+  EXPECT_TRUE(nudge_observer.last_nudge_label_.empty());
+
+  browser()->tab_strip_model()->ActivateTabAt(1);
+  EXPECT_EQ("test label", nudge_observer.last_nudge_label_);
+
+  browser()->tab_strip_model()->ActivateTabAt(2);
+  EXPECT_TRUE(nudge_observer.last_nudge_label_.empty());
+}
+
+#endif
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_helper_unittest.cc b/chrome/browser/contextual_cueing/contextual_cueing_helper_unittest.cc
index 31270af..0f55f8254 100644
--- a/chrome/browser/contextual_cueing/contextual_cueing_helper_unittest.cc
+++ b/chrome/browser/contextual_cueing/contextual_cueing_helper_unittest.cc
@@ -9,6 +9,7 @@
 #include "chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h"
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "components/component_updater/pref_names.h"
 #include "components/optimization_guide/core/model_execution/model_execution_prefs.h"
@@ -47,8 +48,10 @@
 class ContextualCueingHelperTest : public ChromeRenderViewHostTestHarness {
  public:
   ContextualCueingHelperTest() {
-    scoped_feature_list_.InitAndEnableFeature(
-        contextual_cueing::kContextualCueing);
+    scoped_feature_list_.InitWithFeatures(
+        {features::kGlic, features::kTabstripComboButton,
+         contextual_cueing::kContextualCueing},
+        {});
   }
 
   void SetUp() override {
@@ -72,11 +75,6 @@
                     DogfoodStatus::NON_DOGFOOD));
   }
 
-  void TearDown() override {
-    contextual_cueing_helper_.reset();
-    ChromeRenderViewHostTestHarness::TearDown();
-  }
-
   void EnableSignIn() {
     auto account_info = identity_test_env_.MakePrimaryAccountAvailable(
         "test_email", signin::ConsentLevel::kSignin);
@@ -101,9 +99,6 @@
         base::BindRepeating(&CreateOptimizationGuideKeyedService)}};
   }
 
- protected:
-  std::unique_ptr<ContextualCueingHelper> contextual_cueing_helper_;
-
  private:
   signin::IdentityTestEnvironment identity_test_env_;
   std::unique_ptr<TestingPrefServiceSimple> pref_service_;
@@ -111,24 +106,30 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 };
 
+#if BUILDFLAG(ENABLE_GLIC)
+
 TEST_F(ContextualCueingHelperTest, NullTabHelperWithoutSignin) {
-  contextual_cueing_helper_ =
-      ContextualCueingHelper::MaybeCreateForWebContents(web_contents());
-  EXPECT_FALSE(contextual_cueing_helper_);
+  ContextualCueingHelper::MaybeCreateForWebContents(web_contents());
+  auto* contextual_cueing_helper =
+      ContextualCueingHelper::FromWebContents(web_contents());
+  EXPECT_FALSE(contextual_cueing_helper);
 }
 
 TEST_F(ContextualCueingHelperTest, NullTabHelperIfWithoutCapability) {
   EnableSignInWithoutCapability();
-  contextual_cueing_helper_ =
-      ContextualCueingHelper::MaybeCreateForWebContents(web_contents());
-  EXPECT_FALSE(contextual_cueing_helper_);
+  ContextualCueingHelper::MaybeCreateForWebContents(web_contents());
+  auto* contextual_cueing_helper =
+      ContextualCueingHelper::FromWebContents(web_contents());
+  EXPECT_FALSE(contextual_cueing_helper);
 }
 
 TEST_F(ContextualCueingHelperTest, TabHelperStartsUp) {
   EnableSignIn();
-  contextual_cueing_helper_ =
-      ContextualCueingHelper::MaybeCreateForWebContents(web_contents());
-  EXPECT_TRUE(contextual_cueing_helper_);
+  ContextualCueingHelper::MaybeCreateForWebContents(web_contents());
+  auto* contextual_cueing_helper =
+      ContextualCueingHelper::FromWebContents(web_contents());
+  EXPECT_TRUE(contextual_cueing_helper);
 }
+#endif  // BUILDFLAG(ENABLE_GLIC)
 
 }  // namespace contextual_cueing
diff --git a/chrome/browser/controlled_frame/controlled_frame_apitest.cc b/chrome/browser/controlled_frame/controlled_frame_apitest.cc
index 4733d916..fa43a44 100644
--- a/chrome/browser/controlled_frame/controlled_frame_apitest.cc
+++ b/chrome/browser/controlled_frame/controlled_frame_apitest.cc
@@ -70,90 +70,6 @@
   return file_contents;
 }
 
-const extensions::MenuItem::Id CreateMenuItemId(
-    const extensions::MenuItem::ExtensionKey& extension_key,
-    const std::string& string_uid) {
-  extensions::MenuItem::Id id;
-  id.extension_key = extension_key;
-  id.string_uid = string_uid;
-  return id;
-}
-
-const content::EvalJsResult CreateContextMenuItem(
-    content::RenderFrameHost* app_frame,
-    const std::string& id,
-    const std::string& title) {
-  return content::EvalJs(app_frame, content::JsReplace(R"(
-      new Promise(async (resolve, reject) => {
-        const frame = document.getElementsByTagName('controlledframe')[0];
-        if (!frame || !frame.contextMenus || !frame.contextMenus.create) {
-          reject('FAIL: frame, frame.contextMenus, or ' +
-              'frame.contextMenus.create is undefined');
-          return;
-        }
-        await frame.contextMenus.create({ title: $2, id: $1 });
-        resolve('SUCCESS');
-      });
-    )",
-                                                       id, title));
-}
-
-const content::EvalJsResult UpdateContextMenuItemTitle(
-    content::RenderFrameHost* app_frame,
-    const std::string& id,
-    const std::string& new_title) {
-  return content::EvalJs(app_frame, content::JsReplace(R"(
-      new Promise(async (resolve, reject) => {
-        const frame = document.getElementsByTagName('controlledframe')[0];
-        if (!frame || !frame.contextMenus || !frame.contextMenus.update) {
-          reject('FAIL: frame, frame.contextMenus, or ' +
-              'frame.contextMenus.update is undefined');
-          return;
-        }
-
-        await frame.contextMenus.update(/*id=*/$1, { title: $2 });
-        resolve('SUCCESS');
-      });
-  )",
-                                                       id, new_title));
-}
-
-const content::EvalJsResult RemoveContextMenuItem(
-    content::RenderFrameHost* app_frame,
-    const std::string& id) {
-  return content::EvalJs(app_frame, content::JsReplace(R"(
-      new Promise(async (resolve, reject) => {
-        const frame = document.getElementsByTagName('controlledframe')[0];
-        if (!frame || !frame.contextMenus || !frame.contextMenus.remove) {
-          reject('FAIL: frame, frame.contextMenus, or ' +
-              'frame.contextMenus.remove is undefined');
-          return;
-        }
-
-        await frame.contextMenus.remove(/*id=*/$1);
-        resolve('SUCCESS');
-      });
-  )",
-                                                       id));
-}
-
-const content::EvalJsResult RemoveAllContextMenuItems(
-    content::RenderFrameHost* app_frame) {
-  return content::EvalJs(app_frame, R"(
-      new Promise(async (resolve, reject) => {
-        const frame = document.getElementsByTagName('controlledframe')[0];
-        if (!frame || !frame.contextMenus || !frame.contextMenus.removeAll) {
-          reject('FAIL: frame, frame.contextMenus, or ' +
-              'frame.contextMenus.removeAll is undefined');
-          return;
-        }
-
-        await frame.contextMenus.removeAll();
-        resolve('SUCCESS');
-      });
-  )");
-}
-
 const content::EvalJsResult SetBackgroundColorToWhite(
     extensions::WebViewGuest* guest) {
   return content::EvalJs(guest->GetGuestMainFrame(), R"(
@@ -238,145 +154,8 @@
     StartContentServer("web_apps/simple_isolated_app");
   }
 
-  void ExpectMenuItemWithIdAndTitle(
-      const extensions::MenuItem::ExtensionKey& extension_key,
-      const std::string& expected_id,
-      const std::string& expected_title) {
-    auto* menu_manager = extensions::MenuManager::Get(profile());
-    extensions::MenuItem* menu_item =
-        menu_manager->GetItemById(CreateMenuItemId(extension_key, expected_id));
-
-    ASSERT_TRUE(menu_item);
-    EXPECT_EQ(expected_title, menu_item->title());
-  }
 };
 
-IN_PROC_BROWSER_TEST_F(ControlledFrameApiTest, ContextMenusCreate) {
-  web_app::IsolatedWebAppUrlInfo url_info =
-      CreateAndInstallEmptyApp(web_app::ManifestBuilder());
-  content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
-
-  ASSERT_TRUE(CreateControlledFrame(
-      app_frame, embedded_https_test_server().GetURL("/index.html")));
-  extensions::WebViewGuest* web_view_guest = GetWebViewGuest(app_frame);
-  auto* menu_manager = extensions::MenuManager::Get(profile());
-
-  const extensions::MenuItem::ExtensionKey extension_key(
-      /*extension_id=*/"",
-      web_view_guest->owner_rfh()->GetProcess()->GetDeprecatedID(),
-      web_view_guest->owner_rfh()->GetRoutingID(),
-      web_view_guest->view_instance_id());
-  EXPECT_EQ(0u, menu_manager->MenuItemsSize(extension_key));
-
-  static constexpr std::string kItem1ID = "1";
-  static constexpr std::string kItem1Title = "Test";
-  EXPECT_EQ(kEvalSuccessStr,
-            CreateContextMenuItem(app_frame, kItem1ID, kItem1Title));
-  ASSERT_EQ(1u, menu_manager->MenuItemsSize(extension_key));
-  ExpectMenuItemWithIdAndTitle(extension_key, kItem1ID, kItem1Title);
-
-  static constexpr std::string kItem2ID = "2";
-  static constexpr std::string kItem2Title = "Test2";
-  EXPECT_EQ(kEvalSuccessStr,
-            CreateContextMenuItem(app_frame, kItem2ID, kItem2Title));
-  ASSERT_EQ(2u, menu_manager->MenuItemsSize(extension_key));
-  ExpectMenuItemWithIdAndTitle(extension_key, kItem2ID, kItem2Title);
-
-  static constexpr std::string kItem3ID = "3";
-  static constexpr std::string kItem3Title = "Test3";
-  EXPECT_EQ(kEvalSuccessStr,
-            CreateContextMenuItem(app_frame, kItem3ID, kItem3Title));
-  ASSERT_EQ(3u, menu_manager->MenuItemsSize(extension_key));
-  ExpectMenuItemWithIdAndTitle(extension_key, kItem3ID, kItem3Title);
-}
-
-IN_PROC_BROWSER_TEST_F(ControlledFrameApiTest, ContextMenusUpdate) {
-  web_app::IsolatedWebAppUrlInfo url_info =
-      CreateAndInstallEmptyApp(web_app::ManifestBuilder());
-  content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
-
-  ASSERT_TRUE(CreateControlledFrame(
-      app_frame, embedded_https_test_server().GetURL("/index.html")));
-  extensions::WebViewGuest* web_view_guest = GetWebViewGuest(app_frame);
-  auto* menu_manager = extensions::MenuManager::Get(profile());
-
-  static constexpr std::string kItem1ID = "1";
-  static constexpr std::string kItem1Title = "Test";
-  EXPECT_EQ(kEvalSuccessStr,
-            CreateContextMenuItem(app_frame, kItem1ID, kItem1Title));
-
-  const extensions::MenuItem::ExtensionKey extension_key(
-      /*extension_id=*/"",
-      web_view_guest->owner_rfh()->GetProcess()->GetDeprecatedID(),
-      web_view_guest->owner_rfh()->GetRoutingID(),
-      web_view_guest->view_instance_id());
-  ASSERT_EQ(1u, menu_manager->MenuItemsSize(extension_key));
-  ExpectMenuItemWithIdAndTitle(extension_key, kItem1ID, kItem1Title);
-
-  static constexpr std::string kItem1NewTitle = "Test1";
-  EXPECT_EQ(kEvalSuccessStr,
-            UpdateContextMenuItemTitle(app_frame, kItem1ID, kItem1NewTitle));
-
-  ASSERT_EQ(1u, menu_manager->MenuItemsSize(extension_key));
-  ExpectMenuItemWithIdAndTitle(extension_key, kItem1ID, kItem1NewTitle);
-}
-
-IN_PROC_BROWSER_TEST_F(ControlledFrameApiTest, ContextMenusRemove) {
-  web_app::IsolatedWebAppUrlInfo url_info =
-      CreateAndInstallEmptyApp(web_app::ManifestBuilder());
-  content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
-
-  ASSERT_TRUE(CreateControlledFrame(
-      app_frame, embedded_https_test_server().GetURL("/index.html")));
-  extensions::WebViewGuest* web_view_guest = GetWebViewGuest(app_frame);
-  auto* menu_manager = extensions::MenuManager::Get(profile());
-
-  static constexpr std::string kItem1ID = "1";
-  static constexpr std::string kItem1Title = "Test1";
-  EXPECT_EQ(kEvalSuccessStr,
-            CreateContextMenuItem(app_frame, kItem1ID, kItem1Title));
-  EXPECT_EQ(kEvalSuccessStr, CreateContextMenuItem(app_frame, /*id=*/"2",
-                                                   /*title=*/"Test2"));
-
-  EXPECT_EQ(kEvalSuccessStr, RemoveContextMenuItem(app_frame, kItem1ID));
-
-  const extensions::MenuItem::ExtensionKey extension_key(
-      /*extension_id=*/"",
-      web_view_guest->owner_rfh()->GetProcess()->GetDeprecatedID(),
-      web_view_guest->owner_rfh()->GetRoutingID(),
-      web_view_guest->view_instance_id());
-  ASSERT_EQ(1u, menu_manager->MenuItemsSize(extension_key));
-
-  extensions::MenuItem* deleted_item =
-      menu_manager->GetItemById(CreateMenuItemId(extension_key, kItem1ID));
-  EXPECT_FALSE(deleted_item);
-}
-
-IN_PROC_BROWSER_TEST_F(ControlledFrameApiTest, ContextMenusRemoveAll) {
-  web_app::IsolatedWebAppUrlInfo url_info =
-      CreateAndInstallEmptyApp(web_app::ManifestBuilder());
-  content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
-
-  ASSERT_TRUE(CreateControlledFrame(
-      app_frame, embedded_https_test_server().GetURL("/index.html")));
-  extensions::WebViewGuest* web_view_guest = GetWebViewGuest(app_frame);
-  auto* menu_manager = extensions::MenuManager::Get(profile());
-
-  EXPECT_EQ(kEvalSuccessStr, CreateContextMenuItem(app_frame, /*id=*/"1",
-                                                   /*title=*/"Test1"));
-  EXPECT_EQ(kEvalSuccessStr, CreateContextMenuItem(app_frame, /*id=*/"2",
-                                                   /*title=*/"Test2"));
-
-  EXPECT_EQ(kEvalSuccessStr, RemoveAllContextMenuItems(app_frame));
-
-  const extensions::MenuItem::ExtensionKey extension_key(
-      /*extension_id=*/"",
-      web_view_guest->owner_rfh()->GetProcess()->GetDeprecatedID(),
-      web_view_guest->owner_rfh()->GetRoutingID(),
-      web_view_guest->view_instance_id());
-  ASSERT_EQ(0u, menu_manager->MenuItemsSize(extension_key));
-}
-
 // This test checks if the Controlled Frame is able to intercept URL navigation
 // requests.
 IN_PROC_BROWSER_TEST_F(ControlledFrameApiTest, URLLoaderIsProxied) {
diff --git a/chrome/browser/controlled_frame/controlled_frame_contextmenus_browsertest.cc b/chrome/browser/controlled_frame/controlled_frame_contextmenus_browsertest.cc
new file mode 100644
index 0000000..f93a902
--- /dev/null
+++ b/chrome/browser/controlled_frame/controlled_frame_contextmenus_browsertest.cc
@@ -0,0 +1,372 @@
+// Copyright 2025 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 <string>
+
+#include "chrome/browser/controlled_frame/controlled_frame_test_base.h"
+#include "chrome/browser/extensions/menu_manager.h"
+#include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_url_info.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/context_menu_interceptor.h"
+#include "content/public/test/hit_test_region_observer.h"
+#include "extensions/browser/guest_view/web_view/web_view_guest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/input/web_input_event.h"
+#include "third_party/blink/public/common/input/web_mouse_event.h"
+
+namespace controlled_frame {
+
+constexpr char kEvalSuccessStr[] = "SUCCESS";
+
+class ControlledFrameContextMenusTest : public ControlledFrameTestBase {
+ public:
+  ControlledFrameContextMenusTest()
+      : ControlledFrameTestBase(
+            /*channel=*/version_info::Channel::STABLE,
+            /*feature_setting=*/FeatureSetting::ENABLED,
+            /*flag_setting=*/FlagSetting::CONTROLLED_FRAME) {}
+
+  void SetUpOnMainThread() override {
+    ControlledFrameTestBase::SetUpOnMainThread();
+    StartContentServer("web_apps/simple_isolated_app");
+  }
+
+  const extensions::MenuItem::Id CreateMenuItemId(
+      const extensions::MenuItem::ExtensionKey& extension_key,
+      const std::string& string_uid) {
+    extensions::MenuItem::Id id;
+    id.extension_key = extension_key;
+    id.string_uid = string_uid;
+    return id;
+  }
+
+  void ExpectMenuItemWithIdAndTitle(
+      const extensions::MenuItem::ExtensionKey& extension_key,
+      const std::string& expected_id,
+      const std::string& expected_title) {
+    auto* menu_manager = extensions::MenuManager::Get(profile());
+    extensions::MenuItem* menu_item =
+        menu_manager->GetItemById(CreateMenuItemId(extension_key, expected_id));
+
+    ASSERT_TRUE(menu_item);
+    EXPECT_EQ(expected_title, menu_item->title());
+  }
+
+  const content::EvalJsResult CreateContextMenuItem(
+      content::RenderFrameHost* app_frame,
+      const std::string& id,
+      const std::string& title) {
+    return content::EvalJs(app_frame, content::JsReplace(R"(
+      new Promise(async (resolve, reject) => {
+        const frame = document.getElementsByTagName('controlledframe')[0];
+        if (!frame || !frame.contextMenus || !frame.contextMenus.create) {
+          reject('FAIL: frame, frame.contextMenus, or ' +
+              'frame.contextMenus.create is undefined');
+          return;
+        }
+        await frame.contextMenus.create({ title: $2, id: $1 });
+        resolve('SUCCESS');
+      });
+    )",
+                                                         id, title));
+  }
+
+  const content::EvalJsResult UpdateContextMenuItemTitle(
+      content::RenderFrameHost* app_frame,
+      const std::string& id,
+      const std::string& new_title) {
+    return content::EvalJs(app_frame, content::JsReplace(R"(
+      new Promise(async (resolve, reject) => {
+        const frame = document.getElementsByTagName('controlledframe')[0];
+        if (!frame || !frame.contextMenus || !frame.contextMenus.update) {
+          reject('FAIL: frame, frame.contextMenus, or ' +
+              'frame.contextMenus.update is undefined');
+          return;
+        }
+
+        await frame.contextMenus.update(/*id=*/$1, { title: $2 });
+        resolve('SUCCESS');
+      });
+  )",
+                                                         id, new_title));
+  }
+
+  const content::EvalJsResult RemoveContextMenuItem(
+      content::RenderFrameHost* app_frame,
+      const std::string& id) {
+    return content::EvalJs(app_frame, content::JsReplace(R"(
+      new Promise(async (resolve, reject) => {
+        const frame = document.getElementsByTagName('controlledframe')[0];
+        if (!frame || !frame.contextMenus || !frame.contextMenus.remove) {
+          reject('FAIL: frame, frame.contextMenus, or ' +
+              'frame.contextMenus.remove is undefined');
+          return;
+        }
+
+        await frame.contextMenus.remove(/*id=*/$1);
+        resolve('SUCCESS');
+      });
+  )",
+                                                         id));
+  }
+
+  const content::EvalJsResult RemoveAllContextMenuItems(
+      content::RenderFrameHost* app_frame) {
+    return content::EvalJs(app_frame, R"(
+      new Promise(async (resolve, reject) => {
+        const frame = document.getElementsByTagName('controlledframe')[0];
+        if (!frame || !frame.contextMenus || !frame.contextMenus.removeAll) {
+          reject('FAIL: frame, frame.contextMenus, or ' +
+              'frame.contextMenus.removeAll is undefined');
+          return;
+        }
+
+        await frame.contextMenus.removeAll();
+        resolve('SUCCESS');
+      });
+  )");
+  }
+
+  void SimulateOpenContextMenu(content::RenderFrameHost* controlled_frame) {
+    CHECK(controlled_frame);
+    content::WaitForHitTestData(
+        content::WebContents::FromRenderFrameHost(controlled_frame));
+
+    auto context_menu_interceptor =
+        std::make_unique<content::ContextMenuInterceptor>(controlled_frame);
+    gfx::Point click_pos(1, 1);
+    content::SimulateMouseClickAt(
+        content::WebContents::FromRenderFrameHost(controlled_frame),
+        blink::WebInputEvent::kNoModifiers,
+        blink::WebMouseEvent::Button::kRight, click_pos);
+    context_menu_interceptor->Wait();
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(ControlledFrameContextMenusTest, Create) {
+  web_app::IsolatedWebAppUrlInfo url_info =
+      CreateAndInstallEmptyApp(web_app::ManifestBuilder());
+  content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
+
+  ASSERT_TRUE(CreateControlledFrame(
+      app_frame, embedded_https_test_server().GetURL("/index.html")));
+  extensions::WebViewGuest* web_view_guest = GetWebViewGuest(app_frame);
+  auto* menu_manager = extensions::MenuManager::Get(profile());
+
+  const extensions::MenuItem::ExtensionKey extension_key(
+      /*extension_id=*/"",
+      web_view_guest->owner_rfh()->GetProcess()->GetDeprecatedID(),
+      web_view_guest->owner_rfh()->GetRoutingID(),
+      web_view_guest->view_instance_id());
+  EXPECT_EQ(0u, menu_manager->MenuItemsSize(extension_key));
+
+  static constexpr std::string kItem1ID = "1";
+  static constexpr std::string kItem1Title = "Test";
+  EXPECT_EQ(kEvalSuccessStr,
+            CreateContextMenuItem(app_frame, kItem1ID, kItem1Title));
+  ASSERT_EQ(1u, menu_manager->MenuItemsSize(extension_key));
+  ExpectMenuItemWithIdAndTitle(extension_key, kItem1ID, kItem1Title);
+
+  static constexpr std::string kItem2ID = "2";
+  static constexpr std::string kItem2Title = "Test2";
+  EXPECT_EQ(kEvalSuccessStr,
+            CreateContextMenuItem(app_frame, kItem2ID, kItem2Title));
+  ASSERT_EQ(2u, menu_manager->MenuItemsSize(extension_key));
+  ExpectMenuItemWithIdAndTitle(extension_key, kItem2ID, kItem2Title);
+
+  static constexpr std::string kItem3ID = "3";
+  static constexpr std::string kItem3Title = "Test3";
+  EXPECT_EQ(kEvalSuccessStr,
+            CreateContextMenuItem(app_frame, kItem3ID, kItem3Title));
+  ASSERT_EQ(3u, menu_manager->MenuItemsSize(extension_key));
+  ExpectMenuItemWithIdAndTitle(extension_key, kItem3ID, kItem3Title);
+}
+
+IN_PROC_BROWSER_TEST_F(ControlledFrameContextMenusTest, Update) {
+  web_app::IsolatedWebAppUrlInfo url_info =
+      CreateAndInstallEmptyApp(web_app::ManifestBuilder());
+  content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
+
+  ASSERT_TRUE(CreateControlledFrame(
+      app_frame, embedded_https_test_server().GetURL("/index.html")));
+  extensions::WebViewGuest* web_view_guest = GetWebViewGuest(app_frame);
+  auto* menu_manager = extensions::MenuManager::Get(profile());
+
+  static constexpr std::string kItem1ID = "1";
+  static constexpr std::string kItem1Title = "Test";
+  EXPECT_EQ(kEvalSuccessStr,
+            CreateContextMenuItem(app_frame, kItem1ID, kItem1Title));
+
+  const extensions::MenuItem::ExtensionKey extension_key(
+      /*extension_id=*/"",
+      web_view_guest->owner_rfh()->GetProcess()->GetDeprecatedID(),
+      web_view_guest->owner_rfh()->GetRoutingID(),
+      web_view_guest->view_instance_id());
+  ASSERT_EQ(1u, menu_manager->MenuItemsSize(extension_key));
+  ExpectMenuItemWithIdAndTitle(extension_key, kItem1ID, kItem1Title);
+
+  static constexpr std::string kItem1NewTitle = "Test1";
+  EXPECT_EQ(kEvalSuccessStr,
+            UpdateContextMenuItemTitle(app_frame, kItem1ID, kItem1NewTitle));
+
+  ASSERT_EQ(1u, menu_manager->MenuItemsSize(extension_key));
+  ExpectMenuItemWithIdAndTitle(extension_key, kItem1ID, kItem1NewTitle);
+}
+
+IN_PROC_BROWSER_TEST_F(ControlledFrameContextMenusTest, Remove) {
+  web_app::IsolatedWebAppUrlInfo url_info =
+      CreateAndInstallEmptyApp(web_app::ManifestBuilder());
+  content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
+
+  ASSERT_TRUE(CreateControlledFrame(
+      app_frame, embedded_https_test_server().GetURL("/index.html")));
+  extensions::WebViewGuest* web_view_guest = GetWebViewGuest(app_frame);
+  auto* menu_manager = extensions::MenuManager::Get(profile());
+
+  static constexpr std::string kItem1ID = "1";
+  static constexpr std::string kItem1Title = "Test1";
+  EXPECT_EQ(kEvalSuccessStr,
+            CreateContextMenuItem(app_frame, kItem1ID, kItem1Title));
+  EXPECT_EQ(kEvalSuccessStr, CreateContextMenuItem(app_frame, /*id=*/"2",
+                                                   /*title=*/"Test2"));
+
+  EXPECT_EQ(kEvalSuccessStr, RemoveContextMenuItem(app_frame, kItem1ID));
+
+  const extensions::MenuItem::ExtensionKey extension_key(
+      /*extension_id=*/"",
+      web_view_guest->owner_rfh()->GetProcess()->GetDeprecatedID(),
+      web_view_guest->owner_rfh()->GetRoutingID(),
+      web_view_guest->view_instance_id());
+  ASSERT_EQ(1u, menu_manager->MenuItemsSize(extension_key));
+
+  extensions::MenuItem* deleted_item =
+      menu_manager->GetItemById(CreateMenuItemId(extension_key, kItem1ID));
+  EXPECT_FALSE(deleted_item);
+}
+
+IN_PROC_BROWSER_TEST_F(ControlledFrameContextMenusTest, RemoveAll) {
+  web_app::IsolatedWebAppUrlInfo url_info =
+      CreateAndInstallEmptyApp(web_app::ManifestBuilder());
+  content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
+
+  ASSERT_TRUE(CreateControlledFrame(
+      app_frame, embedded_https_test_server().GetURL("/index.html")));
+  extensions::WebViewGuest* web_view_guest = GetWebViewGuest(app_frame);
+  auto* menu_manager = extensions::MenuManager::Get(profile());
+
+  EXPECT_EQ(kEvalSuccessStr, CreateContextMenuItem(app_frame, /*id=*/"1",
+                                                   /*title=*/"Test1"));
+  EXPECT_EQ(kEvalSuccessStr, CreateContextMenuItem(app_frame, /*id=*/"2",
+                                                   /*title=*/"Test2"));
+
+  EXPECT_EQ(kEvalSuccessStr, RemoveAllContextMenuItems(app_frame));
+
+  const extensions::MenuItem::ExtensionKey extension_key(
+      /*extension_id=*/"",
+      web_view_guest->owner_rfh()->GetProcess()->GetDeprecatedID(),
+      web_view_guest->owner_rfh()->GetRoutingID(),
+      web_view_guest->view_instance_id());
+  ASSERT_EQ(0u, menu_manager->MenuItemsSize(extension_key));
+}
+
+IN_PROC_BROWSER_TEST_F(ControlledFrameContextMenusTest, OnShow) {
+  web_app::IsolatedWebAppUrlInfo url_info =
+      CreateAndInstallEmptyApp(web_app::ManifestBuilder());
+  content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
+
+  ASSERT_TRUE(CreateControlledFrame(
+      app_frame, embedded_https_test_server().GetURL("/index.html")));
+
+  auto add_handler_script = content::JsReplace(
+      R"(
+document.onShowHandler = function() {
+  document.onShowCount = (document.onShowCount ?? 0) + 1;
+};
+
+(function() {
+  const frame = document.getElementsByTagName('controlledframe')[0];
+  if (!frame || !frame.contextMenus) {
+    return ('FAIL: frame or frame.contextMenus is undefined');
+  }
+
+  if (frame.contextMenus.onShow.hasListeners()) {
+    return 'FAIL: frame.contextMenus.onShow.hasListeners() \
+    returns true before addListener().';
+  }
+
+  frame.contextMenus.onShow.addListener(document.onShowHandler);
+
+  if (!frame.contextMenus.onShow.hasListeners()) {
+    return 'FAIL: frame.contextMenus.hasListeners() \
+    returns false after addListener().';
+  }
+
+  if (!frame.contextMenus.onShow.hasListener(document.onShowHandler)) {
+    return 'FAIL: frame.contextMenus.onShow.hasListener() \
+    returns false after addListener().';
+  }
+
+  return $1;
+})();
+)",
+      kEvalSuccessStr);
+
+  // Add a listener for 'onShow' then simulate right click and expect the
+  // listener to be triggered.
+  ASSERT_EQ(content::EvalJs(app_frame, add_handler_script), kEvalSuccessStr);
+
+  extensions::WebViewGuest* web_view_guest = GetWebViewGuest(app_frame);
+  ASSERT_TRUE(web_view_guest);
+  content::RenderFrameHost* controlled_frame =
+      web_view_guest->GetGuestMainFrame();
+  ASSERT_TRUE(controlled_frame);
+  SimulateOpenContextMenu(controlled_frame);
+  ASSERT_EQ(content::EvalJs(app_frame,
+                            "(function(){return document.onShowCount;})();"),
+            1);
+
+  auto remove_handler_script = content::JsReplace(
+      R"(
+(function() {
+  const frame = document.getElementsByTagName('controlledframe')[0];
+  if (!frame || !frame.contextMenus) {
+    return ('FAIL: frame or frame.contextMenus is undefined');
+  }
+
+  if (!frame.contextMenus.onShow.hasListeners()) {
+    return 'FAIL: frame.contextMenus.onShow.hasListeners() \
+    returns false before removeListener().';
+  }
+
+  frame.contextMenus.onShow.removeListener(document.onShowHandler);
+
+  if (frame.contextMenus.onShow.hasListeners()) {
+    return 'FAIL: frame.contextMenus.hasListeners() \
+    returns true after removeListener().';
+  }
+
+  if (frame.contextMenus.onShow.hasListener(document.onShowHandler)) {
+    return 'FAIL: frame.contextMenus.onShow.hasListener() \
+    returns true after removeListener().';
+  }
+
+  return $1;
+})();
+)",
+      kEvalSuccessStr);
+
+  // Remove the listener for 'onShow' then simulate right click and expect the
+  // listener to not be triggered.
+  ASSERT_EQ(content::EvalJs(app_frame, remove_handler_script), kEvalSuccessStr);
+
+  SimulateOpenContextMenu(controlled_frame);
+  ASSERT_EQ(content::EvalJs(app_frame,
+                            "(function(){return document.onShowCount;})();"),
+            1);
+}
+}  // namespace controlled_frame
diff --git a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java
index 5e4b7cb..d175865 100644
--- a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java
+++ b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorCoordinator.java
@@ -396,10 +396,7 @@
     private void initBottomSheet() {
         mScrim =
                 new ScrimCoordinator(
-                        mActivity,
-                        /* systemUiScrimDelegate= */ null,
-                        mCreatorViewGroup,
-                        mActivity.getColor(R.color.default_scrim_color));
+                        mActivity, /* systemUiScrimDelegate= */ null, mCreatorViewGroup);
 
         mBottomSheetContainer = new FrameLayout(mActivity);
         mBottomSheetContainer.setId(R.id.creator_content_preview_bottom_sheet);
@@ -657,6 +654,11 @@
                     mContext.getResources().getDimensionPixelSize(R.dimen.preview_tab_favicon_size);
         }
 
+        /** Destroys the native favicon helper. */
+        public void destroy() {
+            mFaviconHelper.destroy();
+        }
+
         /**
          * Generates a favicon for a given URL. If no favicon was could be found or generated from
          * the URL, a default favicon will be shown.
diff --git a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorTabMediator.java b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorTabMediator.java
index fadbff1..1c41cc6 100644
--- a/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorTabMediator.java
+++ b/chrome/browser/creator/android/java/src/org/chromium/chrome/browser/creator/CreatorTabMediator.java
@@ -224,12 +224,13 @@
     /** Destroys the objects used for the current preview tab. */
     void destroyContent() {
         if (mWebContentsObserver != null) {
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
             mWebContentsObserver = null;
         }
         mWebContentsDelegate = null;
         mWebContents = null;
         mSheetContent = null;
         mProfile = null;
+        mFaviconLoader.destroy();
     }
 }
diff --git a/chrome/browser/data_sharing/BUILD.gn b/chrome/browser/data_sharing/BUILD.gn
index 28148d1..c7dae62 100644
--- a/chrome/browser/data_sharing/BUILD.gn
+++ b/chrome/browser/data_sharing/BUILD.gn
@@ -100,6 +100,7 @@
       "//components/data_sharing/public/protocol:proto_java",
       "//components/embedder_support/android:util_java",
       "//components/saved_tab_groups/public:java",
+      "//components/strings:components_strings_grd",
       "//content/public/android:content_full_java",
       "//third_party/android_sdk:android_window_extensions_java",
       "//ui/android:ui_java",
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
index bcb141bf..691fcd66 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManager.java
@@ -88,6 +88,9 @@
     private static final String LEARN_ABOUT_BLOCKED_ACCOUNTS_URL =
             "https://support.google.com/chrome/?p=chrome_collaboration";
 
+    // Separator for description and link in share sheet.
+    private static final String SHARED_TEXT_SEPARATOR = "\n";
+
     private final ObservableSupplier<TabModelSelector> mTabModelSelectorSupplier;
     private final DataSharingTabGroupsDelegate mDataSharingTabGroupsDelegate;
     private final Supplier<BottomSheetController> mBottomSheetControllerSupplier;
@@ -394,6 +397,7 @@
                 || previewData.sharedDataPreview.sharedTabGroupPreview == null) {
             DataSharingMetrics.recordJoinActionFlowState(
                     DataSharingMetrics.JoinActionStateAndroid.PREVIEW_PERMISSION_DENIED);
+
             showInvitationFailureDialog();
             return;
         }
@@ -577,6 +581,22 @@
     }
 
     /**
+     * Open and focus on the tab group.
+     *
+     * @param collaborationId The collaboration id of the shared tab group.
+     */
+    public void promoteTabGroup(String collaborationId) {
+        TabGroupSyncService tabGroupSyncService =
+                TabGroupSyncServiceFactory.getForProfile(mProfile);
+        SavedTabGroup existingGroup =
+                DataSharingTabGroupUtils.getTabGroupForCollabIdFromSync(
+                        collaborationId, tabGroupSyncService);
+        assert existingGroup != null;
+
+        onSavedTabGroupAvailable(existingGroup);
+    }
+
+    /**
      * Called when a saved tab group is available.
      *
      * @param group The SavedTabGroup that became available.
@@ -714,10 +734,12 @@
                         createGroupFinishedCallback.onResult(true);
                         DataSharingMetrics.recordShareActionFlowState(
                                 DataSharingMetrics.ShareActionStateAndroid.GROUP_CREATE_SUCCESS);
+                        // Consider using an utility to convert result to GroupData.
                         showShareSheet(
+                                activity,
                                 new GroupData(
                                         result.getGroupId(),
-                                        tabGroupDisplayName,
+                                        /* displayName= */ null,
                                         /* members= */ null,
                                         result.getAccessToken()),
                                 onCreateFinished);
@@ -750,17 +772,26 @@
                 /* maxFaviconsToFetch= */ 4);
     }
 
-    private void showShareSheet(GroupData groupData, Callback<Boolean> onShareSheetShown) {
+    private void showShareSheet(
+            Context context, GroupData groupData, Callback<Boolean> onShareSheetShown) {
         mDataSharingTabGroupsDelegate.getPreviewBitmap(
                 groupData.groupToken.collaborationId,
                 ShareHelper.getTextPreviewImageSizePx(mResources),
                 (preview) -> {
-                    showShareSheetWithPreview(groupData, preview, onShareSheetShown);
+                    showShareSheetWithPreview(context, groupData, preview, onShareSheetShown);
                 });
     }
 
     private void showShareSheetWithPreview(
-            GroupData groupData, Bitmap preview, Callback<Boolean> onShareSheetShown) {
+            Context context,
+            GroupData groupData,
+            Bitmap preview,
+            Callback<Boolean> onShareSheetShown) {
+        TabGroupSyncService tabGroupSyncService =
+                TabGroupSyncServiceFactory.getForProfile(mProfile);
+        SavedTabGroup tabGroup =
+                DataSharingTabGroupUtils.getTabGroupForCollabIdFromSync(
+                        groupData.groupToken.collaborationId, tabGroupSyncService);
         GURL url = mDataSharingService.getDataSharingUrl(groupData);
         if (url == null) {
             // TODO(ritikagup) : Show error dialog showing fetching URL failed. Contact owner for
@@ -777,8 +808,26 @@
                         .setDetailedContentType(DetailedContentType.TAB_GROUP_LINK)
                         .build();
 
+        String tabGroupName = null;
+        // TODO(ssid): The tab group should not be null, if we wait for makeTabGroupShared() to
+        // finish. Remove this check when its integrated.
+        if (tabGroup != null) {
+            tabGroupName = tabGroup.title;
+        }
+        if (TextUtils.isEmpty(tabGroupName)) {
+            tabGroupName =
+                    context.getString(R.string.collaboration_share_sheet_tab_group_fallback_name);
+        }
+        // TODO(ssid): Share delegate adds another separator, fix the formatting.
+        String text =
+                context.getString(R.string.collaboration_share_sheet_message, tabGroupName)
+                        + SHARED_TEXT_SEPARATOR;
         ShareParams.Builder shareParamsBuilder =
-                new ShareParams.Builder(mWindowAndroid, groupData.displayName, url.getSpec());
+                new ShareParams.Builder(
+                                mWindowAndroid,
+                                context.getString(R.string.collaboration_share_sheet_title),
+                                url.getSpec())
+                        .setText(text);
 
         if (preview != null) {
             shareParamsBuilder.setPreviewImageBitmap(preview);
@@ -853,10 +902,12 @@
                     @Override
                     public void onShareInviteLinkClickedWithWait(
                             GroupToken groupToken, Callback<Boolean> onFinished) {
+                        // Consider pass GroupData from the UI.
                         showShareSheet(
+                                activity,
                                 new GroupData(
                                         groupToken.collaborationId,
-                                        tabGroupName,
+                                        /* displayName= */ null,
                                         /* members= */ null,
                                         groupToken.accessToken),
                                 onFinished);
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManagerUnitTest.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManagerUnitTest.java
index ac35a6b..eeb328af7 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManagerUnitTest.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/DataSharingTabManagerUnitTest.java
@@ -468,6 +468,10 @@
                         mShareParamsCaptor.capture(),
                         any(),
                         eq(ShareDelegate.ShareOrigin.TAB_GROUP));
+        ShareParams shareParams = mShareParamsCaptor.getValue();
+        assertEquals(shareParams.getText(), "Shared tab group link expires in 48 hours\n");
+        assertEquals(shareParams.getUrl(), TEST_URL.getSpec());
+        assertEquals(shareParams.getTitle(), "Collaborate on tab group");
     }
 
     @Test
@@ -504,6 +508,11 @@
 
         DataSharingCreateUiConfig uiConfig = uiConfigCaptor.getValue();
 
+        mSavedTabGroup.collaborationId = COLLABORATION_ID1;
+        mSavedTabGroup.title = "test title";
+        doReturn(mSavedTabGroup).when(mTabGroupSyncService).getGroup(SYNC_GROUP_ID1);
+        doReturn(new String[] {SYNC_GROUP_ID1}).when(mTabGroupSyncService).getAllGroupIds();
+
         assertNotNull(uiConfig.getCreateCallback());
         uiConfig.getCreateCallback()
                 .onGroupCreatedWithWait(
@@ -520,7 +529,15 @@
 
         // Verifying DataSharingService createGroup API is called.
         verify(mTabGroupSyncService).makeTabGroupShared(LOCAL_ID, COLLABORATION_ID1);
-        verify(mShareDelegate).share(any(), any(), eq(ShareDelegate.ShareOrigin.TAB_GROUP));
+        verify(mShareDelegate)
+                .share(
+                        mShareParamsCaptor.capture(),
+                        any(),
+                        eq(ShareDelegate.ShareOrigin.TAB_GROUP));
+        ShareParams shareParams = mShareParamsCaptor.getValue();
+        assertEquals(shareParams.getText(), "test title link expires in 48 hours\n");
+        assertEquals(shareParams.getUrl(), TEST_URL.getSpec());
+        assertEquals(shareParams.getTitle(), "Collaborate on tab group");
     }
 
     @Test
@@ -639,6 +656,16 @@
         verify(mMessagingBackendService).getActivityLog(any());
     }
 
+    @Test
+    public void testPromoteTabGroup() {
+        when(mProfile.getOriginalProfile()).thenReturn(mProfile);
+
+        doReturn(mSavedTabGroup).when(mTabGroupSyncService).getGroup(SYNC_GROUP_ID1);
+
+        mDataSharingTabManager.promoteTabGroup(COLLABORATION_ID1);
+        verify(mDataSharingTabGroupsDelegate).openTabGroupWithTabId(TAB_ID);
+    }
+
     private void setupActivityLogItemsOnTheBackend() {
         List<ActivityLogItem> logItems = new ArrayList<>();
 
diff --git a/chrome/browser/data_sharing/data_sharing_navigation_throttle.cc b/chrome/browser/data_sharing/data_sharing_navigation_throttle.cc
index 160ef9d..ff928b7c 100644
--- a/chrome/browser/data_sharing/data_sharing_navigation_throttle.cc
+++ b/chrome/browser/data_sharing/data_sharing_navigation_throttle.cc
@@ -20,6 +20,13 @@
     return false;
   }
 
+  // If this is a session or tab restore, don't intercept the
+  // navigation to avoid showing the dialog on each browser
+  // start.
+  if (navigation_handle->GetRestoreType() == content::RestoreType::kRestored) {
+    return false;
+  }
+
   if (navigation_handle->IsRendererInitiated()) {
     if (navigation_handle->HasUserGesture()) {
       return true;
diff --git a/chrome/browser/dbus_memory_pressure_evaluator_linux.cc b/chrome/browser/dbus_memory_pressure_evaluator_linux.cc
index 6f7d8f9d..193b4d8 100644
--- a/chrome/browser/dbus_memory_pressure_evaluator_linux.cc
+++ b/chrome/browser/dbus_memory_pressure_evaluator_linux.cc
@@ -18,18 +18,6 @@
 #include "dbus/object_path.h"
 #include "dbus/object_proxy.h"
 
-namespace {
-
-scoped_refptr<dbus::Bus> CreateBusOfType(dbus::Bus::BusType type) {
-  dbus::Bus::Options options;
-  options.bus_type = type;
-  options.connection_type = dbus::Bus::PRIVATE;
-  options.dbus_task_runner = dbus_thread_linux::GetTaskRunner();
-  return base::MakeRefCounted<dbus::Bus>(options);
-}
-
-}  // namespace
-
 const char DbusMemoryPressureEvaluatorLinux::kLmmService[] =
     "org.freedesktop.LowMemoryMonitor";
 const char DbusMemoryPressureEvaluatorLinux::kLmmObject[] =
@@ -89,8 +77,9 @@
 void DbusMemoryPressureEvaluatorLinux::CheckIfLmmIsAvailable() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!system_bus_)
-    system_bus_ = CreateBusOfType(dbus::Bus::SYSTEM);
+  if (!system_bus_) {
+    system_bus_ = dbus_thread_linux::GetSharedSystemBus();
+  }
 
   dbus_utils::CheckForServiceAndStart(
       system_bus_, kLmmService,
@@ -118,7 +107,7 @@
   } else {
     VLOG(1) << "LMM is not available, checking for portal";
 
-    ResetBus(system_bus_);
+    system_bus_.reset();
     CheckIfPortalIsAvailable();
   }
 }
@@ -126,8 +115,9 @@
 void DbusMemoryPressureEvaluatorLinux::CheckIfPortalIsAvailable() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  if (!session_bus_)
-    session_bus_ = CreateBusOfType(dbus::Bus::SESSION);
+  if (!session_bus_) {
+    session_bus_ = dbus_thread_linux::GetSharedSessionBus();
+  }
 
   dbus_utils::CheckForServiceAndStart(
       session_bus_, kXdgPortalService,
@@ -156,19 +146,10 @@
   } else {
     VLOG(1) << "No memory monitor found";
 
-    ResetBus(session_bus_);
+    session_bus_.reset();
   }
 }
 
-void DbusMemoryPressureEvaluatorLinux::ResetBus(scoped_refptr<dbus::Bus>& bus) {
-  if (!bus)
-    return;
-
-  bus->GetDBusTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, bus));
-  bus.reset();
-}
-
 void DbusMemoryPressureEvaluatorLinux::OnSignalConnected(
     const std::string& interface,
     const std::string& signal,
@@ -178,8 +159,8 @@
   if (!connected) {
     LOG(WARNING) << "Failed to connect to " << interface << '.' << signal;
 
-    ResetBus(system_bus_);
-    ResetBus(session_bus_);
+    system_bus_.reset();
+    session_bus_.reset();
   }
 }
 
@@ -209,10 +190,12 @@
 
 base::MemoryPressureListener::MemoryPressureLevel
 DbusMemoryPressureEvaluatorLinux::LmmToBasePressureLevel(uint8_t lmm_level) {
-  if (lmm_level >= critical_level_)
+  if (lmm_level >= critical_level_) {
     return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL;
-  if (lmm_level >= moderate_level_)
+  }
+  if (lmm_level >= moderate_level_) {
     return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE;
+  }
   return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
 }
 
diff --git a/chrome/browser/dbus_memory_pressure_evaluator_linux.h b/chrome/browser/dbus_memory_pressure_evaluator_linux.h
index 53ce09b..c14325a 100644
--- a/chrome/browser/dbus_memory_pressure_evaluator_linux.h
+++ b/chrome/browser/dbus_memory_pressure_evaluator_linux.h
@@ -86,9 +86,6 @@
   // Handles the availability response from above.
   void CheckIfPortalIsAvailableResponse(std::optional<bool> is_available);
 
-  // Shuts down the given bus on the D-Bus thread and clears the pointer.
-  void ResetBus(scoped_refptr<dbus::Bus>& bus);
-
   void OnSignalConnected(const std::string& interface,
                          const std::string& signal,
                          bool connected);
diff --git a/chrome/browser/device_notifications/device_status_icon_renderer.cc b/chrome/browser/device_notifications/device_status_icon_renderer.cc
index 91a89e3..0ac84bc6 100644
--- a/chrome/browser/device_notifications/device_status_icon_renderer.cc
+++ b/chrome/browser/device_notifications/device_status_icon_renderer.cc
@@ -67,7 +67,10 @@
   if (status_icon_) {
     auto* status_tray = g_browser_process->status_tray();
     DCHECK(status_tray);
-    status_tray->RemoveStatusIcon(status_icon_);
+    std::unique_ptr<StatusIcon> removed_icon =
+        status_tray->RemoveStatusIcon(status_icon_);
+    status_icon_ = nullptr;
+    removed_icon.reset();
   }
 }
 
@@ -136,8 +139,10 @@
   DCHECK(status_tray);
   if (device_system_tray_icon_->profiles().empty()) {
     if (status_icon_) {
-      status_tray->RemoveStatusIcon(status_icon_);
+      std::unique_ptr<StatusIcon> removed_icon =
+          status_tray->RemoveStatusIcon(status_icon_);
       status_icon_ = nullptr;
+      removed_icon.reset();
     }
     return;
   }
diff --git a/chrome/browser/device_notifications/device_status_icon_renderer.h b/chrome/browser/device_notifications/device_status_icon_renderer.h
index ba9c61d..d3a688c5 100644
--- a/chrome/browser/device_notifications/device_status_icon_renderer.h
+++ b/chrome/browser/device_notifications/device_status_icon_renderer.h
@@ -62,7 +62,7 @@
   int about_device_message_id_;
 
   // Reference to our status icon (if any) - owned by the StatusTray.
-  raw_ptr<StatusIcon, DanglingUntriaged> status_icon_ = nullptr;
+  raw_ptr<StatusIcon> status_icon_ = nullptr;
 
   // The mapping of clickable system tray icon items to their click handlers
   std::vector<base::RepeatingClosure> command_id_callbacks_;
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
index fab87ae5..232931c 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc
@@ -683,12 +683,6 @@
 
 ExtensionFunction::ResponseAction
 EnterpriseReportingPrivateGetFileSystemInfoFunction::Run() {
-  if (!IsNewFunctionEnabled(
-          enterprise_signals::features::NewEvFunction::kFileSystemInfo)) {
-    return RespondNow(Error(device_signals::ErrorToString(
-        device_signals::SignalCollectionError::kUnsupported)));
-  }
-
   std::optional<api::enterprise_reporting_private::GetFileSystemInfo::Params>
       params =
           api::enterprise_reporting_private::GetFileSystemInfo::Params::Create(
@@ -763,12 +757,6 @@
 
 ExtensionFunction::ResponseAction
 EnterpriseReportingPrivateGetSettingsFunction::Run() {
-  if (!IsNewFunctionEnabled(
-          enterprise_signals::features::NewEvFunction::kSettings)) {
-    return RespondNow(Error(device_signals::ErrorToString(
-        device_signals::SignalCollectionError::kUnsupported)));
-  }
-
   std::optional<api::enterprise_reporting_private::GetSettings::Params> params =
       api::enterprise_reporting_private::GetSettings::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(params);
@@ -840,12 +828,6 @@
 
 ExtensionFunction::ResponseAction
 EnterpriseReportingPrivateGetAvInfoFunction::Run() {
-  if (!IsNewFunctionEnabled(
-          enterprise_signals::features::NewEvFunction::kAntiVirus)) {
-    return RespondNow(Error(device_signals::ErrorToString(
-        device_signals::SignalCollectionError::kUnsupported)));
-  }
-
   std::optional<api::enterprise_reporting_private::GetAvInfo::Params> params =
       api::enterprise_reporting_private::GetAvInfo::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(params);
@@ -892,12 +874,6 @@
 
 ExtensionFunction::ResponseAction
 EnterpriseReportingPrivateGetHotfixesFunction::Run() {
-  if (!IsNewFunctionEnabled(
-          enterprise_signals::features::NewEvFunction::kHotfix)) {
-    return RespondNow(Error(device_signals::ErrorToString(
-        device_signals::SignalCollectionError::kUnsupported)));
-  }
-
   std::optional<api::enterprise_reporting_private::GetHotfixes::Params> params =
       api::enterprise_reporting_private::GetHotfixes::Params::Create(args());
   EXTENSION_FUNCTION_VALIDATE(params);
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc
index 03948c9..be1152b 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_apitest.cc
@@ -126,7 +126,6 @@
         {
             extensions_features::
                 kApiEnterpriseReportingPrivateReportDataMaskingEvent,
-            enterprise_signals::features::kNewEvSignalsEnabled,
         },
         /*disabled_features=*/{});
 #else
diff --git a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
index d239705f..1fc67db 100644
--- a/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
+++ b/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc
@@ -62,7 +62,6 @@
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 #include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/enterprise/signals/signals_aggregator_factory.h"
 #include "components/device_signals/core/browser/mock_signals_aggregator.h"  // nogncheck
 #include "components/device_signals/core/browser/signals_aggregator.h"  // nogncheck
@@ -70,7 +69,6 @@
 #include "components/device_signals/core/browser/user_context.h"   // nogncheck
 #include "components/device_signals/core/common/common_types.h"    // nogncheck
 #include "components/device_signals/core/common/signals_constants.h"  // nogncheck
-#include "components/device_signals/core/common/signals_features.h"  // nogncheck
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
@@ -1320,14 +1318,9 @@
             }));
   }
 
-  virtual void SetFeatureFlag() {
-    scoped_features_.InitAndEnableFeature(
-        enterprise_signals::features::kNewEvSignalsEnabled);
-  }
 
   raw_ptr<device_signals::MockSignalsAggregator, DanglingUntriaged>
       mock_aggregator_;
-  base::test::ScopedFeatureList scoped_features_;
   base::HistogramTester histogram_tester_;
 };
 
@@ -1338,8 +1331,6 @@
   void SetUp() override {
     UserContextGatedTest::SetUp();
 
-    SetFeatureFlag();
-
     function_ = base::MakeRefCounted<
         EnterpriseReportingPrivateGetFileSystemInfoFunction>();
   }
@@ -1497,27 +1488,6 @@
       "Enterprise.DeviceSignals.Collection.FileSystemInfo.Delta", 0);
 }
 
-class EnterpriseReportingPrivateGetFileSystemInfoDisabledTest
-    : public EnterpriseReportingPrivateGetFileSystemInfoTest {
- protected:
-  // Overwrite this function to disable the feature flag for tests using this
-  // specific fixture.
-  void SetFeatureFlag() override {
-    scoped_features_.InitAndEnableFeatureWithParameters(
-        enterprise_signals::features::kNewEvSignalsEnabled,
-        {{"DisableFileSystemInfo", "true"}});
-  }
-};
-
-TEST_F(EnterpriseReportingPrivateGetFileSystemInfoDisabledTest,
-       FlagDisabled_Test) {
-  auto error = api_test_utils::RunFunctionAndReturnError(
-      function_.get(), GetFakeRequest(), profile());
-  EXPECT_EQ(error, function_->GetError());
-  EXPECT_EQ(error, device_signals::ErrorToString(
-                       device_signals::SignalCollectionError::kUnsupported));
-}
-
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
@@ -1528,8 +1498,6 @@
   void SetUp() override {
     UserContextGatedTest::SetUp();
 
-    SetFeatureFlag();
-
     function_ =
         base::MakeRefCounted<EnterpriseReportingPrivateGetSettingsFunction>();
   }
@@ -1700,26 +1668,6 @@
       "Enterprise.DeviceSignals.Collection.SystemSettings.Delta", 0);
 }
 
-class EnterpriseReportingPrivateGetSettingsDisabledTest
-    : public EnterpriseReportingPrivateGetSettingsTest {
- protected:
-  // Overwrite this function to disable the feature flag for tests using this
-  // specific fixture.
-  void SetFeatureFlag() override {
-    scoped_features_.InitAndEnableFeatureWithParameters(
-        enterprise_signals::features::kNewEvSignalsEnabled,
-        {{"DisableSettings", "true"}});
-  }
-};
-
-TEST_F(EnterpriseReportingPrivateGetSettingsDisabledTest, FlagDisabled_Test) {
-  auto error = api_test_utils::RunFunctionAndReturnError(
-      function_.get(), GetFakeRequest(), profile());
-  EXPECT_EQ(error, function_->GetError());
-  EXPECT_EQ(error, device_signals::ErrorToString(
-                       device_signals::SignalCollectionError::kUnsupported));
-}
-
 #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC)
 
 #if BUILDFLAG(IS_WIN)
@@ -1739,8 +1687,6 @@
   void SetUp() override {
     UserContextGatedTest::SetUp();
 
-    SetFeatureFlag();
-
     function_ =
         base::MakeRefCounted<EnterpriseReportingPrivateGetAvInfoFunction>();
   }
@@ -1865,25 +1811,6 @@
       "Enterprise.DeviceSignals.Collection.Success.AntiVirus.Latency", 0);
 }
 
-class EnterpriseReportingPrivateGetAvInfoDisabledTest
-    : public EnterpriseReportingPrivateGetAvInfoTest {
- protected:
-  // Overwrite this function to disable the feature flag for tests using this
-  // specific fixture.
-  void SetFeatureFlag() override {
-    scoped_features_.InitAndEnableFeatureWithParameters(
-        enterprise_signals::features::kNewEvSignalsEnabled,
-        {{"DisableAntiVirus", "true"}});
-  }
-};
-
-TEST_F(EnterpriseReportingPrivateGetAvInfoDisabledTest, FlagDisabled_Test) {
-  auto error = api_test_utils::RunFunctionAndReturnError(
-      function_.get(), GetFakeUserContextJsonParams(), profile());
-  EXPECT_EQ(error, function_->GetError());
-  EXPECT_EQ(error, device_signals::ErrorToString(
-                       device_signals::SignalCollectionError::kUnsupported));
-}
 
 // Tests for API enterprise.reportingPrivate.getHotfixes
 class EnterpriseReportingPrivateGetHotfixesTest : public UserContextGatedTest {
@@ -1891,8 +1818,6 @@
   void SetUp() override {
     UserContextGatedTest::SetUp();
 
-    SetFeatureFlag();
-
     function_ =
         base::MakeRefCounted<EnterpriseReportingPrivateGetHotfixesFunction>();
   }
@@ -2009,27 +1934,6 @@
       "Enterprise.DeviceSignals.Collection.Success.Hotfixes.Latency", 0);
 }
 
-class EnterpriseReportingPrivateGetHotfixesInfoDisabledTest
-    : public EnterpriseReportingPrivateGetHotfixesTest {
- protected:
-  // Overwrite this function to disable the feature flag for tests using this
-  // specific fixture.
-  void SetFeatureFlag() override {
-    scoped_features_.InitAndEnableFeatureWithParameters(
-        enterprise_signals::features::kNewEvSignalsEnabled,
-        {{"DisableHotfix", "true"}});
-  }
-};
-
-TEST_F(EnterpriseReportingPrivateGetHotfixesInfoDisabledTest,
-       FlagDisabled_Test) {
-  auto error = api_test_utils::RunFunctionAndReturnError(
-      function_.get(), GetFakeUserContextJsonParams(), profile());
-  EXPECT_EQ(error, function_->GetError());
-  EXPECT_EQ(error, device_signals::ErrorToString(
-                       device_signals::SignalCollectionError::kUnsupported));
-}
-
 #endif  // BUILDFLAG(IS_WIN)
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_win.cc b/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_win.cc
index a05dac5..efd9e139 100644
--- a/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_win.cc
+++ b/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_win.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 // devguid requires Windows.h be imported first.
 #include "chrome/browser/extensions/api/image_writer_private/removable_storage_provider.h"
 
@@ -171,13 +166,13 @@
 
   if (device_descriptor->VendorIdOffset &&
       output_buf[device_descriptor->VendorIdOffset]) {
-    device.vendor.assign(output_buf.data() + device_descriptor->VendorIdOffset);
+    device.vendor.assign(&output_buf[device_descriptor->VendorIdOffset]);
   }
 
   std::string product_id;
   if (device_descriptor->ProductIdOffset &&
       output_buf[device_descriptor->ProductIdOffset]) {
-    device.model.assign(output_buf.data() + device_descriptor->ProductIdOffset);
+    device.model.assign(&output_buf[device_descriptor->ProductIdOffset]);
   }
 
   device_list->data.push_back(std::move(device));
diff --git a/chrome/browser/extensions/api/messaging/native_message_built_in_host.cc b/chrome/browser/extensions/api/messaging/native_message_built_in_host.cc
index afc16be..b305d95 100644
--- a/chrome/browser/extensions/api/messaging/native_message_built_in_host.cc
+++ b/chrome/browser/extensions/api/messaging/native_message_built_in_host.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "chrome/browser/extensions/api/messaging/native_message_built_in_host.h"
 
 #include <string>
@@ -26,8 +21,8 @@
 bool MatchesSecurityOrigin(const NativeMessageBuiltInHost& host,
                            const ExtensionId& extension_id) {
   GURL origin(std::string(kExtensionScheme) + "://" + extension_id);
-  for (size_t i = 0; i < host.allowed_origins_count; i++) {
-    URLPattern allowed_origin(URLPattern::SCHEME_ALL, host.allowed_origins[i]);
+  for (const char* host_allowed_origin : host.allowed_origins) {
+    URLPattern allowed_origin(URLPattern::SCHEME_ALL, host_allowed_origin);
     if (allowed_origin.MatchesSecurityOrigin(origin)) {
       return true;
     }
@@ -44,8 +39,7 @@
     const std::string& native_host_name,
     bool allow_user_level,
     std::string* error) {
-  for (size_t i = 0; i < kBuiltInHostsCount; i++) {
-    const auto& host = kBuiltInHosts[i];
+  for (const auto& host : kBuiltInHosts) {
     if (host.name == native_host_name) {
       if (MatchesSecurityOrigin(host, source_extension_id)) {
         return (*host.create_function)(browser_context);
diff --git a/chrome/browser/extensions/api/messaging/native_message_built_in_host.h b/chrome/browser/extensions/api/messaging/native_message_built_in_host.h
index 08e2ae4..7b19cdb 100644
--- a/chrome/browser/extensions/api/messaging/native_message_built_in_host.h
+++ b/chrome/browser/extensions/api/messaging/native_message_built_in_host.h
@@ -5,11 +5,13 @@
 #ifndef CHROME_BROWSER_EXTENSIONS_API_MESSAGING_NATIVE_MESSAGE_BUILT_IN_HOST_H_
 #define CHROME_BROWSER_EXTENSIONS_API_MESSAGING_NATIVE_MESSAGE_BUILT_IN_HOST_H_
 
-#include <memory>
-#include "base/memory/raw_ptr_exclusion.h"
-
 #include <stddef.h>
 
+#include <memory>
+
+#include "base/containers/span.h"
+#include "base/memory/raw_span.h"
+
 namespace content {
 class BrowserContext;
 }
@@ -23,24 +25,18 @@
   const char* const name;
 
   // The extension origins allowed to create the built-in host.
-  // This field is not a raw_ptr<> because it only ever points at statically-
+  // This field is a raw_span<> because it only ever points at statically-
   // allocated memory which is never freed, and hence cannot dangle.
-  RAW_PTR_EXCLUSION const char* const* const allowed_origins;
-
-  // The count of |allowed_origins|.
-  size_t allowed_origins_count;
+  const base::raw_span<const char* const> allowed_origins;
 
   // The factory function used to create new instances of this host.
-  std::unique_ptr<NativeMessageHost> (*create_function)(
+  std::unique_ptr<NativeMessageHost> (*const create_function)(
       content::BrowserContext*);
 };
 
 // The set of built-in hosts that can be instantiated. These are defined in the
 // platform-specific impl files.
-extern const NativeMessageBuiltInHost kBuiltInHosts[];
-
-// The count of built-in hosts defined in |kBuiltInHosts|.
-extern const size_t kBuiltInHostsCount;
+extern const base::span<const NativeMessageBuiltInHost> kBuiltInHosts;
 
 }  // namespace extensions
 
diff --git a/chrome/browser/extensions/api/messaging/native_message_echo_host.cc b/chrome/browser/extensions/api/messaging/native_message_echo_host.cc
index a126d67..30f9687 100644
--- a/chrome/browser/extensions/api/messaging/native_message_echo_host.cc
+++ b/chrome/browser/extensions/api/messaging/native_message_echo_host.cc
@@ -16,19 +16,6 @@
 namespace extensions {
 
 // static
-// Must match ScopedTestNativeMessagingHost::kHostName.
-const char* const NativeMessageEchoHost::kHostName =
-    "com.google.chrome.test.echo";
-
-// static
-// Must match ScopedTestNativeMessagingHost::kExtensionId.
-const char* const NativeMessageEchoHost::kOrigins[] = {
-    "chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"};
-
-// static
-const size_t NativeMessageEchoHost::kOriginCount = std::size(kOrigins);
-
-// static
 std::unique_ptr<NativeMessageHost> NativeMessageEchoHost::Create(
     content::BrowserContext* browser_context) {
   return std::make_unique<NativeMessageEchoHost>();
diff --git a/chrome/browser/extensions/api/messaging/native_message_echo_host.h b/chrome/browser/extensions/api/messaging/native_message_echo_host.h
index db51ff1..3f39547 100644
--- a/chrome/browser/extensions/api/messaging/native_message_echo_host.h
+++ b/chrome/browser/extensions/api/messaging/native_message_echo_host.h
@@ -26,9 +26,12 @@
 // used to drive the tests.
 class NativeMessageEchoHost : public NativeMessageHost {
  public:
-  static const char* const kHostName;
-  static const char* const kOrigins[];
-  static const size_t kOriginCount;
+  // Must match ScopedTestNativeMessagingHost::kHostName.
+  static constexpr char kHostName[] = "com.google.chrome.test.echo";
+
+  // Must match ScopedTestNativeMessagingHost::kExtensionId.
+  static constexpr const char* kOrigins[] = {
+      "chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"};
 
   static std::unique_ptr<NativeMessageHost> Create(
       content::BrowserContext* browser_context);
diff --git a/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc b/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc
index 34d4380..4d868d9 100644
--- a/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc
+++ b/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc
@@ -26,26 +26,25 @@
   return remoting::CreateIt2MeNativeMessagingHostForChromeOS();
 }
 
-}  // namespace
-
-const NativeMessageBuiltInHost kBuiltInHosts[] = {
+const NativeMessageBuiltInHost kBuiltInHostsArray[] = {
     {NativeMessageEchoHost::kHostName, NativeMessageEchoHost::kOrigins,
-     NativeMessageEchoHost::kOriginCount, &NativeMessageEchoHost::Create},
+     &NativeMessageEchoHost::Create},
     {remoting::kIt2MeNativeMessageHostName, remoting::kIt2MeOrigins,
-     remoting::kIt2MeOriginsSize, &CreateIt2MeHost},
+     &CreateIt2MeHost},
     {arc::ArcSupportMessageHost::kHostName,
-     arc::ArcSupportMessageHost::kHostOrigin, 1,
+     arc::ArcSupportMessageHost::kHostOrigin,
      &arc::ArcSupportMessageHost::Create},
     {drive::kDriveFsNativeMessageHostName,
-     drive::kDriveFsNativeMessageHostOrigins.data(),
-     drive::kDriveFsNativeMessageHostOrigins.size(),
+     {drive::kDriveFsNativeMessageHostOrigins},
      &drive::CreateDriveFsNativeMessageHostAsh},
     {ash::guest_os::VmSKForwardingNativeMessageHost::kHostName,
-     ash::guest_os::VmSKForwardingNativeMessageHost::kOrigins,
-     ash::guest_os::VmSKForwardingNativeMessageHost::kOriginCount,
+     {ash::guest_os::VmSKForwardingNativeMessageHost::kOrigins},
      &ash::guest_os::VmSKForwardingNativeMessageHost::CreateFromExtension},
 };
 
-const size_t kBuiltInHostsCount = std::size(kBuiltInHosts);
+}  // namespace
+
+constexpr base::span<const NativeMessageBuiltInHost> kBuiltInHosts =
+    kBuiltInHostsArray;
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/system_indicator/system_indicator_manager.cc b/chrome/browser/extensions/api/system_indicator/system_indicator_manager.cc
index ee43c8f0..d263a11e 100644
--- a/chrome/browser/extensions/api/system_indicator/system_indicator_manager.cc
+++ b/chrome/browser/extensions/api/system_indicator/system_indicator_manager.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/extensions/api/system_indicator/system_indicator_manager.h"
 
+#include <memory>
 #include <utility>
 
 #include "base/containers/contains.h"
@@ -62,7 +63,7 @@
 
   raw_ptr<const Extension> extension_;
   raw_ptr<StatusTray> status_tray_;
-  raw_ptr<StatusIcon, DanglingUntriaged> status_icon_;
+  raw_ptr<StatusIcon> status_icon_;
   raw_ptr<Profile> profile_;
   IconImage manifest_icon_;
   gfx::Image dynamic_icon_;
@@ -88,7 +89,10 @@
 ExtensionIndicatorIcon::~ExtensionIndicatorIcon() {
   if (status_icon_) {
     status_icon_->RemoveObserver(this);
-    status_tray_->RemoveStatusIcon(status_icon_);
+    std::unique_ptr<StatusIcon> removed_icon =
+        status_tray_->RemoveStatusIcon(status_icon_);
+    status_icon_ = nullptr;
+    removed_icon.reset();
   }
 }
 
diff --git a/chrome/browser/extensions/desktop_android/desktop_android_extension_system.cc b/chrome/browser/extensions/desktop_android/desktop_android_extension_system.cc
index 7f023db..6f3000f 100644
--- a/chrome/browser/extensions/desktop_android/desktop_android_extension_system.cc
+++ b/chrome/browser/extensions/desktop_android/desktop_android_extension_system.cc
@@ -109,7 +109,7 @@
       scoped_refptr<const Extension> extension) override {}
   void ShowExtensionDisabledError(const Extension* extension,
                                   bool is_remote_install) override {}
-
+  void FinishDelayedInstallationsIfAny() override {}
   void LoadExtensionForReload(
       const ExtensionId& extension_id,
       const base::FilePath& path,
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 2400563..124cd59 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -585,16 +585,7 @@
 void ExtensionService::EnabledReloadableExtensions() {
   TRACE_EVENT0("browser,startup",
                "ExtensionService::EnabledReloadableExtensions");
-
-  std::vector<std::string> extensions_to_enable;
-  for (const auto& e : registry_->disabled_extensions()) {
-    if (extension_prefs_->GetDisableReasons(e->id()) ==
-        disable_reason::DISABLE_RELOAD)
-      extensions_to_enable.push_back(e->id());
-  }
-  for (const std::string& extension : extensions_to_enable) {
-    EnableExtension(extension);
-  }
+  extension_registrar_.EnabledReloadableExtensions();
 }
 
 void ExtensionService::MaybeFinishShutdownDelayed() {
@@ -791,6 +782,10 @@
   AddExtensionDisabledError(this, extension, is_remote_install);
 }
 
+void ExtensionService::FinishDelayedInstallationsIfAny() {
+  MaybeFinishDelayedInstallations();
+}
+
 void ExtensionService::OnUnpackedReloadFailure(const Extension* extension,
                                                const base::FilePath& file_path,
                                                const std::string& error) {
@@ -1384,13 +1379,7 @@
 
 void ExtensionService::RemoveComponentExtension(
     const std::string& extension_id) {
-  scoped_refptr<const Extension> extension(
-      registry_->enabled_extensions().GetByID(extension_id));
-  UnloadExtension(extension_id, UnloadedExtensionReason::UNINSTALL);
-  if (extension.get()) {
-    ExtensionRegistry::Get(profile_)->TriggerOnUninstalled(
-        extension.get(), UNINSTALL_REASON_COMPONENT_REMOVED);
-  }
+  extension_registrar_.RemoveComponentExtension(extension_id);
 }
 
 void ExtensionService::UnloadAllExtensionsForTest() {
@@ -1771,7 +1760,7 @@
   if (InstallVerifier::NeedsVerification(*extension, GetBrowserContext()))
     InstallVerifier::Get(GetBrowserContext())->VerifyExtension(extension->id());
 
-  FinishInstallation(extension);
+  extension_registrar_.FinishInstallation(extension);
 }
 
 bool ExtensionService::FinishDelayedInstallationIfReady(
@@ -1809,39 +1798,10 @@
     NOTREACHED();
   }
 
-  FinishInstallation(delayed_install.get());
+  extension_registrar_.FinishInstallation(delayed_install.get());
   return true;
 }
 
-void ExtensionService::FinishInstallation(const Extension* extension) {
-  const Extension* existing_extension =
-      registry_->GetInstalledExtension(extension->id());
-  bool is_update = false;
-  std::string old_name;
-  if (existing_extension) {
-    is_update = true;
-    old_name = existing_extension->name();
-  }
-  registry_->TriggerOnWillBeInstalled(extension, is_update, old_name);
-
-  // Unpacked extensions default to allowing file access, but if that has been
-  // overridden, don't reset the value.
-  if (Manifest::ShouldAlwaysAllowFileAccess(extension->location()) &&
-      !extension_prefs_->HasAllowFileAccessSetting(extension->id())) {
-    extension_prefs_->SetAllowFileAccess(extension->id(), true);
-  }
-
-  AddExtension(extension);
-
-  // Notify observers that need to know when an installation is complete.
-  registry_->TriggerOnInstalled(extension, is_update);
-
-  // Check extensions that may have been delayed only because this shared module
-  // was not available.
-  if (SharedModuleInfo::IsSharedModule(extension))
-    MaybeFinishDelayedInstallations();
-}
-
 const Extension* ExtensionService::GetPendingExtensionUpdate(
     const std::string& id) const {
   return delayed_installs_.GetByID(id);
@@ -2299,17 +2259,8 @@
 }
 
 void ExtensionService::UninstallMigratedExtensions() {
-  const ExtensionSet installed_extensions =
-      registry_->GenerateInstalledExtensionsSet();
-  for (const std::string& extension_id : kObsoleteComponentExtensionIds) {
-    auto* extension = installed_extensions.GetByID(extension_id);
-    if (extension) {
-      UninstallExtension(extension_id, UNINSTALL_REASON_COMPONENT_REMOVED,
-                         nullptr);
-      extension_prefs_->MarkObsoleteComponentExtensionAsRemoved(
-          extension->id(), extension->location());
-    }
-  }
+  extension_registrar_.UninstallMigratedExtensions(
+      kObsoleteComponentExtensionIds);
 }
 
 void ExtensionService::OnDeveloperModePrefChanged() {
diff --git a/chrome/browser/extensions/extension_service.h b/chrome/browser/extensions/extension_service.h
index 8348081..24896cd 100644
--- a/chrome/browser/extensions/extension_service.h
+++ b/chrome/browser/extensions/extension_service.h
@@ -482,7 +482,7 @@
 
 #if defined(UNIT_TEST)
   void FinishInstallationForTest(const Extension* extension) {
-    FinishInstallation(extension);
+    extension_registrar_.FinishInstallation(extension);
   }
 
   void UninstallMigratedExtensionsForTest() { UninstallMigratedExtensions(); }
@@ -552,7 +552,7 @@
       ExtensionRegistrar::LoadErrorBehavior load_error_behavior) override;
   void ShowExtensionDisabledError(const Extension* extension,
                                   bool is_remote_install) override;
-
+  void FinishDelayedInstallationsIfAny() override;
   bool CanAddExtension(const Extension* extension) override;
   bool CanEnableExtension(const Extension* extension) override;
   bool CanDisableExtension(const Extension* extension) override;
@@ -595,9 +595,6 @@
                                 const std::string& install_parameter,
                                 base::Value::Dict ruleset_install_prefs);
 
-  // Common helper to finish installing the given extension.
-  void FinishInstallation(const Extension* extension);
-
   // Disables the extension if the privilege level has increased
   // (e.g., due to an upgrade).
   void CheckPermissionsIncrease(const Extension* extension,
diff --git a/chrome/browser/extensions/platform_test_extension_loader.cc b/chrome/browser/extensions/platform_test_extension_loader.cc
index 21c8212e..747ce8b 100644
--- a/chrome/browser/extensions/platform_test_extension_loader.cc
+++ b/chrome/browser/extensions/platform_test_extension_loader.cc
@@ -36,6 +36,9 @@
 
 scoped_refptr<const Extension> PlatformTestExtensionLoader::LoadExtension(
     const base::FilePath& path) {
+  // Clean up the kMetadataFolder if necessary.
+  file_util::MaybeCleanupMetadataFolder(path);
+
   scoped_refptr<const Extension> extension = LoadExtensionFromDirectory(path);
   if (!extension) {
     return nullptr;
diff --git a/chrome/browser/extensions/unpacked_installer.cc b/chrome/browser/extensions/unpacked_installer.cc
index 32bee75f..dcc3ea2 100644
--- a/chrome/browser/extensions/unpacked_installer.cc
+++ b/chrome/browser/extensions/unpacked_installer.cc
@@ -64,19 +64,6 @@
 const char kImportMissing[] = "'import' extension is not installed.";
 const char kImportNotSharedModule[] = "'import' is not a shared module.";
 
-// Deletes files reserved for use by the Extension system in the kMetadataFolder
-// and the kMetadataFolder itself if it is empty.
-void MaybeCleanupMetadataFolder(const base::FilePath& extension_path) {
-  const std::vector<base::FilePath> reserved_filepaths =
-      file_util::GetReservedMetadataFilePaths(extension_path);
-  for (const auto& file : reserved_filepaths)
-    base::DeletePathRecursively(file);
-
-  const base::FilePath& metadata_dir = extension_path.Append(kMetadataFolder);
-  if (base::IsDirectoryEmpty(metadata_dir))
-    base::DeletePathRecursively(metadata_dir);
-}
-
 }  // namespace
 
 // static
@@ -261,7 +248,7 @@
   // Clean up the kMetadataFolder if necessary. This prevents spurious
   // warnings/errors and ensures we don't treat a user provided file as one by
   // the Extension system.
-  MaybeCleanupMetadataFolder(extension_path_);
+  file_util::MaybeCleanupMetadataFolder(extension_path_);
 
   // Treat presence of illegal filenames as a hard error for unpacked
   // extensions. Don't do so for command line extensions since this breaks
diff --git a/chrome/browser/extensions/web_accessible_resources_browsertest.cc b/chrome/browser/extensions/web_accessible_resources_browsertest.cc
index ff94a09..0e3237f 100644
--- a/chrome/browser/extensions/web_accessible_resources_browsertest.cc
+++ b/chrome/browser/extensions/web_accessible_resources_browsertest.cc
@@ -6,11 +6,10 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
-#include "chrome/browser/extensions/extension_browsertest.h"
+#include "build/build_config.h"
+#include "chrome/browser/extensions/extension_platform_browsertest.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/ui_test_utils.h"
 #include "components/version_info/channel.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
@@ -18,12 +17,19 @@
 #include "content/public/test/test_navigation_observer.h"
 #include "extensions/browser/background_script_executor.h"
 #include "extensions/common/constants.h"
+#include "extensions/common/extension.h"
 #include "extensions/common/extension_features.h"
+#include "extensions/common/features/feature_channel.h"
 #include "extensions/test/extension_test_message_listener.h"
 #include "extensions/test/test_extension_dir.h"
 #include "net/dns/mock_host_resolver.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
+#if !BUILDFLAG(IS_ANDROID)
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/ui_test_utils.h"
+#endif
+
 namespace extensions {
 namespace {
 static constexpr char kManifestStub[] = R"({
@@ -73,7 +79,7 @@
 )";
 
 // Exercise web accessible resources with experimental extension features.
-class WebAccessibleResourcesBrowserTest : public ExtensionBrowserTest {
+class WebAccessibleResourcesBrowserTest : public ExtensionPlatformBrowserTest {
  public:
   explicit WebAccessibleResourcesBrowserTest(bool enable_feature = true) {
     feature_list_.InitWithFeatureState(
@@ -81,7 +87,7 @@
   }
 
   void SetUpOnMainThread() override {
-    ExtensionBrowserTest::SetUpOnMainThread();
+    ExtensionPlatformBrowserTest::SetUpOnMainThread();
     host_resolver()->AddRule("*", "127.0.0.1");
     EXPECT_TRUE(embedded_test_server()->Start());
   }
@@ -114,8 +120,8 @@
   // Navigate to a test page and get the web contents.
   base::FilePath test_page;
   GURL gurl = embedded_test_server()->GetURL("example.com", "/simple.html");
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), gurl));
-  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  auto* web_contents = GetActiveWebContents();
+  ASSERT_TRUE(content::NavigateToURL(web_contents, gurl));
 
   std::string script =
       base::StringPrintf(kFetchResourceScriptTemplate,
@@ -128,8 +134,11 @@
   ASSERT_TRUE(content::EvalJs(web_contents, script).ExtractBool());
 }
 
+#if !BUILDFLAG(IS_ANDROID)
 // Exercise these resources being used in iframes in a web page. The navigation
 // flow goes through a different path than resource fetching.
+// TODO(crbug.com/390687767): Port to desktop Android. Fails due to a navigation
+// to about:blank#blocked.
 IN_PROC_BROWSER_TEST_F(WebAccessibleResourcesBrowserTest,
                        UseDynamicUrlInIframe) {
   // Load an extension that has one web accessible resource.
@@ -194,6 +203,8 @@
 // Tests that navigating a main frame via location.href works if and only if
 // the target resource is accessible to the main frame.
 // Regression test for https://crbug.com/374503948.
+// TODO(crbug.com/390687767): Port to desktop Android. Fails due to a navigation
+// to about:blank#blocked.
 IN_PROC_BROWSER_TEST_F(
     WebAccessibleResourcesBrowserTest,
     MainFrameLocationHrefUpdatesAreSubjectToAccessibleResources) {
@@ -285,6 +296,7 @@
     }
   }
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // A test suite that will run both with and without the dynamic URL feature
 // enabled.
@@ -300,9 +312,12 @@
                          ParameterizedWebAccessibleResourcesBrowserTest,
                          testing::Bool());
 
+#if !BUILDFLAG(IS_ANDROID)
 // DNR, WAR, and use_dynamic_url with the extension feature. DNR does not
 // currently succeed when redirecting to a resource using use_dynamic_url with
 // query parameters.
+// TODO(crbug.com/390687767): Port to desktop Android once chrome.runtime is
+// fully ported. Right now the ExtensionTestMessageListener times out.
 IN_PROC_BROWSER_TEST_P(ParameterizedWebAccessibleResourcesBrowserTest,
                        DeclarativeNetRequest) {
   ExtensionTestMessageListener listener("ready");
@@ -352,6 +367,7 @@
     EXPECT_TRUE(navigation_observer.last_navigation_succeeded());
   }
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // If `use_dynamic_url` is set to true in manifest.json, then the associated web
 // accessible resource(s) can only be loaded using the dynamic url if using the
@@ -369,8 +385,8 @@
   // Navigate to a test page and get the web contents.
   base::FilePath test_page;
   GURL gurl = embedded_test_server()->GetURL("example.com", "/simple.html");
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), gurl));
-  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  auto* web_contents = GetActiveWebContents();
+  ASSERT_TRUE(content::NavigateToURL(web_contents, gurl));
 
   std::string script =
       base::StringPrintf(kFetchResourceScriptTemplate,
@@ -397,11 +413,10 @@
   ASSERT_TRUE(listener.WaitUntilSatisfied());
 
   // Navigate to a non extension page.
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
+  content::WebContents* web_contents = GetActiveWebContents();
   GURL gurl = embedded_test_server()->GetURL("example.com", "/empty.html");
   content::TestNavigationObserver navigation_observer(web_contents);
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), gurl));
+  ASSERT_TRUE(content::NavigateToURL(web_contents, gurl));
   ASSERT_TRUE(navigation_observer.last_navigation_succeeded());
   EXPECT_EQ(gurl, web_contents->GetLastCommittedURL());
   EXPECT_EQ(net::Error::OK, navigation_observer.last_net_error_code());
@@ -419,12 +434,11 @@
   ASSERT_TRUE(listener.WaitUntilSatisfied());
 
   // Navigate to a non extension page.
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
+  content::WebContents* web_contents = GetActiveWebContents();
   GURL gurl = embedded_test_server()->GetURL(
       "example.com", "/extensions/api_test/webrequest/script/index.html");
   content::TestNavigationObserver navigation_observer(web_contents);
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), gurl));
+  ASSERT_TRUE(content::NavigateToURL(web_contents, gurl));
   ASSERT_TRUE(navigation_observer.last_navigation_succeeded());
   EXPECT_EQ(gurl, web_contents->GetLastCommittedURL());
   EXPECT_EQ(net::Error::OK, navigation_observer.last_net_error_code());
@@ -439,12 +453,11 @@
   ASSERT_TRUE(extension);
 
   // Navigate to a non extension page.
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
+  content::WebContents* web_contents = GetActiveWebContents();
   GURL gurl =
       embedded_test_server()->GetURL("example.com", "/simple_with_script.html");
   content::TestNavigationObserver navigation_observer(web_contents);
-  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), gurl));
+  ASSERT_TRUE(content::NavigateToURL(web_contents, gurl));
   ASSERT_TRUE(navigation_observer.last_navigation_succeeded());
   EXPECT_EQ(gurl, web_contents->GetLastCommittedURL());
   EXPECT_EQ(net::Error::OK, navigation_observer.last_net_error_code());
@@ -452,6 +465,9 @@
   EXPECT_EQ("dnr redirect success", result.ExtractString());
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+// TODO(crbug.com/390687767): Port to desktop Android. Currently the redirect
+// doesn't happen.
 class WebAccessibleResourcesBrowserRedirectTest
     : public WebAccessibleResourcesBrowserTest {
  protected:
@@ -520,6 +536,7 @@
     })",
       "Extensions.WAR.XOriginWebAccessible.MV3");
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 }  // namespace
 }  // namespace extensions
diff --git a/chrome/browser/feedback/android/BUILD.gn b/chrome/browser/feedback/android/BUILD.gn
index 324c3169..2c675b6 100644
--- a/chrome/browser/feedback/android/BUILD.gn
+++ b/chrome/browser/feedback/android/BUILD.gn
@@ -68,6 +68,7 @@
     "java/src/org/chromium/chrome/browser/feedback/SystemInfoFeedbackSource.java",
     "java/src/org/chromium/chrome/browser/feedback/UrlFeedbackSource.java",
     "java/src/org/chromium/chrome/browser/feedback/VariationsFeedbackSource.java",
+    "java/src/org/chromium/chrome/browser/feedback/VariationsStateFeedbackSource.java",
   ]
   deps = [
     ":feedback_collector_java",
diff --git a/chrome/browser/feedback/android/java/src/org/chromium/chrome/browser/feedback/VariationsStateFeedbackSource.java b/chrome/browser/feedback/android/java/src/org/chromium/chrome/browser/feedback/VariationsStateFeedbackSource.java
new file mode 100644
index 0000000..7c64b7c
--- /dev/null
+++ b/chrome/browser/feedback/android/java/src/org/chromium/chrome/browser/feedback/VariationsStateFeedbackSource.java
@@ -0,0 +1,25 @@
+// Copyright 2025 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.chrome.browser.feedback;
+
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.components.variations.VariationsAssociatedData;
+
+import java.util.Map;
+
+/** Grabs feedback about the current command-line variations. */
+class VariationsStateFeedbackSource implements FeedbackSource {
+    private final boolean mIsOffTheRecord;
+
+    VariationsStateFeedbackSource(Profile profile) {
+        mIsOffTheRecord = profile.isOffTheRecord();
+    }
+
+    @Override
+    public Map<String, String> getFeedback() {
+        if (mIsOffTheRecord) return null;
+        return VariationsAssociatedData.getVariationsStateFeedbackMap();
+    }
+}
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 554ce14..c0729644 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3138,13 +3138,13 @@
   {
     "name": "enable-hardware_mirror-mode",
     "owners": [
-      "gildekel@chromium.org",
+      "vincentchiang@chromium.org",
       "//ui/display/OWNERS"
     ],
     // A temporary flag to control hardware mirroring until it is decided
-    // whether to permanently remove hardware mirroring. See crbug.com/1161556
-    // for more details.
-    "expiry_milestone": 134
+    // whether to permanently remove hardware mirroring. See b/207072219 for
+    // more details.
+    "expiry_milestone": 150
   },
   {
     "name": "enable-hardware-secure-decryption",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 4f5f051..5133f1e 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1546,6 +1546,12 @@
     "When enabled, Chrome will attempt to connect to the system tracing "
     "service";
 
+const char kEnableWindowsGamingInputDataFetcherName[] =
+    "Enable Windows.Gaming.Input";
+const char kEnableWindowsGamingInputDataFetcherDescription[] =
+    "Enable Windows.Gaming.Input by default to provide game controller "
+    "support on Windows 10 desktop.";
+
 const char kBlockInsecurePrivateNetworkRequestsName[] =
     "Block insecure private network requests.";
 const char kBlockInsecurePrivateNetworkRequestsDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 99aa2c5..91f735e 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -806,6 +806,9 @@
 extern const char kEnableNetworkLoggingToFileName[];
 extern const char kEnableNetworkLoggingToFileDescription[];
 
+extern const char kEnableWindowsGamingInputDataFetcherName[];
+extern const char kEnableWindowsGamingInputDataFetcherDescription[];
+
 extern const char kBlockInsecurePrivateNetworkRequestsName[];
 extern const char kBlockInsecurePrivateNetworkRequestsDescription[];
 
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 39a19b5..f06a78c 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
@@ -586,15 +586,15 @@
     public static final CachedFlag sAndroidAppIntegration =
             newCachedFlag(ANDROID_APP_INTEGRATION, true);
     public static final CachedFlag sAndroidAppIntegrationModule =
-            newCachedFlag(ANDROID_APP_INTEGRATION_MODULE, false);
+            newCachedFlag(ANDROID_APP_INTEGRATION_MODULE, false, true);
     public static final CachedFlag sAndroidAppIntegrationV2 =
-            newCachedFlag(ANDROID_APP_INTEGRATION_V2, false);
+            newCachedFlag(ANDROID_APP_INTEGRATION_V2, false, true);
     public static final CachedFlag sAndroidTabSkipSaveTabsKillswitch =
             newCachedFlag(ANDROID_TAB_SKIP_SAVE_TABS_TASK_KILLSWITCH, true, true);
     public static final CachedFlag sNewTabPageCustomization =
             newCachedFlag(NEW_TAB_PAGE_CUSTOMIZATION, false);
     public static final CachedFlag sAndroidAppIntegrationWithFavicon =
-            newCachedFlag(ANDROID_APP_INTEGRATION_WITH_FAVICON, false);
+            newCachedFlag(ANDROID_APP_INTEGRATION_WITH_FAVICON, false, true);
     public static final CachedFlag sAndroidBottomToolbar =
             newCachedFlag(ANDROID_BOTTOM_TOOLBAR, false, true);
     public static final CachedFlag sAndroidElegantTextHeight =
diff --git a/chrome/browser/glic/border_view.cc b/chrome/browser/glic/border_view.cc
index 2d40493..b008af28 100644
--- a/chrome/browser/glic/border_view.cc
+++ b/chrome/browser/glic/border_view.cc
@@ -21,13 +21,8 @@
 
 namespace glic {
 namespace {
-// Note: |           |           |
-//       |<-- 5px -->|<-- 5px -->|
-//       |  outside  |  visible  |
-//
-// So only half of the full width are inside the visible viewport.
-constexpr static int kBorderWidthMin = 2;
-constexpr static int kBorderWidthMax = 10;
+constexpr static int kBorderWidthMin = 1;
+constexpr static int kBorderWidthMax = 5;
 
 constexpr static base::TimeDelta kAnimationDuration = base::Seconds(2);
 
@@ -36,6 +31,10 @@
   return (since_first_frame / kAnimationDuration) * (M_PI / 2);
 }
 
+SkV4 SkRGBA4fToSkV4(SkRGBA4f<kPremul_SkAlphaType> color) {
+  return SkV4{color.fR, color.fG, color.fB, color.fA};
+}
+
 }  // namespace
 
 class BorderView::BorderViewUpdater {
@@ -139,15 +138,43 @@
     return;
   }
 
-  int border_width =
-      kBorderWidthMin + ((kBorderWidthMax - kBorderWidthMin) * progress_);
+  float border_width =
+      (kBorderWidthMin + ((kBorderWidthMax - kBorderWidthMin) * progress_));
+  SkColor4f border_color =
+      SkColor4f::FromColor(GetColorProvider()->GetColor(ui::kColorSysPrimary));
+  border_color.fA = progress_;
+
+  const std::string_view kDrawRect(R"(
+      const float4 transparent = vec4(0);
+      uniform float2 u_top_left;
+      uniform float2 u_btm_right;
+      uniform float4 u_border_color;
+      vec4 main(float2 coord) {
+        if (all(greaterThanEqual(coord, u_top_left)) &&
+            all(lessThan(coord, u_btm_right))) {
+          return transparent;
+        } else {
+          return u_border_color;
+        }
+      }
+    )");
+
+  std::vector<cc::PaintShader::Float2Uniform> float2_uniforms = {
+      {.name = SkString("u_top_left"),
+       .value = SkV2{bounds().origin().x() + border_width,
+                     bounds().origin().y() + border_width}},
+      {.name = SkString("u_btm_right"),
+       .value = SkV2{bounds().bottom_right().x() - border_width,
+                     bounds().bottom_right().y() - border_width}}};
+  std::vector<cc::PaintShader::Float4Uniform> float4_uniforms = {
+      {.name = SkString("u_border_color"),
+       .value = SkRGBA4fToSkV4(border_color.premul())}};
 
   views::View::OnPaint(canvas);
   cc::PaintFlags flags;
-  flags.setStyle(cc::PaintFlags::kStroke_Style);
-  flags.setColor(GetColorProvider()->GetColor(ui::kColorSysPrimary));
-  flags.setStrokeWidth(border_width);
-  flags.setAlphaf(progress_);
+  flags.setShader(cc::PaintShader::MakeSkSLCommand(
+      kDrawRect, /*float_uniforms=*/{}, std::move(float2_uniforms),
+      std::move(float4_uniforms)));
   canvas->DrawRect(gfx::RectF(bounds()), flags);
 }
 
diff --git a/chrome/browser/glic/border_view_browsertest.cc b/chrome/browser/glic/border_view_browsertest.cc
index 320e5ac..61d5c15 100644
--- a/chrome/browser/glic/border_view_browsertest.cc
+++ b/chrome/browser/glic/border_view_browsertest.cc
@@ -83,7 +83,7 @@
   static SkBitmap ConstructExpectedBitmap(const gfx::Size& size,
                                           SkColor border_color,
                                           SkColor center_color,
-                                          int border_width,
+                                          float border_width,
                                           float alpha) {
     SkBitmap bitmap;
     SkImageInfo info =
@@ -188,7 +188,7 @@
     SkBitmap expected_bitmap = ConstructExpectedBitmap(
         capture_rect.size(),
         /*border_color=*/BorderColor(),
-        /*center_color=*/kBlack, /*border_width=*/2, /*alpha=*/0.f);
+        /*center_color=*/kBlack, /*border_width=*/2.f, /*alpha=*/0.f);
 
     EXPECT_TRUE(cc::MatchesBitmap(
         actual_bitmap, expected_bitmap,
@@ -210,7 +210,7 @@
     // The border width is calculated as:
     // `kBorderWidthMin` + ((`kBorderWidthMax` - `kBorderWidthMin`) *
     // `progress`).
-    int border_width = 2 + (8 * progress);
+    float border_width = 2 + (8 * progress);
     SkBitmap expected_bitmap = ConstructExpectedBitmap(
         capture_rect.size(),
         /*border_color=*/BorderColor(),
@@ -231,7 +231,7 @@
     SkBitmap expected_bitmap =
         ConstructExpectedBitmap(capture_rect.size(),
                                 /*border_color=*/BorderColor(),
-                                /*center_color=*/kBlack, /*border_width=*/10,
+                                /*center_color=*/kBlack, /*border_width=*/10.f,
                                 /*alpha=*/1.f);
 
     EXPECT_TRUE(cc::MatchesBitmap(
@@ -248,7 +248,7 @@
     SkBitmap expected_bitmap = ConstructExpectedBitmap(
         capture_rect.size(),
         /*border_color=*/BorderColor(),
-        /*center_color=*/kBlack, /*border_width=*/10, /*alpha=*/1.f);
+        /*center_color=*/kBlack, /*border_width=*/10.f, /*alpha=*/1.f);
 
     EXPECT_TRUE(cc::MatchesBitmap(
         actual_bitmap, expected_bitmap,
@@ -297,7 +297,7 @@
     SkBitmap expected_bitmap = ConstructExpectedBitmap(
         capture_rect.size(),
         /*border_color=*/BorderColor(),
-        /*center_color=*/kBlack, /*border_width=*/2, /*alpha=*/0.f);
+        /*center_color=*/kBlack, /*border_width=*/2.f, /*alpha=*/0.f);
 
     EXPECT_TRUE(cc::MatchesBitmap(
         actual_bitmap, expected_bitmap,
@@ -311,7 +311,7 @@
     SkBitmap actual_bitmap = PaintBorder(border);
 
     float progress = sin(0.125 * M_PI);
-    int border_width = 2 + (8 * progress);
+    float border_width = 2 + (8 * progress);
     SkBitmap expected_bitmap = ConstructExpectedBitmap(
         capture_rect.size(),
         /*border_color=*/BorderColor(),
@@ -339,7 +339,7 @@
     SkBitmap expected_bitmap =
         ConstructExpectedBitmap(capture_rect.size(),
                                 /*border_color=*/BorderColor(),
-                                /*center_color=*/kBlack, /*border_width=*/2,
+                                /*center_color=*/kBlack, /*border_width=*/2.f,
                                 /*alpha=*/0.f);
 
     EXPECT_TRUE(cc::MatchesBitmap(
@@ -354,7 +354,7 @@
     SkBitmap actual_bitmap = PaintBorder(border);
 
     float progress = sin(0.125 * M_PI);
-    int border_width = 2 + (8 * progress);
+    float border_width = 2 + (8 * progress);
     SkBitmap expected_bitmap = ConstructExpectedBitmap(
         capture_rect.size(),
         /*border_color=*/BorderColor(),
@@ -396,7 +396,7 @@
     SkBitmap expected_bitmap = ConstructExpectedBitmap(
         capture_rect.size(),
         /*border_color=*/BorderColor(),
-        /*center_color=*/kBlack, /*border_width=*/2, /*alpha=*/0.f);
+        /*center_color=*/kBlack, /*border_width=*/2.f, /*alpha=*/0.f);
 
     EXPECT_TRUE(cc::MatchesBitmap(
         actual_bitmap, expected_bitmap,
@@ -424,7 +424,7 @@
     SkBitmap expected_bitmap = ConstructExpectedBitmap(
         capture_rect.size(),
         /*border_color=*/BorderColor(),
-        /*center_color=*/kBlack, /*border_width=*/2, /*alpha=*/0.f);
+        /*center_color=*/kBlack, /*border_width=*/2.f, /*alpha=*/0.f);
 
     EXPECT_TRUE(cc::MatchesBitmap(
         actual_bitmap, expected_bitmap,
@@ -436,7 +436,7 @@
     SkBitmap actual_bitmap = PaintBorder(new_border);
 
     float progress = sin(0.25 * M_PI);
-    int border_width = 2 + (8 * progress);
+    float border_width = 2 + (8 * progress);
     SkBitmap expected_bitmap = ConstructExpectedBitmap(
         capture_rect.size(),
         /*border_color=*/BorderColor(),
diff --git a/chrome/browser/glic/glic_enabling.cc b/chrome/browser/glic/glic_enabling.cc
index cb7da74..1ef9ad7 100644
--- a/chrome/browser/glic/glic_enabling.cc
+++ b/chrome/browser/glic/glic_enabling.cc
@@ -27,7 +27,8 @@
     return false;
   }
 
-  return profile->GetPrefs()->GetBoolean(glic::prefs::kGlicEnabledByPolicy);
+  return profile->GetPrefs()->GetInteger(glic::prefs::kGlicEnabledByPolicy) ==
+         static_cast<int>(glic::prefs::EnabledByPolicyState::kEnabled);
 }
 
 glic::GlicEnabledStatus GlicEnabling::CheckEnabling() {
diff --git a/chrome/browser/glic/glic_policy_browsertest.cc b/chrome/browser/glic/glic_policy_browsertest.cc
index ca8f56fc7..974fb557 100644
--- a/chrome/browser/glic/glic_policy_browsertest.cc
+++ b/chrome/browser/glic/glic_policy_browsertest.cc
@@ -26,6 +26,7 @@
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_navigation_observer.h"
 
+using glic::prefs::EnabledByPolicyState;
 using glic::prefs::kGlicEnabledByPolicy;
 
 namespace glic {
@@ -103,6 +104,11 @@
   testing::NiceMock<policy::MockConfigurationPolicyProvider>
       policy_for_profile_2_;
 
+  static constexpr int kEnabledValue =
+      static_cast<int>(EnabledByPolicyState::kEnabled);
+  static constexpr int kDisabledValue =
+      static_cast<int>(EnabledByPolicyState::kDisabled);
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
 };
@@ -111,19 +117,20 @@
   // By default the pref should start off unmanaged and defaulted to enabled.
   PrefService* prefs = browser()->profile()->GetPrefs();
   EXPECT_FALSE(prefs->IsManagedPreference(kGlicEnabledByPolicy));
-  EXPECT_TRUE(prefs->GetBoolean(kGlicEnabledByPolicy));
+  EXPECT_EQ(kEnabledValue, prefs->GetInteger(kGlicEnabledByPolicy));
 
   // Verify that policy can force-disable Glic.
   PolicyMap policies;
-  policies.Set(key::kGlicEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-               POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(false), nullptr);
+  policies.Set(key::kGlicSettings, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(kDisabledValue),
+               nullptr);
   UpdateProviderPolicy(policies);
   EXPECT_TRUE(prefs->IsManagedPreference(kGlicEnabledByPolicy));
-  EXPECT_FALSE(prefs->GetBoolean(kGlicEnabledByPolicy));
+  EXPECT_EQ(kDisabledValue, prefs->GetInteger(kGlicEnabledByPolicy));
 
   // Verify the policy value cannot be overridden.
-  prefs->SetBoolean(kGlicEnabledByPolicy, true);
-  EXPECT_FALSE(prefs->GetBoolean(kGlicEnabledByPolicy));
+  prefs->SetInteger(kGlicEnabledByPolicy, kEnabledValue);
+  EXPECT_EQ(kDisabledValue, prefs->GetInteger(kGlicEnabledByPolicy));
 }
 
 // Ensure that when policy disables Glic, a browser window doesn't show the Glic
@@ -133,14 +140,17 @@
   ASSERT_NE(profile_1_, profile_2_);
 
   // The pref defaults to enabled.
-  ASSERT_TRUE(profile_1_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
+  ASSERT_EQ(kEnabledValue,
+            profile_1_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
 
   // Disable the policy in the default profile.
   PolicyMap policies;
-  policies.Set(key::kGlicEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-               POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(false), nullptr);
+  policies.Set(key::kGlicSettings, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(kDisabledValue),
+               nullptr);
   UpdateProviderPolicy(policies);
-  ASSERT_FALSE(profile_1_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
+  ASSERT_EQ(kDisabledValue,
+            profile_1_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
 
   {
     // A new window in profile 1 shouldn't have the Glic button.
@@ -154,10 +164,12 @@
   }
 
   // Re-enable the policy. Ensure the button is recreated.
-  policies.Set(key::kGlicEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-               POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(true), nullptr);
+  policies.Set(key::kGlicSettings, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(kEnabledValue),
+               nullptr);
   UpdateProviderPolicy(policies);
-  ASSERT_TRUE(profile_1_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
+  ASSERT_EQ(kEnabledValue,
+            profile_1_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
 
   {
     // A new window in profile 1 should again get the Glic button now that the
@@ -180,7 +192,8 @@
   Browser* profile_2_window_2 = CreateBrowser(profile_2_);
 
   // The pref defaults to enabled. Ensure the button was created in each window.
-  ASSERT_TRUE(profile_1_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
+  ASSERT_EQ(kEnabledValue,
+            profile_1_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
   EXPECT_TRUE(GetGlicButtonForBrowser(profile_1_window_1));
   EXPECT_TRUE(GetGlicButtonForBrowser(profile_1_window_2));
   EXPECT_TRUE(GetGlicButtonForBrowser(profile_2_window_1));
@@ -188,10 +201,12 @@
 
   // Disable the policy in the first profile.
   PolicyMap policies;
-  policies.Set(key::kGlicEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-               POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(false), nullptr);
+  policies.Set(key::kGlicSettings, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(kDisabledValue),
+               nullptr);
   UpdateProviderPolicy(policies);
-  ASSERT_FALSE(profile_1_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
+  ASSERT_EQ(kDisabledValue,
+            profile_1_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
 
   {
     // The windows in profile 1 should have lost their Glic button.
@@ -204,10 +219,12 @@
   }
 
   // Re-enable the policy. Ensure the button is recreated.
-  policies.Set(key::kGlicEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-               POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(true), nullptr);
+  policies.Set(key::kGlicSettings, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(kEnabledValue),
+               nullptr);
   UpdateProviderPolicy(policies);
-  ASSERT_TRUE(profile_1_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
+  ASSERT_EQ(kEnabledValue,
+            profile_1_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
 
   {
     // The windows in profile 1 should get back their Glic button.
@@ -230,8 +247,10 @@
   ASSERT_TRUE(new_window_profile_2);
 
   // The pref defaults to enabled.
-  ASSERT_TRUE(profile_1_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
-  ASSERT_TRUE(profile_2_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
+  ASSERT_EQ(kEnabledValue,
+            profile_1_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
+  ASSERT_EQ(kEnabledValue,
+            profile_2_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
 
   glic::GlicBackgroundModeManager* background_mode_manager =
       g_browser_process->GetFeatures()->glic_background_mode_manager();
@@ -240,11 +259,14 @@
   // Disable the policy in the default profile.
   {
     PolicyMap policies;
-    policies.Set(key::kGlicEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-                 POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(false), nullptr);
+    policies.Set(key::kGlicSettings, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(kDisabledValue),
+                 nullptr);
     UpdateProviderPolicy(policies);
-    ASSERT_FALSE(profile_1_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
-    ASSERT_TRUE(profile_2_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
+    ASSERT_EQ(kDisabledValue,
+              profile_1_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
+    ASSERT_EQ(kEnabledValue,
+              profile_2_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
   }
 
   // Background mode should remain active since profile_2_ still has it enabled.
@@ -253,11 +275,14 @@
   // Disable the policy in the second profile.
   {
     PolicyMap policies;
-    policies.Set(key::kGlicEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-                 POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(false), nullptr);
+    policies.Set(key::kGlicSettings, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(kDisabledValue),
+                 nullptr);
     policy_for_profile_2_.UpdateChromePolicy(policies);
-    ASSERT_FALSE(profile_1_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
-    ASSERT_FALSE(profile_2_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
+    ASSERT_EQ(kDisabledValue,
+              profile_1_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
+    ASSERT_EQ(kDisabledValue,
+              profile_2_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
   }
 
   // Background mode should be exited since none of the loaded profiles enable
@@ -267,11 +292,14 @@
   // Enable the policy in the default profile again.
   {
     PolicyMap policies;
-    policies.Set(key::kGlicEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-                 POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(true), nullptr);
+    policies.Set(key::kGlicSettings, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+                 POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(kEnabledValue),
+                 nullptr);
     UpdateProviderPolicy(policies);
-    ASSERT_TRUE(profile_1_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
-    ASSERT_FALSE(profile_2_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
+    ASSERT_EQ(kEnabledValue,
+              profile_1_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
+    ASSERT_EQ(kDisabledValue,
+              profile_2_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
   }
 
   // Background mode should be reentered since the first profile is enabled.
@@ -282,11 +310,12 @@
 IN_PROC_BROWSER_TEST_F(GlicPolicyTest, PolicyDisablesWebUi) {
   // Disable the policy.
   PolicyMap policies;
-  policies.Set(key::kGlicEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-               POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(false), nullptr);
+  policies.Set(key::kGlicSettings, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(kDisabledValue),
+               nullptr);
   UpdateProviderPolicy(policies);
-  ASSERT_FALSE(
-      browser()->profile()->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
+  ASSERT_EQ(kDisabledValue,
+            browser()->profile()->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
 
   GURL glic_url = GURL(chrome::kChromeUIGlicURL);
 
@@ -303,10 +332,12 @@
   }
 
   // Re-enable the policy.
-  policies.Set(key::kGlicEnabled, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
-               POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(true), nullptr);
+  policies.Set(key::kGlicSettings, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
+               POLICY_SOURCE_ENTERPRISE_DEFAULT, base::Value(kEnabledValue),
+               nullptr);
   UpdateProviderPolicy(policies);
-  ASSERT_TRUE(profile_1_->GetPrefs()->GetBoolean(kGlicEnabledByPolicy));
+  ASSERT_EQ(kEnabledValue,
+            profile_1_->GetPrefs()->GetInteger(kGlicEnabledByPolicy));
 
   // Navigating to chrome://glic should now succeed.
   {
diff --git a/chrome/browser/glic/glic_pref_names.h b/chrome/browser/glic/glic_pref_names.h
index 2282877..6a500406 100644
--- a/chrome/browser/glic/glic_pref_names.h
+++ b/chrome/browser/glic/glic_pref_names.h
@@ -20,9 +20,17 @@
 // ************* PROFILE PREFS ***************
 // Prefs below are tied to a user profile
 
-// Boolean pref that determines whether Glic is enabled for this user profile.
+// Integer pref that determines whether Glic is enabled for this user profile.
 // This is controlled from enterprise policy.
 inline constexpr char kGlicEnabledByPolicy[] = "glic.enabled_by_policy";
+enum class EnabledByPolicyState {
+  kMinValue = 0,
+
+  kEnabled = kMinValue,
+  kDisabled = 1,
+
+  kMaxValue = kDisabled
+};
 
 // Boolean pref that enables or disables geolocation access for Glic.
 inline constexpr char kGlicGeolocationEnabled[] = "glic.geolocation_enabled";
diff --git a/chrome/browser/glic/glic_profile_configuration.cc b/chrome/browser/glic/glic_profile_configuration.cc
index 1585af8..4b918cec 100644
--- a/chrome/browser/glic/glic_profile_configuration.cc
+++ b/chrome/browser/glic/glic_profile_configuration.cc
@@ -36,7 +36,9 @@
 // static
 void GlicProfileConfiguration::RegisterProfilePrefs(
     PrefRegistrySimple* registry) {
-  registry->RegisterBooleanPref(prefs::kGlicEnabledByPolicy, true);
+  registry->RegisterIntegerPref(
+      prefs::kGlicEnabledByPolicy,
+      static_cast<int>(prefs::EnabledByPolicyState::kEnabled));
   registry->RegisterBooleanPref(prefs::kGlicMicrophoneEnabled, false);
   registry->RegisterBooleanPref(prefs::kGlicGeolocationEnabled, false);
   registry->RegisterBooleanPref(prefs::kGlicTabContextEnabled, false);
@@ -44,12 +46,13 @@
 }
 
 bool GlicProfileConfiguration::IsEnabledByPolicy() const {
-  return profile_->GetPrefs()->GetBoolean(prefs::kGlicEnabledByPolicy);
+  return profile_->GetPrefs()->GetInteger(prefs::kGlicEnabledByPolicy) ==
+         static_cast<int>(prefs::EnabledByPolicyState::kEnabled);
 }
 
 void GlicProfileConfiguration::OnEnabledByPolicyChanged() {
   // Note: the pref listener can sometimes fire even if the value from
-  // GetBoolean doesn't change (e.g. value was set from multiple sources). See
+  // GetInteger doesn't change (e.g. value was set from multiple sources). See
   // GlicPolicyTest.PrefDisabledByPolicy for an example.
   for (Browser* const browser : *BrowserList::GetInstance()) {
     if (browser->profile() == &profile_.get()) {
diff --git a/chrome/browser/glic/glic_view.cc b/chrome/browser/glic/glic_view.cc
index bac3c8f..1337bf44 100644
--- a/chrome/browser/glic/glic_view.cc
+++ b/chrome/browser/glic/glic_view.cc
@@ -43,7 +43,6 @@
   views::Widget::InitParams params(
       views::Widget::InitParams::CLIENT_OWNS_WIDGET,
       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
-  params.opacity = views::Widget::InitParams::WindowOpacity::kOpaque;
   params.remove_standard_frame = true;
 #if BUILDFLAG(IS_WIN)
   params.dont_show_in_taskbar = true;
@@ -79,4 +78,10 @@
   return false;
 }
 
+void GlicView::AnimateFrameBounds(const gfx::Rect& bounds) {
+  bounds_change_animation_ =
+      std::make_unique<BrowserFrameBoundsChangeAnimation>(*GetWidget(), bounds);
+  bounds_change_animation_->Start();
+}
+
 }  // namespace glic
diff --git a/chrome/browser/glic/glic_view.h b/chrome/browser/glic/glic_view.h
index 9570bf2..a641d11 100644
--- a/chrome/browser/glic/glic_view.h
+++ b/chrome/browser/glic/glic_view.h
@@ -44,6 +44,9 @@
 
   views::WebView* web_view() { return web_view_; }
 
+  // Sets the bounds of the widget with animation
+  void AnimateFrameBounds(const gfx::Rect& bounds);
+
  private:
   raw_ptr<views::WebView> web_view_;
 
diff --git a/chrome/browser/glic/glic_window_controller.cc b/chrome/browser/glic/glic_window_controller.cc
index 3858a325..27075b7 100644
--- a/chrome/browser/glic/glic_window_controller.cc
+++ b/chrome/browser/glic/glic_window_controller.cc
@@ -43,9 +43,9 @@
 // to a browser's glic button to attach to said browser.
 constexpr static int kAttachmentDistanceThreshold = 50;
 
-constexpr static int kWidgetDefaultWidth = 400;
+constexpr static int kWidgetWidth = 400;
+constexpr static int kWidgetHeight = 800;
 constexpr static int kWidgetTopBarHeight = 80;
-static constexpr int kEntryDurationMs = 300;
 
 class ContentsAndProfileKeepAlive : public content::WebContentsDelegate {
  public:
@@ -216,11 +216,7 @@
   if (glic_window_widget_ || will_show_) {
     return;
   }
-
-  if (!contents_) {
-    contents_ = std::make_unique<ContentsAndProfileKeepAlive>(profile_, this);
-  }
-
+  int padding;
   gfx::Point top_right_point;
   will_show_ = true;
   if (!glic_button_view) {
@@ -229,6 +225,7 @@
     // TODO(crbug.com/384061064): Add more logic for when the glic window should
     // show up in a detached state.
     top_right_point = GetTopRightPositionForDetachedGlicWindow();
+    padding = 50;
     button_widget_for_browser_attachment_ = nullptr;
   } else {
     // If summoned from the tab strip button. This will always show up attached
@@ -236,49 +233,37 @@
     // window.
     top_right_point =
         GetTopRightPositionForAttachedGlicWindow(glic_button_view);
+    padding = GetLayoutConstant(TAB_STRIP_PADDING);
     button_widget_for_browser_attachment_ =
         glic_button_view->GetWidget()->GetWeakPtr();
   }
 
-  gfx::Size default_widget_size(kWidgetDefaultWidth, kWidgetTopBarHeight);
-  if (final_widget_bounds_.IsEmpty()) {
-    final_widget_bounds_.set_size(default_widget_size);
+  glic_window_widget_ = glic::GlicView::CreateWidget(
+      profile_, {top_right_point.x() - kWidgetWidth - padding,
+                 top_right_point.y() + padding, kWidgetWidth, kWidgetHeight});
+
+  GlicView* glic_view = GlicView::FromWidget(*glic_window_widget_);
+  if (!contents_) {
+    contents_ = std::make_unique<ContentsAndProfileKeepAlive>(profile_, this);
   }
-
-  gfx::Rect glic_window_widget_initial_rect =
-      glic_button_view->GetBoundsInScreen();
-
-  glic_window_widget_ =
-      glic::GlicView::CreateWidget(profile_, glic_window_widget_initial_rect);
+  glic_view->web_view()->SetWebContents(contents_->web_contents());
 
   glic_window_widget_->AddObserver(this);
   glic_widget_observer_ =
       std::make_unique<GlicWidgetObserver>(this, glic_window_widget_.get());
 
-  glic_window_widget_->Show();
-
-  if (button_widget_for_browser_attachment_) {
-    Browser* browser = chrome::FindBrowserWithWindow(
-        button_widget_for_browser_attachment_->GetNativeWindow());
-    AttachToBrowser(browser);
-    // Set target bounds for animation and run the open attached animation.
-    gfx::Rect target_bounds = glic_window_widget_->GetWindowBoundsInScreen();
-    int final_x = top_right_point.x() - kWidgetDefaultWidth;
-    target_bounds.set_x(final_x);
-    target_bounds.set_width(kWidgetDefaultWidth);
-    target_bounds.set_height(final_widget_bounds_.height());
-
-    // TODO(crbug.com/389982576): Match the background color of the widget with
-    // the web client background.
-    GetGlicView()->SetBackground(
-        views::CreateRoundedRectBackground(SK_ColorBLACK, 12));
-    AnimateBounds(
-        target_bounds, base::Milliseconds(kEntryDurationMs),
-        base::BindOnce(&GlicWindowController::SetWebContents, GetWeakPtr()));
-  } else {
-    SetWebContents();
-    MaybeCreateHolderWindowAndReparent();
-  }
+  // This is used to handle the case where the native window is closed
+  // directly (e.g., Windows context menu close on the title bar). The widget
+  // can't be deleted by the OnWidgetDestroyed notification because the widget
+  // code doesn't support that.
+  glic_window_widget_->widget_delegate()->RegisterDeleteDelegateCallback(
+      base::BindOnce(
+          [](const base::WeakPtr<GlicWindowController>& weak_this) {
+            if (weak_this) {
+              weak_this->Close();
+            }
+          },
+          weak_ptr_factory_.GetWeakPtr()));
 
   // If the web client is already initialized, go to phase 2. Otherwise, wait
   // for the web client to initialize.
@@ -306,20 +291,31 @@
 
 void GlicWindowController::ShowFinish() {
   will_show_ = false;
-  if (!glic_window_widget_) {
+  if (!glic_window_widget_ || glic_window_widget_->IsVisible()) {
     return;
   }
 
+  if (button_widget_for_browser_attachment_) {
+    glic_window_widget_->Show();
+    Browser* browser = chrome::FindBrowserWithWindow(
+        button_widget_for_browser_attachment_->GetNativeWindow());
+    AttachToBrowser(browser);
+  } else {
+    // Be sure to reparent the widget and set its state first before showing it.
+    MaybeCreateHolderWindowAndReparent();
+#if BUILDFLAG(IS_MAC)
+    // Be careful to not activate, so that in case Chromium isn't the front-most
+    // app it's not brought to the front.
+    glic_window_widget_->ShowInactive();
+#else
+    glic_window_widget_->Show();
+#endif
+  }
+
   window_event_observer_ =
       std::make_unique<WindowEventObserver>(this, GetGlicView());
-
   // Set the draggable area to the top bar of the window, by default.
-  GetGlicView()->SetDraggableAreas(
-      {{0, 0, final_widget_bounds_.width(), kWidgetTopBarHeight}});
-}
-
-void GlicWindowController::SetWebContents() {
-  GetGlicView()->web_view()->SetWebContents(contents_->web_contents());
+  GetGlicView()->SetDraggableAreas({{0, 0, kWidgetWidth, kWidgetTopBarHeight}});
 }
 
 GlicView* GlicWindowController::GetGlicView() {
@@ -376,11 +372,9 @@
   // moves to the top right of the display.
   gfx::Size screen_size =
       display::Screen::GetScreen()->GetPrimaryDisplay().GetSizeInPixel();
-  final_widget_bounds_.set_origin(
-      gfx::Point(screen_size.width() - final_widget_bounds_.width(), 0));
-  AnimateBounds(
-      final_widget_bounds_, base::Milliseconds(kEntryDurationMs),
-      base::BindOnce(&GlicWindowController::ResizeFinished, GetWeakPtr()));
+  gfx::Rect bounds = glic_window_widget_->GetWindowBoundsInScreen();
+  bounds.set_origin(gfx::Point(screen_size.width() - bounds.width(), 0));
+  GetGlicView()->AnimateFrameBounds(bounds);
 }
 
 void GlicWindowController::AttachToBrowser(Browser* browser) {
@@ -414,30 +408,14 @@
 }
 
 bool GlicWindowController::Resize(const gfx::Size& size) {
-  final_widget_bounds_.set_size(size);
-
   if (!glic_window_widget_) {
     return false;
   }
-
-  AnimateBounds(
-      final_widget_bounds_, base::Milliseconds(0),
-      base::BindOnce(&GlicWindowController::ResizeFinished, GetWeakPtr()));
-
-  return true;
-}
-
-void GlicWindowController::AnimateBounds(const gfx::Rect& target_bounds,
-                                         base::TimeDelta duration,
-                                         base::OnceClosure callback) {
-  if (!glic_window_widget_) {
-    return;
-  }
-
   // TODO(iwells): Set animation duration based on value set by client.
   window_resize_animation_ = std::make_unique<GlicWindowResizeAnimation>(
-      glic_window_widget_.get(), target_bounds,
-      /*duration=*/duration, std::move(callback));
+      glic_window_widget_.get(), size, /*duration=*/base::Milliseconds(0),
+      base::BindOnce(&GlicWindowController::ResizeFinished, GetWeakPtr()));
+  return true;
 }
 
 gfx::Size GlicWindowController::GetSize() {
@@ -592,21 +570,21 @@
   CHECK(glic_button);
 
   attached_browser_ = browser->AsWeakPtr();
+  gfx::Rect glic_rect = glic_window_widget_->GetWindowBoundsInScreen();
   // TODO(andreaxg): Fix exact attachment position.
   gfx::Rect glic_button_rect = glic_button->GetBoundsInScreen();
   gfx::Point top_right = glic_button_rect.top_right();
   int tab_strip_padding = GetLayoutConstant(TAB_STRIP_PADDING);
-  final_widget_bounds_.set_x(top_right.x() - final_widget_bounds_.width() -
-                             tab_strip_padding);
-  final_widget_bounds_.set_y(top_right.y() + tab_strip_padding);
+  glic_rect.set_x(top_right.x() - glic_rect.width() - tab_strip_padding);
+  glic_rect.set_y(top_right.y() + tab_strip_padding);
   // Avoid conversions between pixels and DIP on non 1.0 scale factor displays
   // changing widget width and height.
+  glic_rect.set_width(kWidgetWidth);
+  glic_rect.set_height(kWidgetHeight);
   if (animate) {
-    AnimateBounds(
-        final_widget_bounds_, base::Milliseconds(kEntryDurationMs),
-        base::BindOnce(&GlicWindowController::SetWebContents, GetWeakPtr()));
+    GetGlicView()->AnimateFrameBounds(glic_rect);
   } else {
-    glic_window_widget_->SetBounds(final_widget_bounds_);
+    glic_window_widget_->SetBounds(glic_rect);
   }
   NotifyIfPanelStateChanged();
 }
diff --git a/chrome/browser/glic/glic_window_controller.h b/chrome/browser/glic/glic_window_controller.h
index 1078700..77942130 100644
--- a/chrome/browser/glic/glic_window_controller.h
+++ b/chrome/browser/glic/glic_window_controller.h
@@ -54,8 +54,6 @@
   // shows the glic window.
   void Show(views::View* glic_button_view);
 
-  void OnShowAnimationCompleted();
-
   // Attaches glic to the last focused Chrome window.
   void Attach();
 
@@ -127,8 +125,6 @@
   void ShowPhase2();
   void ShowFinish();
 
-  void SetWebContents();
-
   // Determines the correct position for the glic window when attached to a
   // browser window.
   gfx::Point GetTopRightPositionForAttachedGlicWindow(
@@ -203,13 +199,6 @@
   // Called when the programmatic resize has finished.
   void ResizeFinished();
 
-  // Sets target bounds for the widget and creates a WindowResizeAnimation
-  // instance to begin a new animation. Blocks any calls to animate if the
-  // widget it not yet visible.
-  void AnimateBounds(const gfx::Rect& target_bounds,
-                     base::TimeDelta duration,
-                     base::OnceClosure callback);
-
   AttachedTargetWidgetObserver attached_target_widget_observer_{this};
   base::WeakPtr<Browser> attached_browser_;
 
@@ -234,8 +223,6 @@
   std::unique_ptr<GlicWindowResizeAnimation> window_resize_animation_;
   bool glic_window_widget_visible_ = false;
 
-  gfx::Rect final_widget_bounds_;
-
   // Indicates `Show()` has been called, but not `FinishShow()`.
   bool will_show_ = false;
   // While `will_show_` is true, this is the button widget on the browser window
diff --git a/chrome/browser/glic/glic_window_resize_animation.cc b/chrome/browser/glic/glic_window_resize_animation.cc
index e1214d0b..395d859 100644
--- a/chrome/browser/glic/glic_window_resize_animation.cc
+++ b/chrome/browser/glic/glic_window_resize_animation.cc
@@ -19,13 +19,13 @@
 
 GlicWindowResizeAnimation::GlicWindowResizeAnimation(
     views::Widget* widget,
-    const gfx::Rect& target_bounds,
+    gfx::Size new_size,
     base::TimeDelta duration,
     FinishedCallback finished_callback)
     : gfx::LinearAnimation(duration, kDefaultFrameRate, this),
       widget_(widget),
-      initial_bounds_(widget->GetWindowBoundsInScreen()),
-      new_bounds_(target_bounds),
+      initial_size_(widget->GetWindowBoundsInScreen().size()),
+      new_size_(new_size),
       finished_callback_(std::move(finished_callback)) {
   // TODO(crbug.com/389238233): CompositorAnimationRunner does not appear to
   // be fully functional.
@@ -41,9 +41,9 @@
 GlicWindowResizeAnimation::~GlicWindowResizeAnimation() = default;
 
 void GlicWindowResizeAnimation::AnimateToState(double state) {
-  widget_->SetBounds(gfx::Tween::RectValueBetween(
+  widget_->SetSize(gfx::Tween::SizeValueBetween(
       gfx::Tween::CalculateValue(gfx::Tween::EASE_IN_OUT_EMPHASIZED, state),
-      initial_bounds_, new_bounds_));
+      initial_size_, new_size_));
 }
 
 void GlicWindowResizeAnimation::AnimationEnded(const Animation* animation) {
diff --git a/chrome/browser/glic/glic_window_resize_animation.h b/chrome/browser/glic/glic_window_resize_animation.h
index 8ad2d0b02..5c255fd5 100644
--- a/chrome/browser/glic/glic_window_resize_animation.h
+++ b/chrome/browser/glic/glic_window_resize_animation.h
@@ -30,7 +30,7 @@
   // FinishedCallback. FinishedCallback is always invoked asynchronously.
   using FinishedCallback = base::OnceClosure;
   GlicWindowResizeAnimation(views::Widget* widget,
-                            const gfx::Rect& target_bounds,
+                            gfx::Size new_size,
                             base::TimeDelta duration,
                             FinishedCallback finished_callback);
   GlicWindowResizeAnimation(const GlicWindowResizeAnimation&) = delete;
@@ -43,8 +43,8 @@
 
  private:
   const raw_ptr<views::Widget> widget_;
-  const gfx::Rect initial_bounds_;
-  const gfx::Rect new_bounds_;
+  const gfx::Size initial_size_;
+  const gfx::Size new_size_;
   FinishedCallback finished_callback_;
   base::WeakPtrFactory<GlicWindowResizeAnimation> weak_ptr_factory_{this};
 };
diff --git a/chrome/browser/glic/launcher/OWNERS b/chrome/browser/glic/launcher/OWNERS
new file mode 100644
index 0000000..f6b08bc
--- /dev/null
+++ b/chrome/browser/glic/launcher/OWNERS
@@ -0,0 +1,4 @@
+agale@chromium.org
+charlesmeng@chromium.org
+estalin@chromium.org
+stluong@chromium.org
diff --git a/chrome/browser/glic/launcher/glic_background_mode_manager.cc b/chrome/browser/glic/launcher/glic_background_mode_manager.cc
index 254247e9..821114b 100644
--- a/chrome/browser/glic/launcher/glic_background_mode_manager.cc
+++ b/chrome/browser/glic/launcher/glic_background_mode_manager.cc
@@ -33,8 +33,9 @@
     : configuration_(std::make_unique<GlicLauncherConfiguration>(this)),
       controller_(std::make_unique<GlicController>()),
       status_tray_(status_tray),
-      enabled_pref_(configuration_->IsEnabled()),
-      expected_registered_hotkey_(configuration_->GetGlobalHotkey()) {
+      enabled_pref_(GlicLauncherConfiguration::IsEnabled()),
+      expected_registered_hotkey_(
+          GlicLauncherConfiguration::GetGlobalHotkey()) {
   UpdateState();
   g_browser_process->profile_manager()->AddObserver(this);
 }
diff --git a/chrome/browser/glic/launcher/glic_launcher_configuration.cc b/chrome/browser/glic/launcher/glic_launcher_configuration.cc
index 5e66fc4e4..ba188673 100644
--- a/chrome/browser/glic/launcher/glic_launcher_configuration.cc
+++ b/chrome/browser/glic/launcher/glic_launcher_configuration.cc
@@ -50,11 +50,13 @@
           .Set(kHotkeyModifiers, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN));
 }
 
+// static
 bool GlicLauncherConfiguration::IsEnabled() {
   return g_browser_process->local_state()->GetBoolean(
       prefs::kGlicLauncherEnabled);
 }
 
+// static
 ui::Accelerator GlicLauncherConfiguration::GetGlobalHotkey() {
   const base::Value::Dict& hotkey_dictionary =
       g_browser_process->local_state()->GetDict(
diff --git a/chrome/browser/glic/launcher/glic_launcher_configuration.h b/chrome/browser/glic/launcher/glic_launcher_configuration.h
index d36a98e..653e41b9 100644
--- a/chrome/browser/glic/launcher/glic_launcher_configuration.h
+++ b/chrome/browser/glic/launcher/glic_launcher_configuration.h
@@ -32,9 +32,9 @@
 
   static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
 
-  bool IsEnabled();
+  static bool IsEnabled();
 
-  ui::Accelerator GetGlobalHotkey();
+  static ui::Accelerator GetGlobalHotkey();
 
  private:
   void OnEnabledPrefChanged();
diff --git a/chrome/browser/glic/launcher/glic_status_icon.cc b/chrome/browser/glic/launcher/glic_status_icon.cc
index 2d9398c45..60175c61 100644
--- a/chrome/browser/glic/launcher/glic_status_icon.cc
+++ b/chrome/browser/glic/launcher/glic_status_icon.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/glic/launcher/glic_status_icon.h"
 
+#include <memory>
 #include <optional>
 
 #include "chrome/app/chrome_command_ids.h"
@@ -90,8 +91,10 @@
   context_menu_ = nullptr;
   if (status_icon_) {
     status_icon_->RemoveObserver(this);
-    status_tray_->RemoveStatusIcon(status_icon_);
+    std::unique_ptr<StatusIcon> removed_icon =
+        status_tray_->RemoveStatusIcon(status_icon_);
     status_icon_ = nullptr;
+    removed_icon.reset();
   }
   status_tray_ = nullptr;
 }
diff --git a/chrome/browser/glic/launcher/glic_status_icon.h b/chrome/browser/glic/launcher/glic_status_icon.h
index bccd32b..a374636 100644
--- a/chrome/browser/glic/launcher/glic_status_icon.h
+++ b/chrome/browser/glic/launcher/glic_status_icon.h
@@ -47,8 +47,7 @@
       native_theme_observer_{this};
 
   raw_ptr<StatusTray> status_tray_;
-  // TODO(crbug.com/390463341): Figure out how to not dangle this pointer.
-  raw_ptr<StatusIcon, DanglingUntriaged> status_icon_;
+  raw_ptr<StatusIcon> status_icon_;
   raw_ptr<StatusIconMenuModel> context_menu_;
 };
 
diff --git a/chrome/browser/history/java/src/org/chromium/chrome/browser/history/AppFilterCoordinatorTest.java b/chrome/browser/history/java/src/org/chromium/chrome/browser/history/AppFilterCoordinatorTest.java
index d3a6a3b5..3d4d089 100644
--- a/chrome/browser/history/java/src/org/chromium/chrome/browser/history/AppFilterCoordinatorTest.java
+++ b/chrome/browser/history/java/src/org/chromium/chrome/browser/history/AppFilterCoordinatorTest.java
@@ -11,7 +11,6 @@
 import static org.chromium.chrome.browser.history.AppFilterCoordinator.MAX_VISIBLE_ITEM_COUNT;
 
 import android.app.Activity;
-import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.view.ViewGroup;
 
@@ -98,10 +97,7 @@
         ViewGroup activityContentView = getActivity().findViewById(android.R.id.content);
         ScrimCoordinator scrimCoordinator =
                 new ScrimCoordinator(
-                        getActivity(),
-                        /* systemUiScrimDelegate= */ null,
-                        activityContentView,
-                        Color.WHITE);
+                        getActivity(), /* systemUiScrimDelegate= */ null, activityContentView);
         return BottomSheetControllerFactory.createBottomSheetController(
                 () -> scrimCoordinator,
                 (unused) -> {},
diff --git a/chrome/browser/hub/BUILD.gn b/chrome/browser/hub/BUILD.gn
index 44c772877..5fdef2d6 100644
--- a/chrome/browser/hub/BUILD.gn
+++ b/chrome/browser/hub/BUILD.gn
@@ -140,5 +140,6 @@
     "//third_party/mockito:mockito_java",
     "//ui/android:ui_java_test_support",
     "//ui/android:ui_no_recycler_view_java",
+    "//ui/android:ui_unit_device_javatests",
   ]
 }
diff --git a/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/HubLayoutScrimController.java b/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/HubLayoutScrimController.java
index 532b795..206963c 100644
--- a/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/HubLayoutScrimController.java
+++ b/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/HubLayoutScrimController.java
@@ -76,7 +76,9 @@
     }
 
     private void onIncognitoChange(Boolean ignored) {
-        mPropertyModel.set(ScrimProperties.BACKGROUND_COLOR, calculateScrimColor());
+        if (mPropertyModel == null) return;
+
+        mScrimCoordinator.setScrimColor(calculateScrimColor(), mPropertyModel);
     }
 
     private @ColorInt int calculateScrimColor() {
diff --git a/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/HubLayoutScrimControllerUnitTest.java b/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/HubLayoutScrimControllerUnitTest.java
index 0490c52..d759f0c 100644
--- a/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/HubLayoutScrimControllerUnitTest.java
+++ b/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/HubLayoutScrimControllerUnitTest.java
@@ -13,7 +13,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.Activity;
-import android.graphics.Color;
 import android.view.View;
 import android.widget.FrameLayout;
 
@@ -73,8 +72,7 @@
         mAnchorView = new View(mActivity);
         rootView.addView(mAnchorView);
 
-        mScrimCoordinator =
-                spy(new ScrimCoordinator(mActivity, mScrimDelegate, rootView, Color.RED));
+        mScrimCoordinator = spy(new ScrimCoordinator(mActivity, mScrimDelegate, rootView));
 
         mIsIncognitoSupplier = new ObservableSupplierImpl<>(false);
 
diff --git a/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/ShrinkExpandAnimatorRenderTest.java b/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/ShrinkExpandAnimatorRenderTest.java
index 30408f3..0d138c9 100644
--- a/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/ShrinkExpandAnimatorRenderTest.java
+++ b/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/ShrinkExpandAnimatorRenderTest.java
@@ -37,12 +37,11 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.Feature;
 import org.chromium.base.test.util.Restriction;
+import org.chromium.ui.animation.RenderTestAnimationUtils;
 import org.chromium.ui.test.util.BlankUiTestActivity;
 import org.chromium.ui.test.util.NightModeTestUtils;
 import org.chromium.ui.test.util.RenderTestRule;
 
-import java.util.Locale;
-
 /** Render tests for {@link ShrinkExpandAnimator}. */
 // TODO(crbug.com/40286625): Move to hub/internal/ once TabSwitcherLayout no longer depends on this.
 @RunWith(BaseJUnit4ClassRunner.class)
@@ -67,6 +66,12 @@
     private FrameLayout mRootView;
     private ShrinkExpandImageView mView;
 
+    // Retains a strong reference to the {@link ShrinkExpandAnimator} on the class to prevent it
+    // from being prematurely GC'd during {@link RenderTestAnimationUtils#stepThroughAnimation}. The
+    // {@link ObjectAnimator} only retains a {@link java.lang.ref.WeakReference} to the object,
+    //  meaning it could be GC'd anytime after changing stack frames.
+    private ShrinkExpandAnimator mAnimator;
+
     @BeforeClass
     public static void setupSuite() {
         sActivity = sActivityTestRule.launchActivity(null);
@@ -121,13 +126,21 @@
                         startX + thumbnailSize.getWidth(),
                         startY + thumbnailSize.getHeight());
 
-        setupShrinkExpandImageView(startValue);
-        ShrinkExpandAnimator animator =
-                createAnimator(startValue, endValue, thumbnailSize, /* searchBoxHeight= */ 0);
-
         Rect startValueCopy = new Rect(startValue);
         Rect endValueCopy = new Rect(endValue);
 
+        setupShrinkExpandImageView(startValue);
+        mAnimator = createAnimator(startValue, endValue, thumbnailSize, /* searchBoxHeight= */ 0);
+        ObjectAnimator expandAnimator =
+                ThreadUtils.runOnUiThreadBlocking(
+                        () ->
+                                ObjectAnimator.ofObject(
+                                        mAnimator,
+                                        ShrinkExpandAnimator.RECT,
+                                        new RectEvaluator(),
+                                        startValueCopy,
+                                        endValueCopy));
+
         // Verify changing rects after doesn't cause ShrinkExpandAnimator to behave differently.
         startValue.left = 0;
         startValue.right = 10;
@@ -138,8 +151,8 @@
         endValue.top = 100;
         endValue.bottom = 200;
 
-        stepThroughAnimation(
-                "expand_rect", animator, startValueCopy, endValueCopy, ANIMATION_STEPS);
+        RenderTestAnimationUtils.stepThroughAnimation(
+                "expand_rect", mRenderTestRule, mRootView, expandAnimator, ANIMATION_STEPS);
     }
 
     @Test
@@ -162,11 +175,23 @@
                         Math.round(thumbnailSize.getHeight() / 2.0f));
 
         setupShrinkExpandImageView(startValue);
-        ShrinkExpandAnimator animator =
-                createAnimator(startValue, endValue, thumbnailSize, /* searchBoxHeight= */ 0);
+        mAnimator = createAnimator(startValue, endValue, thumbnailSize, /* searchBoxHeight= */ 0);
+        ObjectAnimator expandAnimator =
+                ThreadUtils.runOnUiThreadBlocking(
+                        () ->
+                                ObjectAnimator.ofObject(
+                                        mAnimator,
+                                        ShrinkExpandAnimator.RECT,
+                                        new RectEvaluator(),
+                                        startValue,
+                                        endValue));
 
-        stepThroughAnimation(
-                "expand_rect_with_top_clip", animator, startValue, endValue, ANIMATION_STEPS);
+        RenderTestAnimationUtils.stepThroughAnimation(
+                "expand_rect_with_top_clip",
+                mRenderTestRule,
+                mRootView,
+                expandAnimator,
+                ANIMATION_STEPS);
     }
 
     @Test
@@ -190,14 +215,22 @@
                         Math.round(thumbnailSize.getHeight() / 2.0f));
 
         setupShrinkExpandImageView(startValue);
-        ShrinkExpandAnimator animator =
-                createAnimator(startValue, endValue, thumbnailSize, SEARCH_BOX_HEIGHT);
+        mAnimator = createAnimator(startValue, endValue, thumbnailSize, SEARCH_BOX_HEIGHT);
+        ObjectAnimator expandAnimator =
+                ThreadUtils.runOnUiThreadBlocking(
+                        () ->
+                                ObjectAnimator.ofObject(
+                                        mAnimator,
+                                        ShrinkExpandAnimator.RECT,
+                                        new RectEvaluator(),
+                                        startValue,
+                                        endValue));
 
-        stepThroughAnimation(
+        RenderTestAnimationUtils.stepThroughAnimation(
                 "expand_rect_with_top_clip_hub_search",
-                animator,
-                startValue,
-                endValue,
+                mRenderTestRule,
+                mRootView,
+                expandAnimator,
                 ANIMATION_STEPS);
     }
 
@@ -222,10 +255,19 @@
                         endY + thumbnailSize.getHeight());
 
         setupShrinkExpandImageView(startValue);
-        ShrinkExpandAnimator animator =
-                createAnimator(startValue, endValue, thumbnailSize, /* searchBoxHeight= */ 0);
+        mAnimator = createAnimator(startValue, endValue, thumbnailSize, /* searchBoxHeight= */ 0);
+        ObjectAnimator shrinkAnimator =
+                ThreadUtils.runOnUiThreadBlocking(
+                        () ->
+                                ObjectAnimator.ofObject(
+                                        mAnimator,
+                                        ShrinkExpandAnimator.RECT,
+                                        new RectEvaluator(),
+                                        startValue,
+                                        endValue));
 
-        stepThroughAnimation("shrink_rect_rect", animator, startValue, endValue, ANIMATION_STEPS);
+        RenderTestAnimationUtils.stepThroughAnimation(
+                "shrink_rect_rect", mRenderTestRule, mRootView, shrinkAnimator, ANIMATION_STEPS);
     }
 
     private ShrinkExpandAnimator createAnimator(
@@ -240,59 +282,13 @@
                 });
     }
 
-    /** Returns a thumbnail size 1/4 the size of {@link mRootView}. */
+    /** Returns a thumbnail size 1/4 the size of {@link #mRootView}. */
     private Size getThumbnailSize() {
         return new Size(
                 Math.round(mRootView.getWidth() / 4.0f), Math.round(mRootView.getHeight() / 4.0f));
     }
 
     /**
-     * Steps through an animation.
-     *
-     * @param testcaseName The base name for the render test results.
-     * @param rectAnimator The animator to drive the animation of.
-     * @param startValue The initial react.
-     * @param endValue The final rect.
-     * @param steps The number of steps to take. Must be 2 or more.
-     */
-    private void stepThroughAnimation(
-            String testcaseName,
-            ShrinkExpandAnimator rectAnimator,
-            Rect startValue,
-            Rect endValue,
-            int steps)
-            throws Exception {
-        assert steps >= 2;
-        float fractionPerStep = 1.0f / (steps - 1);
-
-        ObjectAnimator animator =
-                ThreadUtils.runOnUiThreadBlocking(
-                        () -> {
-                            return ObjectAnimator.ofObject(
-                                    rectAnimator,
-                                    ShrinkExpandAnimator.RECT,
-                                    new RectEvaluator(),
-                                    startValue,
-                                    endValue);
-                        });
-
-        // Manually drive the animation instead of using an ObjectAnimator for exact control over
-        // step size and timing.
-        for (int step = 0; step < steps; step++) {
-            final float animationFraction = fractionPerStep * step;
-            ThreadUtils.runOnUiThreadBlocking(
-                    () -> {
-                        animator.setCurrentFraction(animationFraction);
-                    });
-
-            mRenderTestRule.render(
-                    mRootView,
-                    testcaseName
-                            + String.format(Locale.ENGLISH, "_step_%d_of_%d", step + 1, steps));
-        }
-    }
-
-    /**
      * Sets the initial position of the image view.
      *
      * @param startValue The rect to position the image view at.
@@ -321,7 +317,7 @@
         onNextLayout.waitForNext();
     }
 
-    /** Returns a blue checkerboard bitmap the size of {@link mRootView}. */
+    /** Returns a blue checkerboard bitmap the size of {@link #mRootView}. */
     private Bitmap createBitmap() {
         int width = mRootView.getWidth();
         int height = mRootView.getHeight();
diff --git a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubToolbarView.java b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubToolbarView.java
index 6b25f3b..2e82cd1 100644
--- a/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubToolbarView.java
+++ b/chrome/browser/hub/internal/android/java/src/org/chromium/chrome/browser/hub/HubToolbarView.java
@@ -5,11 +5,14 @@
 package org.chromium.chrome.browser.hub;
 
 import static org.chromium.chrome.browser.hub.HubAnimationConstants.PANE_COLOR_BLEND_ANIMATION_DURATION_MS;
+import static org.chromium.chrome.browser.hub.HubAnimationConstants.PANE_FADE_ANIMATION_DURATION_MS;
 import static org.chromium.chrome.browser.hub.HubAnimationConstants.getPaneColorBlendInterpolator;
 import static org.chromium.ui.util.ColorBlendAnimationFactory.createMultiColorBlendAnimation;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.drawable.Drawable;
@@ -56,12 +59,14 @@
     private OnTabSelectedListener mOnTabSelectedListener;
     private boolean mBlockTabSelectionCallback;
     private final AnimationHandler mColorBlendAnimatorHandler;
+    private final AnimationHandler mHubSearchAnimatorHandler;
     private final HubColorBlendAnimatorSetHelper mAnimatorSetBuilder;
 
     /** Default {@link LinearLayout} constructor called by inflation. */
     public HubToolbarView(Context context, AttributeSet attributeSet) {
         super(context, attributeSet);
         mColorBlendAnimatorHandler = new AnimationHandler();
+        mHubSearchAnimatorHandler = new AnimationHandler();
         mAnimatorSetBuilder = new HubColorBlendAnimatorSetHelper();
         mToolbarOverviewColorSetter = (color) -> {};
     }
@@ -209,6 +214,14 @@
                             ImageViewCompat.setImageTintList(mMenuButton, menuButtonColor);
                         }));
 
+        // We don't want to pass a method reference. Lambdas will ensure we run the most recent
+        // setter.
+        mAnimatorSetBuilder.registerBlend(
+                new SingleHubViewColorBlend(
+                        PANE_COLOR_BLEND_ANIMATION_DURATION_MS,
+                        colorScheme -> HubColors.getBackgroundColor(context, colorScheme),
+                        color -> mToolbarOverviewColorSetter.onResult(color)));
+
         // TODO(crbug.com/40948541): Updating the app menu color here is more correct and
         // should be done for code health. Menu Button Color is also set by
         // HubToolbarCoordinator.
@@ -236,14 +249,6 @@
                         PANE_COLOR_BLEND_ANIMATION_DURATION_MS,
                         colorScheme -> HubColors.getIconColor(context, colorScheme),
                         this::updateSearchLoupeColor));
-
-        // We don't want to pass a method reference. Lambdas will ensure we run the most recent
-        // setter.
-        mAnimatorSetBuilder.registerBlend(
-                new SingleHubViewColorBlend(
-                        PANE_COLOR_BLEND_ANIMATION_DURATION_MS,
-                        colorScheme -> HubColors.getBackgroundColor(context, colorScheme),
-                        color -> mToolbarOverviewColorSetter.onResult(color)));
     }
 
     private void updateTabIconTintInternal(
@@ -269,7 +274,25 @@
     }
 
     void setSearchBoxVisible(boolean visible) {
-        mSearchBoxLayout.setVisibility(visible ? View.VISIBLE : View.GONE);
+        AnimatorSet hubSearchTransitionAnimation = getHubSearchBoxTransitionAnimation(visible);
+        AnimatorListenerAdapter animationListener =
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        if (visible) {
+                            mSearchBoxLayout.setVisibility(View.VISIBLE);
+                        }
+                    }
+
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        if (!visible) {
+                            mSearchBoxLayout.setVisibility(View.GONE);
+                        }
+                    }
+                };
+        hubSearchTransitionAnimation.addListener(animationListener);
+        mHubSearchAnimatorHandler.startAnimation(hubSearchTransitionAnimation);
     }
 
     public void setSearchLoupeVisible(boolean visible) {
@@ -324,4 +347,18 @@
                         : R.string.hub_search_empty_hint;
         mSearchBoxTextView.setHint(context.getString(emptyHintRes));
     }
+
+    private AnimatorSet getHubSearchBoxTransitionAnimation(boolean visible) {
+        float fadeAlphaFrom = visible ? 0 : 1;
+        float fadeAlphaTo = visible ? 1 : 0;
+        float slideTransitionY = visible ? 0 : -mSearchBoxLayout.getHeight();
+        Animator fade =
+                ObjectAnimator.ofFloat(mSearchBoxLayout, View.ALPHA, fadeAlphaFrom, fadeAlphaTo);
+        Animator slide =
+                ObjectAnimator.ofFloat(mSearchBoxLayout, View.TRANSLATION_Y, slideTransitionY);
+        AnimatorSet slideFadeHubSearchBoxAnimator = new AnimatorSet();
+        slideFadeHubSearchBoxAnimator.play(slide).with(fade);
+        slideFadeHubSearchBoxAnimator.setDuration(PANE_FADE_ANIMATION_DURATION_MS);
+        return slideFadeHubSearchBoxAnimator;
+    }
 }
diff --git a/chrome/browser/image_descriptions/android/java/src/org/chromium/chrome/browser/image_descriptions/ImageDescriptionsDialog.java b/chrome/browser/image_descriptions/android/java/src/org/chromium/chrome/browser/image_descriptions/ImageDescriptionsDialog.java
index 9f85ba5..99e05c1 100644
--- a/chrome/browser/image_descriptions/android/java/src/org/chromium/chrome/browser/image_descriptions/ImageDescriptionsDialog.java
+++ b/chrome/browser/image_descriptions/android/java/src/org/chromium/chrome/browser/image_descriptions/ImageDescriptionsDialog.java
@@ -155,12 +155,9 @@
                     }
 
                     @Override
-                    public void onDestroy() {
-                        // If no dismissal cause has been set, web contents were destroyed.
-                        if (mDismissalCause == DialogDismissalCause.UNKNOWN) {
-                            mDismissalCause = DialogDismissalCause.WEB_CONTENTS_DESTROYED;
-                        }
-                        dismiss();
+                    public void webContentsDestroyed() {
+                        mDismissalCause = DialogDismissalCause.WEB_CONTENTS_DESTROYED;
+                        unregisterObserverAndDismiss();
                     }
                 };
 
@@ -273,7 +270,8 @@
      * or on user action. The call to #destroy() will also dismiss the dialog.
      */
     private void unregisterObserverAndDismiss() {
-        mWebContentsObserver.destroy();
+        mWebContentsObserver.observe(null);
+        dismiss();
     }
 
     /** Helper method to display this dialog. */
diff --git a/chrome/browser/media/webrtc/media_stream_capture_indicator.cc b/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
index 961b1a2f..399b290 100644
--- a/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
+++ b/chrome/browser/media/webrtc/media_stream_capture_indicator.cc
@@ -675,8 +675,10 @@
 
   StatusTray* status_tray = g_browser_process->status_tray();
   if (status_tray != nullptr) {
-    status_tray->RemoveStatusIcon(status_icon_);
+    std::unique_ptr<StatusIcon> removed_icon =
+        status_tray->RemoveStatusIcon(status_icon_);
     status_icon_ = nullptr;
+    removed_icon.reset();
   }
 }
 
diff --git a/chrome/browser/media/webrtc/media_stream_capture_indicator.h b/chrome/browser/media/webrtc/media_stream_capture_indicator.h
index 327b5a14..84b8ee0 100644
--- a/chrome/browser/media/webrtc/media_stream_capture_indicator.h
+++ b/chrome/browser/media/webrtc/media_stream_capture_indicator.h
@@ -173,7 +173,7 @@
 
   // Reference to our status icon - owned by the StatusTray. If null,
   // the platform doesn't support status icons.
-  raw_ptr<StatusIcon, DanglingUntriaged> status_icon_ = nullptr;
+  raw_ptr<StatusIcon> status_icon_ = nullptr;
 
   // A map that contains the usage counts of the opened capture devices for each
   // WebContents instance.
diff --git a/chrome/browser/nearby_sharing/nearby_share_settings.cc b/chrome/browser/nearby_sharing/nearby_share_settings.cc
index cea2bfd9..a0785b6 100644
--- a/chrome/browser/nearby_sharing/nearby_share_settings.cc
+++ b/chrome/browser/nearby_sharing/nearby_share_settings.cc
@@ -52,6 +52,14 @@
     base::UmaHistogramEnumeration("Nearby.Share.VisibilityChoice",
                                   GetVisibility());
   }
+
+  // In Quick Share v2, the 'Selected contacts' visibility is deprecated. Set
+  // user visibility, if in 'Selected contacts', to 'Your devices'.
+  if (chromeos::features::IsQuickShareV2Enabled()) {
+    if (GetVisibility() == nearby_share::mojom::Visibility::kSelectedContacts) {
+      SetVisibility(nearby_share::mojom::Visibility::kYourDevices);
+    }
+  }
 }
 
 NearbyShareSettings::~NearbyShareSettings() {
diff --git a/chrome/browser/nearby_sharing/nearby_share_settings_unittest.cc b/chrome/browser/nearby_sharing/nearby_share_settings_unittest.cc
index 3b37649..f40d2165 100644
--- a/chrome/browser/nearby_sharing/nearby_share_settings_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_share_settings_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/feature_list.h"
 #include "base/run_loop.h"
 #include "base/test/bind.h"
+#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/nearby_sharing/common/nearby_share_features.h"
 #include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h"
 #include "chrome/browser/nearby_sharing/local_device_data/fake_nearby_share_local_device_data_manager.h"
@@ -116,6 +117,7 @@
   FakeNearbyShareSettingsObserver observer_;
   std::unique_ptr<NearbyShareSettings> nearby_share_settings_;
   std::unique_ptr<NearbyShareSettingsAsyncWaiter> nearby_share_settings_waiter_;
+  base::test::ScopedFeatureList feature_list_;
 };
 
 TEST_F(NearbyShareSettingsTest, GetAndSetEnabled) {
@@ -343,3 +345,18 @@
   settings_waiter()->GetAllowedContacts(&allowed_contacts);
   EXPECT_EQ(0u, allowed_contacts.size());
 }
+
+TEST_F(NearbyShareSettingsTest, QuickShareV2_SomeContacts_ToYourDevices) {
+  feature_list_.InitAndEnableFeature(chromeos::features::kQuickShareV2);
+  pref_service_.SetInteger(
+      prefs::kNearbySharingBackgroundVisibilityName,
+      static_cast<int>(nearby_share::mojom::Visibility::kSelectedContacts));
+
+  // Since some contacts -> your devices occurs in constructor, new
+  // NearbyShareSettings object must be created after QuickShareV2 is enabled.
+  NearbyShareSettings nearby_share_settings(&pref_service_,
+                                            &local_device_data_manager_);
+
+  EXPECT_EQ(nearby_share_settings.GetVisibility(),
+            nearby_share::mojom::Visibility::kYourDevices);
+}
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
index d9fec9b..37d1787 100644
--- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
+++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
@@ -5461,9 +5461,9 @@
 
 TEST_P(
     NearbySharingServiceImplTest,
-    SelfShareEnabled_YourDevicesVisibilityOnScreenLock_DefaultSelectedContactsVisibility) {
+    SelfShareEnabled_YourDevicesVisibilityOnScreenLock_DefaultAllContactsVisibility) {
   const std::set<std::string> contacts = {"1", "2"};
-  SetVisibility(nearby_share::mojom::Visibility::kSelectedContacts);
+  SetVisibility(nearby_share::mojom::Visibility::kAllContacts);
   contact_manager()->SetAllowedContacts(contacts);
 
   // Lock screen, expect Your Devices visibility.
@@ -5473,8 +5473,7 @@
 
   // Unlock screen, expect visibility to return to All Contacts.
   session_controller_->SetScreenLocked(false);
-  EXPECT_EQ(nearby_share::mojom::Visibility::kSelectedContacts,
-            GetVisibility());
+  EXPECT_EQ(nearby_share::mojom::Visibility::kAllContacts, GetVisibility());
   EXPECT_EQ(contacts, contact_manager()->GetAllowedContacts());
 }
 
diff --git a/chrome/browser/net/cookie_policy_browsertest.cc b/chrome/browser/net/cookie_policy_browsertest.cc
index a47c752c..1e3a0b8e 100644
--- a/chrome/browser/net/cookie_policy_browsertest.cc
+++ b/chrome/browser/net/cookie_policy_browsertest.cc
@@ -666,7 +666,8 @@
 }
 
 IN_PROC_BROWSER_TEST_P(CookiePolicyStorageBrowserTest,
-                       NestedThirdPartyIFrameStorage) {
+                       // TODO(crbug.com/390648566): Re-enable this test
+                       DISABLED_NestedThirdPartyIFrameStorage) {
   NavigateToPageWithFrame(kHostA);
   NavigateFrameTo(kHostB, "/iframe.html");
   NavigateNestedFrameTo(kHostC, "/browsing_data/site_data.html");
diff --git a/chrome/browser/notifications/platform_notification_service_impl.cc b/chrome/browser/notifications/platform_notification_service_impl.cc
index 8f66c350..467093a 100644
--- a/chrome/browser/notifications/platform_notification_service_impl.cc
+++ b/chrome/browser/notifications/platform_notification_service_impl.cc
@@ -679,15 +679,9 @@
 #if !BUILDFLAG(IS_ANDROID)
   web_app::WebAppProvider* web_app_provider =
       web_app::WebAppProvider::GetForLocalAppsUnchecked(profile_);
-  // TODO(crbug.com/379827962): Evaluate call sites of FindBestAppWithUrlInScope
-  // for correctness.
   if (web_app_provider) {
     return web_app_provider->registrar_unsafe().FindBestAppWithUrlInScope(
-        web_app_hint_url,
-        {
-            web_app::proto::InstallState::INSTALLED_WITH_OS_INTEGRATION,
-            web_app::proto::InstallState::INSTALLED_WITHOUT_OS_INTEGRATION,
-        });
+        web_app_hint_url, web_app::WebAppFilter::InstalledInChrome());
   }
 #endif
 
@@ -701,15 +695,19 @@
   web_app::WebAppProvider* web_app_provider =
       web_app::WebAppProvider::GetForLocalAppsUnchecked(profile_);
   if (web_app_provider) {
-    // TODO(crbug.com/379827962): Evaluate call sites of
-    // FindBestAppWithUrlInScope for correctness.
+#if BUILDFLAG(IS_CHROMEOS)
+    // The PlatformNotificationServiceTest FindWebAppIconAndTitle seems to be
+    // verifying the availability of an icon and a title for notification
+    // purposes, even though the app is not installed with OS integration, which
+    // is surprising.
+    web_app::WebAppFilter filter = web_app::WebAppFilter::InstalledInChrome();
+#else
+    web_app::WebAppFilter filter =
+        web_app::WebAppFilter::SupportsOsNotifications();
+#endif
     const std::optional<webapps::AppId> app_id =
         web_app_provider->registrar_unsafe().FindBestAppWithUrlInScope(
-            web_app_hint_url,
-            {
-                web_app::proto::InstallState::INSTALLED_WITH_OS_INTEGRATION,
-                web_app::proto::InstallState::INSTALLED_WITHOUT_OS_INTEGRATION,
-            });
+            web_app_hint_url, filter);
     if (app_id) {
       std::optional<WebAppIconAndTitle> icon_and_title;
       icon_and_title.emplace();
@@ -740,8 +738,7 @@
   }
   const std::optional<webapps::AppId> app_id =
       web_app_provider->registrar_unsafe().FindBestAppWithUrlInScope(
-          web_app_url,
-          {web_app::proto::InstallState::INSTALLED_WITH_OS_INTEGRATION});
+          web_app_url, web_app::WebAppFilter::SupportsOsNotifications());
   return app_id.has_value();
 #endif
 }
diff --git a/chrome/browser/platform_util_linux.cc b/chrome/browser/platform_util_linux.cc
index df221178..5216ab56 100644
--- a/chrome/browser/platform_util_linux.cc
+++ b/chrome/browser/platform_util_linux.cc
@@ -31,7 +31,6 @@
 #include "base/task/thread_pool.h"
 #include "base/threading/scoped_blocking_call.h"
 #include "base/types/expected.h"
-#include "chrome/browser/lifetime/termination_notification.h"
 #include "chrome/browser/platform_util_internal.h"
 #include "components/dbus/properties/types.h"
 #include "components/dbus/thread_linux/dbus_thread_linux.h"
@@ -66,13 +65,7 @@
     return *instance;
   }
 
-  ShowItemHelper()
-      : browser_shutdown_subscription_(
-            browser_shutdown::AddAppTerminatingCallback(
-                base::BindOnce(&ShowItemHelper::OnAppTerminating,
-                               // Unretained is safe, the ShowItemHelper
-                               // instance is never destroyed.
-                               base::Unretained(this)))) {}
+  ShowItemHelper() = default;
 
   ShowItemHelper(const ShowItemHelper&) = delete;
   ShowItemHelper& operator=(const ShowItemHelper&) = delete;
@@ -84,12 +77,7 @@
       return;
     }
     if (!bus_) {
-      // Sets up the D-Bus connection.
-      dbus::Bus::Options bus_options;
-      bus_options.bus_type = dbus::Bus::SESSION;
-      bus_options.connection_type = dbus::Bus::PRIVATE;
-      bus_options.dbus_task_runner = dbus_thread_linux::GetTaskRunner();
-      bus_ = base::MakeRefCounted<dbus::Bus>(bus_options);
+      bus_ = dbus_thread_linux::GetSharedSessionBus();
     }
 
     if (api_type_.has_value()) {
@@ -143,16 +131,6 @@
     }
   }
 
-  void OnAppTerminating() {
-    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-    // The browser process is about to exit. Clean up while we still can.
-    portal_object_proxy_ = nullptr;
-    file_manager_object_proxy_ = nullptr;
-    if (bus_)
-      bus_->ShutdownOnDBusThreadAndBlock();
-    bus_.reset();
-  }
-
   void CheckPortalRunningResponse(std::optional<bool> is_running) {
     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     if (is_running.value_or(false)) {
@@ -315,8 +293,6 @@
 
   // Requests that are queued until the API availability is determined.
   std::queue<base::FilePath> pending_requests_;
-
-  base::CallbackListSubscription browser_shutdown_subscription_;
 };
 
 void OnLaunchOptionsCreated(const std::string& command,
@@ -339,12 +315,14 @@
   // applications. See http://crbug.com/24120
   char* disable_gnome_bug_buddy = getenv("GNOME_DISABLE_CRASH_DIALOG");
   if (disable_gnome_bug_buddy &&
-      disable_gnome_bug_buddy == std::string("SET_BY_GOOGLE_CHROME"))
+      disable_gnome_bug_buddy == std::string("SET_BY_GOOGLE_CHROME")) {
     options.environment["GNOME_DISABLE_CRASH_DIALOG"] = std::string();
+  }
 
   base::Process process = base::LaunchProcess(argv, options);
-  if (process.IsValid())
+  if (process.IsValid()) {
     base::EnsureProcessGetsReaped(std::move(process));
+  }
 }
 
 void RunCommand(const std::string& command,
@@ -396,10 +374,11 @@
 
 void OpenExternal(const GURL& url) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  if (url.SchemeIs("mailto"))
+  if (url.SchemeIs("mailto")) {
     XDGEmail(url.spec());
-  else
+  } else {
     XDGOpen(base::FilePath(), url.spec());
+  }
 }
 
 }  // namespace platform_util
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 203905ed..77a87976 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -2351,12 +2351,6 @@
   { key::kWebAudioOutputBufferingEnabled,
     prefs::kWebAudioOutputBufferingEnabled,
     base::Value::Type::BOOLEAN },
-
-#if BUILDFLAG(ENABLE_GLIC)
-  { key::kGlicEnabled,
-    glic::prefs::kGlicEnabledByPolicy,
-    base::Value::Type::BOOLEAN },
-#endif  // BUILDFLAG(ENABLE_GLIC)
 };
 // clang-format on
 
@@ -3304,6 +3298,13 @@
       std::make_unique<PowerBatteryChargingOptimizationPolicyHandler>()));
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
+#if BUILDFLAG(ENABLE_GLIC)
+  handlers->AddHandler(std::make_unique<IntRangePolicyHandler>(
+      key::kGlicSettings, glic::prefs::kGlicEnabledByPolicy,
+      static_cast<int>(glic::prefs::EnabledByPolicyState::kMinValue),
+      static_cast<int>(glic::prefs::EnabledByPolicyState::kMaxValue), false));
+#endif
+
   return handlers;
 }
 
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogConsentEEA.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogConsentEEA.java
index be2005f..4b3356f1 100644
--- a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogConsentEEA.java
+++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogConsentEEA.java
@@ -366,7 +366,7 @@
         if (mThinWebView != null) {
             mWebContents.destroy();
             mWebContents = null;
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
             mWebContentsObserver = null;
             mThinWebView.destroy();
             mThinWebView = null;
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogNoticeEeaV2.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogNoticeEeaV2.java
index ec94e596..9662504 100644
--- a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogNoticeEeaV2.java
+++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogNoticeEeaV2.java
@@ -302,7 +302,7 @@
         if (mThinWebView != null) {
             mWebContents.destroy();
             mWebContents = null;
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
             mWebContentsObserver = null;
             mThinWebView.destroy();
             mThinWebView = null;
diff --git a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogNoticeROW.java b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogNoticeROW.java
index 2947258a..f639511 100644
--- a/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogNoticeROW.java
+++ b/chrome/browser/privacy_sandbox/android/java/src/org/chromium/chrome/browser/privacy_sandbox/PrivacySandboxDialogNoticeROW.java
@@ -287,7 +287,7 @@
         if (mThinWebView != null) {
             mWebContents.destroy();
             mWebContents = null;
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
             mWebContentsObserver = null;
             mThinWebView.destroy();
             mThinWebView = null;
diff --git a/chrome/browser/resources/ash/settings/nearby_share_page/nearby_share_subpage.ts b/chrome/browser/resources/ash/settings/nearby_share_page/nearby_share_subpage.ts
index 63498e46..c64ea59 100644
--- a/chrome/browser/resources/ash/settings/nearby_share_page/nearby_share_subpage.ts
+++ b/chrome/browser/resources/ash/settings/nearby_share_page/nearby_share_subpage.ts
@@ -307,6 +307,22 @@
     if (visibility === undefined) {
       return '';
     }
+
+    if (this.isQuickShareV2Enabled_) {
+      switch (visibility) {
+        case Visibility.kAllContacts:
+          return this.i18n('nearbyShareContactVisiblityContactsButton');
+        case Visibility.kNoOne:
+          return this.i18n('nearbyShareContactVisibilityNone');
+        case Visibility.kUnknown:
+          return this.i18n('nearbyShareContactVisibilityUnknown');
+        case Visibility.kYourDevices:
+          return this.i18n('nearbyShareContactVisibilityYourDevices');
+        default:
+          assertNotReached();
+      }
+    }
+
     switch (visibility) {
       case Visibility.kAllContacts:
         return this.i18n('nearbyShareContactVisibilityAll');
diff --git a/chrome/browser/resources/chromeos/nearby_share/shared/nearby_contact_visibility.ts b/chrome/browser/resources/chromeos/nearby_share/shared/nearby_contact_visibility.ts
index 82712f6..d2a94e5 100644
--- a/chrome/browser/resources/chromeos/nearby_share/shared/nearby_contact_visibility.ts
+++ b/chrome/browser/resources/chromeos/nearby_share/shared/nearby_contact_visibility.ts
@@ -146,6 +146,14 @@
         type: String,
         value: '',
       },
+
+      /**
+       * Determines whether the QuickShareV2 flag is enabled.
+       */
+      isQuickShareV2Enabled_: {
+        type: Boolean,
+        value: () => loadTimeData.getBoolean('isQuickShareV2Enabled'),
+      },
     };
   }
 
@@ -172,6 +180,7 @@
   private downloadTimeoutId_: number|null;
   private isDarkModeActive_: boolean;
   private isAllContactsToggledOn_: boolean;
+  private isQuickShareV2Enabled_: boolean;
   private numUnreachable_: number;
   private numUnreachableMessage_: string;
 
@@ -226,6 +235,10 @@
       |null {
     switch (visibilityString) {
       case 'contacts':
+        if (this.isQuickShareV2Enabled_) {
+          return Visibility.kAllContacts
+        }
+
         if (this.isAllContactsToggledOn_) {
           return Visibility.kAllContacts;
         }
@@ -339,6 +352,10 @@
    * @return true when checkboxes should be shown for contacts.
    */
   private showContactCheckBoxes_(): boolean {
+    if (this.isQuickShareV2Enabled_) {
+      return false;
+    }
+
     return this.getSelectedVisibility() === Visibility.kSelectedContacts;
   }
 
@@ -417,6 +434,10 @@
 
   private showAllContactsToggle_(
       selectedVisibility: string, contactsState: ContactsState): boolean {
+    if (this.isQuickShareV2Enabled_) {
+      return false;
+    }
+
     return selectedVisibility === 'contacts' &&
         contactsState === ContactsState.HAS_CONTACTS;
   }
diff --git a/chrome/browser/resources/commerce/product_specifications/app.ts b/chrome/browser/resources/commerce/product_specifications/app.ts
index 707e9e3..8562891a 100644
--- a/chrome/browser/resources/commerce/product_specifications/app.ts
+++ b/chrome/browser/resources/commerce/product_specifications/app.ts
@@ -887,6 +887,7 @@
     }
 
     if (urlSetChanged) {
+      this.closeAllProductSelectionMenus_();
       this.populateTable_(set.urls.map(url => url.url));
     } else if (orderChanged) {
       const newCols: TableColumn[] = [];
@@ -1051,6 +1052,11 @@
   private onHeaderMenuDeleteClick_() {
     this.deleteSet_();
   }
+
+  private closeAllProductSelectionMenus_() {
+    this.$.summaryTable.closeAllProductSelectionMenus();
+    this.$.newColumnSelector.closeMenu();
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/commerce/product_specifications/comparison_table_list.html.ts b/chrome/browser/resources/commerce/product_specifications/comparison_table_list.html.ts
index 140f70d..5028ea65 100644
--- a/chrome/browser/resources/commerce/product_specifications/comparison_table_list.html.ts
+++ b/chrome/browser/resources/commerce/product_specifications/comparison_table_list.html.ts
@@ -58,7 +58,11 @@
         <cr-action-menu>
           <button id="menuOpenAll" class="dropdown-item"
               @click="${this.onOpenAllClick_}">
-            ${this.getOpenAllString_(this.numSelected_)}
+          ${this.getOpenAllString_(this.numSelected_)}
+          </button>
+          <button id="menuOpenAllInNewWindow" class="dropdown-item"
+              @click="${this.onOpenAllInNewWindowClick_}">
+            ${this.getOpenAllInNewWindowString_(this.numSelected_)}
           </button>
           <hr>
           <button id="menuDelete" class="dropdown-item"
diff --git a/chrome/browser/resources/commerce/product_specifications/comparison_table_list.ts b/chrome/browser/resources/commerce/product_specifications/comparison_table_list.ts
index ba242156..4e4a1ad0 100644
--- a/chrome/browser/resources/commerce/product_specifications/comparison_table_list.ts
+++ b/chrome/browser/resources/commerce/product_specifications/comparison_table_list.ts
@@ -9,6 +9,8 @@
 import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render_lit.js';
 import './images/icons.html.js';
 
+import {ShowSetDisposition} from '//resources/cr_components/commerce/product_specifications.mojom-webui.js';
+import type {ProductSpecificationsBrowserProxy} from '//resources/cr_components/commerce/product_specifications_browser_proxy.js';
 import {ProductSpecificationsBrowserProxyImpl} from '//resources/cr_components/commerce/product_specifications_browser_proxy.js';
 import type {ShoppingServiceBrowserProxy} from '//resources/cr_components/commerce/shopping_service_browser_proxy.js';
 import {ShoppingServiceBrowserProxyImpl} from '//resources/cr_components/commerce/shopping_service_browser_proxy.js';
@@ -72,6 +74,8 @@
   private selectedUuids_: Set<Uuid> = new Set();
   private shoppingApi_: ShoppingServiceBrowserProxy =
       ShoppingServiceBrowserProxyImpl.getInstance();
+  private productSpecificationsProxy_: ProductSpecificationsBrowserProxy =
+      ProductSpecificationsBrowserProxyImpl.getInstance();
 
   protected getSelectionLabel_(numSelected: number): string {
     return loadTimeData.getStringF('numSelected', numSelected);
@@ -81,6 +85,10 @@
     return loadTimeData.getStringF('menuOpenAll', numSelected);
   }
 
+  protected getOpenAllInNewWindowString_(numSelected: number): string {
+    return loadTimeData.getStringF('menuOpenAllInNewWindow', numSelected);
+  }
+
   protected onEditClick_() {
     if (!this.isEditing_) {
       this.isEditing_ = true;
@@ -110,17 +118,21 @@
   }
 
   protected async onOpenAllClick_() {
-    this.selectedUuids_.forEach(uuid => {
-      ProductSpecificationsBrowserProxyImpl.getInstance()
-          .showProductSpecificationsSetForUuid(uuid, true);
-    });
-
+    this.productSpecificationsProxy_.showProductSpecificationsSetsForUuids(
+        Array.from(this.selectedUuids_), ShowSetDisposition.kInNewTabs);
     this.$.menu.get().close();
-    this.stopEditing_();
 
     this.fire('open-all-finished-for-testing');
   }
 
+  protected async onOpenAllInNewWindowClick_() {
+    this.productSpecificationsProxy_.showProductSpecificationsSetsForUuids(
+        Array.from(this.selectedUuids_), ShowSetDisposition.kInNewWindow);
+    this.$.menu.get().close();
+
+    this.fire('open-all-in-new-window-finished-for-testing');
+  }
+
   protected onCheckboxChange_(event:
                                   ComparisonTableListItemCheckboxChangeEvent) {
     if (event.detail.checked) {
diff --git a/chrome/browser/resources/commerce/product_specifications/comparison_table_list_item.html.ts b/chrome/browser/resources/commerce/product_specifications/comparison_table_list_item.html.ts
index 49c0ec9..3a289ed 100644
--- a/chrome/browser/resources/commerce/product_specifications/comparison_table_list_item.html.ts
+++ b/chrome/browser/resources/commerce/product_specifications/comparison_table_list_item.html.ts
@@ -51,6 +51,10 @@
               @click="${this.onOpenInNewTabClick_}">
             $i18n{menuOpenInNewTab}
           </button>
+          <button id="openInNewWindow" class="dropdown-item" role="menuitem"
+              @click="${this.onOpenInNewWindowClick_}">
+            $i18n{menuOpenInNewWindow}
+          </button>
           <hr>
           <button id="rename" class="dropdown-item" role="menuitem"
               @click="${this.onRenameClick_}">
diff --git a/chrome/browser/resources/commerce/product_specifications/comparison_table_list_item.ts b/chrome/browser/resources/commerce/product_specifications/comparison_table_list_item.ts
index 382d5c3a..7405c9d 100644
--- a/chrome/browser/resources/commerce/product_specifications/comparison_table_list_item.ts
+++ b/chrome/browser/resources/commerce/product_specifications/comparison_table_list_item.ts
@@ -11,6 +11,7 @@
 import 'chrome://resources/cr_elements/cr_input/cr_input.js';
 import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js';
 
+import {ShowSetDisposition} from '//resources/cr_components/commerce/product_specifications.mojom-webui.js';
 import type {CrActionMenuElement} from '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
 import type {CrIconButtonElement} from '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import type {CrLazyRenderLitElement} from '//resources/cr_elements/cr_lazy_render/cr_lazy_render_lit.js';
@@ -167,6 +168,12 @@
         this.uuid, true);
   }
 
+  protected onOpenInNewWindowClick_() {
+    this.$.menu.get().close();
+    this.productSpecificationsProxy_.showProductSpecificationsSetsForUuids(
+        [this.uuid], ShowSetDisposition.kInNewWindow);
+  }
+
   protected async onRenameClick_() {
     this.$.menu.get().close();
     this.isRenaming_ = true;
diff --git a/chrome/browser/resources/commerce/product_specifications/new_column_selector.ts b/chrome/browser/resources/commerce/product_specifications/new_column_selector.ts
index 2c671bc7..0439728 100644
--- a/chrome/browser/resources/commerce/product_specifications/new_column_selector.ts
+++ b/chrome/browser/resources/commerce/product_specifications/new_column_selector.ts
@@ -47,6 +47,10 @@
     return getHtml.bind(this)();
   }
 
+  closeMenu() {
+    this.$.productSelectionMenu.close();
+  }
+
   protected showMenu_() {
     this.$.productSelectionMenu.showAt(this.$.button);
     this.$.button.classList.add('showing-menu');
diff --git a/chrome/browser/resources/commerce/product_specifications/product_selection_menu.ts b/chrome/browser/resources/commerce/product_specifications/product_selection_menu.ts
index 6fe46b4b..1299f25 100644
--- a/chrome/browser/resources/commerce/product_specifications/product_selection_menu.ts
+++ b/chrome/browser/resources/commerce/product_specifications/product_selection_menu.ts
@@ -135,7 +135,10 @@
   }
 
   close() {
-    this.$.menu.get().close();
+    const menu = this.$.menu.getIfExists();
+    if (menu) {
+      menu.close();
+    }
   }
 
   protected expandedChanged_(
diff --git a/chrome/browser/resources/commerce/product_specifications/product_selector.ts b/chrome/browser/resources/commerce/product_specifications/product_selector.ts
index bea6083..1687dd7 100644
--- a/chrome/browser/resources/commerce/product_specifications/product_selector.ts
+++ b/chrome/browser/resources/commerce/product_specifications/product_selector.ts
@@ -52,6 +52,10 @@
     return getHtml.bind(this)();
   }
 
+  closeMenu() {
+    this.$.productSelectionMenu.close();
+  }
+
   protected showMenu_() {
     this.$.productSelectionMenu.showAt(this.$.currentProductContainer);
     this.$.currentProductContainer.classList.add('showing-menu');
diff --git a/chrome/browser/resources/commerce/product_specifications/table.ts b/chrome/browser/resources/commerce/product_specifications/table.ts
index 3d76471..2d4cbb1 100644
--- a/chrome/browser/resources/commerce/product_specifications/table.ts
+++ b/chrome/browser/resources/commerce/product_specifications/table.ts
@@ -26,6 +26,7 @@
 import type {ProductDescription} from './description_section.js';
 import {DragAndDropManager} from './drag_and_drop_manager.js';
 import type {SectionType} from './product_selection_menu.js';
+import type {ProductSelectorElement} from './product_selector.js';
 import {getTemplate} from './table.html.js';
 import {WindowProxy} from './window_proxy.js';
 
@@ -106,6 +107,13 @@
     this.dispatchEvent(new Event('url-order-update'));
   }
 
+  closeAllProductSelectionMenus() {
+    const productSelectors =
+        this.shadowRoot!.querySelectorAll<ProductSelectorElement>(
+            'product-selector');
+    productSelectors.forEach(productSelector => productSelector.closeMenu());
+  }
+
   private onColumnsChanged_() {
     this.style.setProperty('--num-columns', String(this.columns.length));
   }
diff --git a/chrome/browser/resources/pdf/elements/viewer_toolbar.html b/chrome/browser/resources/pdf/elements/viewer_toolbar.html
index e7ece01a..b7fca01 100644
--- a/chrome/browser/resources/pdf/elements/viewer_toolbar.html
+++ b/chrome/browser/resources/pdf/elements/viewer_toolbar.html
@@ -4,7 +4,7 @@
         title="$i18n{menu}" aria-label="$i18n{menu}"
         aria-expanded="${this.getAriaExpanded_()}"
 <if expr="enable_ink">
-        ?disabled="${this.annotationMode}"
+        ?disabled="${this.isInInk1AnnotationMode_()}"
 </if>
         @click="${this.onSidenavToggleClick_}">
     </cr-icon-button>
@@ -44,7 +44,7 @@
     <cr-icon-button id="rotate" .ironIcon="${this.iconsetName_()}:rotate-left"
         suppress-rtl-flip
 <if expr="enable_ink">
-        ?disabled="${this.annotationMode}"
+        ?disabled="${this.isInInk1AnnotationMode_()}"
 </if>
         aria-label="$i18n{tooltipRotateCCW}" title="$i18n{tooltipRotateCCW}"
         @click="${this.onRotateClick_}">
@@ -106,7 +106,7 @@
   <button id="two-page-view-button" class="dropdown-item"
       @click="${this.toggleTwoPageViewClick_}" role="checkbox"
 <if expr="enable_ink">
-      ?disabled="${this.annotationMode}"
+      ?disabled="${this.isInInk1AnnotationMode_()}"
 </if>
       aria-checked="${this.getAriaChecked_(this.twoUpViewEnabled)}">
     <span class="check-container">
diff --git a/chrome/browser/resources/pdf/elements/viewer_toolbar.ts b/chrome/browser/resources/pdf/elements/viewer_toolbar.ts
index 6352275..bd70c85 100644
--- a/chrome/browser/resources/pdf/elements/viewer_toolbar.ts
+++ b/chrome/browser/resources/pdf/elements/viewer_toolbar.ts
@@ -44,7 +44,7 @@
 
 export interface ViewerToolbarElement {
   $: {
-    sidenavToggle: HTMLElement,
+    sidenavToggle: HTMLButtonElement,
     menu: CrActionMenuElement,
     'present-button': HTMLButtonElement,
     'two-page-view-button': HTMLButtonElement,
@@ -268,7 +268,7 @@
         this.isInInk1AnnotationMode_();
   }
 
-  private isInInk1AnnotationMode_(): boolean {
+  protected isInInk1AnnotationMode_(): boolean {
     // <if expr="enable_pdf_ink2">
     if (this.pdfInk2Enabled) {
       return false;
diff --git a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_group.html.ts b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_group.html.ts
index 5bcd8c4..e2948c92 100644
--- a/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_group.html.ts
+++ b/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_group.html.ts
@@ -62,9 +62,6 @@
       </tab-search-item>
     `)}
   </cr-page-selector>
-  <auto-tab-groups-results-actions
-      @create-group-click="${this.onCreateGroupClick_}">
-  </auto-tab-groups-results-actions>
 </div>
 <!--_html_template_end_-->`;
   // clang-format on
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
index 169bade..fdd95b9 100644
--- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
+++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
@@ -6,6 +6,7 @@
 #define CHROME_BROWSER_SAFE_BROWSING_CLOUD_CONTENT_SCANNING_BINARY_UPLOAD_SERVICE_H_
 
 #include "base/functional/bind.h"
+#include "base/functional/callback.h"
 #include "base/memory/read_only_shared_memory_region.h"
 #include "base/memory/weak_ptr.h"
 #include "base/types/id_type.h"
@@ -13,7 +14,6 @@
 #include "components/enterprise/common/proto/connectors.pb.h"
 #include "components/enterprise/connectors/core/analysis_settings.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "services/network/public/cpp/resource_request.h"
 #include "url/gurl.h"
 
 class Profile;
diff --git a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
index 1d9e200..a6bfbe66 100644
--- a/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
+++ b/chrome/browser/safe_browsing/download_protection/check_client_download_request_base.cc
@@ -34,6 +34,7 @@
 #include "net/base/load_flags.h"
 #include "net/http/http_request_headers.h"
 #include "net/http/http_status_code.h"
+#include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
diff --git a/chrome/browser/safe_browsing/download_protection/ppapi_download_request.cc b/chrome/browser/safe_browsing/download_protection/ppapi_download_request.cc
index 389121a..0aeea0a 100644
--- a/chrome/browser/safe_browsing/download_protection/ppapi_download_request.cc
+++ b/chrome/browser/safe_browsing/download_protection/ppapi_download_request.cc
@@ -28,6 +28,7 @@
 #include "net/base/load_flags.h"
 #include "net/http/http_cache.h"
 #include "net/http/http_status_code.h"
+#include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "services/network/public/mojom/url_response_head.mojom.h"
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/android_share_sheet/AndroidCustomActionProvider.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/android_share_sheet/AndroidCustomActionProvider.java
index 08da0137..a819f00 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/android_share_sheet/AndroidCustomActionProvider.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/android_share_sheet/AndroidCustomActionProvider.java
@@ -277,7 +277,7 @@
         }
         // TODO(386833405): Update the text based on UX reviews and icon resolution.
         return new FirstPartyOptionBuilder(ContentType.LINK_PAGE_VISIBLE)
-                .setIcon(R.drawable.ic_person_add_40dp, R.string.collaboration_share_group_title)
+                .setIcon(R.drawable.ic_person_add_40dp, R.string.sharing_tab_group)
                 .setShareActionType(ShareCustomAction.SHARE_AS_TAB_GROUP)
                 .setFeatureNameForMetrics(USER_ACTION_SHARE_AS_TAB_GROUP)
                 .setOnClickCallback(
diff --git a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/android_share_sheet/AndroidShareSheetController.java b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/android_share_sheet/AndroidShareSheetController.java
index b8338e8..a179f92e 100644
--- a/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/android_share_sheet/AndroidShareSheetController.java
+++ b/chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/android_share_sheet/AndroidShareSheetController.java
@@ -269,6 +269,7 @@
                 size,
                 (Bitmap icon, GURL iconUrl) -> {
                     onFaviconRetrieved(context, icon, size, onUriReady);
+                    faviconHelper.destroy();
                 });
     }
 
diff --git a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/android_share_sheet/AndroidShareSheetControllerUnitTest.java b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/android_share_sheet/AndroidShareSheetControllerUnitTest.java
index 6e15545f..332adca4 100644
--- a/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/android_share_sheet/AndroidShareSheetControllerUnitTest.java
+++ b/chrome/browser/share/android/javatests/src/org/chromium/chrome/browser/share/android_share_sheet/AndroidShareSheetControllerUnitTest.java
@@ -244,7 +244,7 @@
         } else {
             assertCustomActions(
                     intent,
-                    R.string.collaboration_share_group_title,
+                    R.string.sharing_tab_group,
                     R.string.sharing_long_screenshot,
                     R.string.print_share_activity_title,
                     R.string.sharing_send_tab_to_self,
@@ -858,7 +858,7 @@
         } else {
             assertCustomActions(
                     intent,
-                    R.string.collaboration_share_group_title,
+                    R.string.sharing_tab_group,
                     R.string.sharing_long_screenshot,
                     R.string.print_share_activity_title,
                     R.string.sharing_send_tab_to_self,
diff --git a/chrome/browser/status_icons/status_tray.cc b/chrome/browser/status_icons/status_tray.cc
index 2756d74..ffa2a09 100644
--- a/chrome/browser/status_icons/status_tray.cc
+++ b/chrome/browser/status_icons/status_tray.cc
@@ -23,11 +23,12 @@
   return status_icons_.back().icon.get();
 }
 
-void StatusTray::RemoveStatusIcon(StatusIcon* icon) {
+std::unique_ptr<StatusIcon> StatusTray::RemoveStatusIcon(StatusIcon* icon) {
   for (auto iter = status_icons_.begin(); iter != status_icons_.end(); ++iter) {
     if (iter->icon.get() == icon) {
+      auto removed_icon = std::move(iter->icon);
       status_icons_.erase(iter);
-      return;
+      return removed_icon;
     }
   }
   NOTREACHED();
diff --git a/chrome/browser/status_icons/status_tray.h b/chrome/browser/status_icons/status_tray.h
index 0cccc59..4f0af0c 100644
--- a/chrome/browser/status_icons/status_tray.h
+++ b/chrome/browser/status_icons/status_tray.h
@@ -44,8 +44,9 @@
                                const gfx::ImageSkia& image,
                                const std::u16string& tool_tip);
 
-  // Removes |icon| from this status tray.
-  void RemoveStatusIcon(StatusIcon* icon);
+  // Removes |icon| from this status tray. Returns the `std::unique_ptr` to the
+  // icon so it can be cleaned up safely.
+  std::unique_ptr<StatusIcon> RemoveStatusIcon(StatusIcon* icon);
 
   // Checks if a status icon of a specific type exists in the status tray.
   bool HasStatusIconOfTypeForTesting(StatusIconType type) const;
diff --git a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributes.java b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributes.java
index 6528c23..4cca70c 100644
--- a/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributes.java
+++ b/chrome/browser/tab/java/src/org/chromium/chrome/browser/tab/TabStateAttributes.java
@@ -236,7 +236,7 @@
     @Override
     public void cleanupWebContents(WebContents webContents) {
         if (mWebContentsObserver != null) {
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
             mWebContentsObserver = null;
         }
     }
diff --git a/chrome/browser/touch_to_fill/password_manager/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetRenderTest.java b/chrome/browser/touch_to_fill/password_manager/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetRenderTest.java
index 3f820d9..e2da7766a 100644
--- a/chrome/browser/touch_to_fill/password_manager/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetRenderTest.java
+++ b/chrome/browser/touch_to_fill/password_manager/no_passkeys/internal/android/java/src/org/chromium/chrome/browser/touch_to_fill/no_passkeys/NoPasskeysBottomSheetRenderTest.java
@@ -8,7 +8,6 @@
 import static org.chromium.base.test.util.ApplicationTestUtils.finishActivity;
 import static org.chromium.ui.base.LocalizationUtils.setRtlForTesting;
 
-import android.graphics.Color;
 import android.view.ViewGroup;
 
 import androidx.test.filters.MediumTest;
@@ -137,10 +136,7 @@
         ViewGroup activityContentView = getActivity().findViewById(android.R.id.content);
         ScrimCoordinator scrimCoordinator =
                 new ScrimCoordinator(
-                        getActivity(),
-                        /* systemUiScrimDelegate= */ null,
-                        activityContentView,
-                        Color.WHITE);
+                        getActivity(), /* systemUiScrimDelegate= */ null, activityContentView);
         return BottomSheetControllerFactory.createFullWidthBottomSheetController(
                 () -> scrimCoordinator,
                 (unused) -> {},
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 4b71499..b6e4ee5 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1676,6 +1676,7 @@
       "//chrome/browser/apps/link_capturing",
       "//chrome/browser/apps/link_capturing:features",
       "//chrome/browser/browsing_data:constants",
+      "//chrome/browser/contextual_cueing",
 
       # TODO(crbug.com/370804668i): Remove the dependency when chrome/browser/serial
       # gets modularized.
@@ -3089,7 +3090,6 @@
 
     deps += [
       "//chrome/app:generated_resources",
-      "//chrome/browser/contextual_cueing:contextual_cueing",
       "//chrome/browser/on_device_translation:language_pack_util",
       "//chrome/browser/shortcuts",
       "//components/capture_mode",
diff --git a/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java b/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java
index 4e4d0a30..d4c04b2 100644
--- a/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java
+++ b/chrome/browser/ui/android/edge_to_edge/internal/java/src/org/chromium/chrome/browser/ui/edge_to_edge/EdgeToEdgeControllerImpl.java
@@ -334,7 +334,7 @@
      * @param tab The {@link Tab} whose {@link WebContents} we want to observe.
      */
     private void updateWebContentsObserver(Tab tab) {
-        if (mWebContentsObserver != null) mWebContentsObserver.destroy();
+        if (mWebContentsObserver != null) mWebContentsObserver.observe(null);
         mWebContentsObserver =
                 new WebContentsObserver(tab.getWebContents()) {
                     @Override
@@ -564,7 +564,7 @@
     @Override
     public void destroy() {
         if (mWebContentsObserver != null) {
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
             mWebContentsObserver = null;
         }
         if (mCurrentTab != null) mCurrentTab.removeObserver(mTabObserver);
diff --git a/chrome/browser/ui/android/ephemeraltab/java/src/org/chromium/chrome/browser/ephemeraltab/EphemeralTabMediator.java b/chrome/browser/ui/android/ephemeraltab/java/src/org/chromium/chrome/browser/ephemeraltab/EphemeralTabMediator.java
index 094bd89..f87622c 100644
--- a/chrome/browser/ui/android/ephemeraltab/java/src/org/chromium/chrome/browser/ephemeraltab/EphemeralTabMediator.java
+++ b/chrome/browser/ui/android/ephemeraltab/java/src/org/chromium/chrome/browser/ephemeraltab/EphemeralTabMediator.java
@@ -271,7 +271,7 @@
     /** Destroys the objects used for the current preview tab. */
     void destroyContent() {
         if (mWebContentsObserver != null) {
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
             mWebContentsObserver = null;
         }
         mWebContentsDelegate = null;
diff --git a/chrome/browser/ui/android/omnibox/java/res/values/colors.xml b/chrome/browser/ui/android/omnibox/java/res/values/colors.xml
index ba64843..91d414e 100644
--- a/chrome/browser/ui/android/omnibox/java/res/values/colors.xml
+++ b/chrome/browser/ui/android/omnibox/java/res/values/colors.xml
@@ -26,7 +26,6 @@
     <color name="locationbar_status_separator_color_light">@color/white_alpha_60</color>
     <color name="locationbar_status_separator_color_incognito">@color/baseline_neutral_variant_60</color>
 
-    <color name="omnibox_focused_fading_background_color">@color/black_alpha_65</color>
     <color name="omnibox_focused_fading_background_color_light">@color/white_alpha_65</color>
 
     <color name="branded_url_text_on_dark_bg">@color/baseline_neutral_100</color>
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtils.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtils.java
index 89292af..8eba39d 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtils.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/SearchEngineUtils.java
@@ -111,6 +111,7 @@
     @Override
     public void destroy() {
         mTemplateUrlService.removeObserver(this);
+        mFaviconHelper.destroy();
     }
 
     @Override
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java
index 1ab86fa..5e5f294c 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/voice/VoiceRecognitionHandler.java
@@ -257,7 +257,7 @@
             if (navigation.hasCommitted() && !navigation.isErrorPage()) {
                 setReceivedUserGesture(navigation.getUrl());
             }
-            destroy();
+            observe(null);
         }
     }
 
@@ -335,7 +335,7 @@
                     locationBarDataProvider != null ? locationBarDataProvider.getTab() : null;
             if (currentTab != null) {
                 if (mVoiceSearchWebContentsObserver != null) {
-                    mVoiceSearchWebContentsObserver.destroy();
+                    mVoiceSearchWebContentsObserver.observe(null);
                     mVoiceSearchWebContentsObserver = null;
                 }
                 if (currentTab.getWebContents() != null) {
diff --git a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninAccountPickerCoordinator.java b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninAccountPickerCoordinator.java
index 2ca3e24..b8faf28 100644
--- a/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninAccountPickerCoordinator.java
+++ b/chrome/browser/ui/android/signin/java/src/org/chromium/chrome/browser/ui/signin/SigninAccountPickerCoordinator.java
@@ -123,13 +123,16 @@
         sheetContainer.setLayoutParams(
                 new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
         mContainerView.addView(sheetContainer);
-        @ColorInt int scrimColor = mActivity.getColor(R.color.default_scrim_color);
         mScrim =
                 new ScrimCoordinator(
                         mActivity,
                         new ScrimCoordinator.SystemUiScrimDelegate() {
                             @Override
                             public void setStatusBarScrimFraction(float scrimFraction) {
+                                // TODO(skym): Once the ability to get the current scrim color is
+                                // added, this should be updated to use that instead.
+                                @ColorInt int scrimColor = mScrim.getDefaultScrimColor();
+
                                 // Update the status bar color to match the currently shown scrim
                                 // color when the latter is changed.
                                 float alpha = ((float) Color.alpha(scrimColor)) * scrimFraction;
@@ -143,8 +146,7 @@
                                 mDelegate.setScrimColor(scrimColor);
                             }
                         },
-                        (ViewGroup) sheetContainer.getParent(),
-                        scrimColor);
+                        (ViewGroup) sheetContainer.getParent());
 
         mBottomSheetController =
                 BottomSheetControllerFactory.createBottomSheetController(
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index eb450413d..e748024 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -5896,6 +5896,10 @@
         Including link: <ph name="ORIGIN">%1$s<ex>https://www.example.com</ex></ph>
       </message>
 
+      <message name="IDS_SHARING_TAB_GROUP" desc="Label for the button in share bottom sheet for sharing a link to collaborate in a shared tab group.">
+        Tab group
+      </message>
+
       <message name="IDS_QR_CODE_FILENAME_PREFIX" desc="File name prefix for downloaded qrcode that is followed by timestamp.">
         chrome_qrcode_<ph name="CURRENT_TIMESTAMP_MS">%1$s<ex>1582667748515</ex></ph>
       </message>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SHARING_TAB_GROUP.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SHARING_TAB_GROUP.png.sha1
new file mode 100644
index 0000000..730475e
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_SHARING_TAB_GROUP.png.sha1
@@ -0,0 +1 @@
+e9ac74b00007eaff9103105e5fc8d6a9ca0592de
\ No newline at end of file
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
index 73d2fa7..d717bce5 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/ToolbarPositionController.java
@@ -402,7 +402,7 @@
     }
 
     /** Returns whether the toolbar will be shown on top for the supplied tab. */
-    static boolean shouldShowToolbarOnTop(Tab tab) {
+    public static boolean shouldShowToolbarOnTop(Tab tab) {
         boolean isNtpUrl =
                 (tab != null) && (tab.getUrl() != null) && UrlUtilities.isNtpUrl(tab.getUrl());
 
diff --git a/chrome/browser/ui/ash/capture_mode/BUILD.gn b/chrome/browser/ui/ash/capture_mode/BUILD.gn
index 6a6f10d..bdcc1700 100644
--- a/chrome/browser/ui/ash/capture_mode/BUILD.gn
+++ b/chrome/browser/ui/ash/capture_mode/BUILD.gn
@@ -55,6 +55,7 @@
     "//components/endpoint_fetcher",
     "//components/language/core/common:common",
     "//components/lens",
+    "//components/lens:enterprise_policy",
     "//content/public/browser",
     "//services/screen_ai/public/mojom",
     "//services/video_capture/public/mojom",
@@ -103,6 +104,7 @@
     "//chrome/browser/ui/ash/system_web_apps",
     "//chrome/common",
     "//chrome/test:test_support",
+    "//components/lens:enterprise_policy",
     "//content/test:test_support",
     "//media",
     "//media:test_support",
diff --git a/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.cc b/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.cc
index d8bf6fa08..3f4b7e48 100644
--- a/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.cc
+++ b/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.cc
@@ -57,6 +57,7 @@
 #include "chromeos/ash/services/recording/public/mojom/recording_service.mojom.h"
 #include "components/drive/file_errors.h"
 #include "components/lens/lens_overlay_mime_type.h"
+#include "components/lens/lens_overlay_permission_utils.h"
 #include "components/prefs/pref_service.h"
 #include "components/services/app_service/public/cpp/app_launch_util.h"
 #include "content/public/browser/audio_service.h"
@@ -143,6 +144,14 @@
   return g_instance;
 }
 
+bool ChromeCaptureModeDelegate::IsSearchAllowedByPolicy() const {
+  auto* profile = ProfileManager::GetActiveUserProfile();
+  return profile && profile->GetPrefs() &&
+         profile->GetPrefs()->GetInteger(lens::prefs::kLensOverlaySettings) ==
+             static_cast<int>(
+                 lens::prefs::LensOverlaySettingsPolicyValue::kEnabled);
+}
+
 void ChromeCaptureModeDelegate::SetIsScreenCaptureLocked(bool locked) {
   is_screen_capture_locked_ = locked;
   if (is_screen_capture_locked_) {
diff --git a/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.h b/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.h
index e7111b3..cbc370aa 100644
--- a/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.h
+++ b/chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.h
@@ -69,6 +69,7 @@
       const gfx::Rect& bounds,
       ash::OnCaptureModeDlpRestrictionChecked callback) override;
   bool IsCaptureAllowedByPolicy() const override;
+  bool IsSearchAllowedByPolicy() const override;
   void StartObservingRestrictedContent(
       const aura::Window* window,
       const gfx::Rect& bounds,
diff --git a/chrome/browser/ui/ash/capture_mode/sunfish_browsertest.cc b/chrome/browser/ui/ash/capture_mode/sunfish_browsertest.cc
index 5934dba..5028c38 100644
--- a/chrome/browser/ui/ash/capture_mode/sunfish_browsertest.cc
+++ b/chrome/browser/ui/ash/capture_mode/sunfish_browsertest.cc
@@ -7,12 +7,14 @@
 #include "ash/capture_mode/search_results_panel.h"
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_switches.h"
+#include "ash/public/cpp/capture_mode/capture_mode_api.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/ash/capture_mode/chrome_capture_mode_delegate.h"
 #include "chrome/browser/ui/ash/capture_mode/search_results_view.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/test/base/in_process_browser_test.h"
+#include "components/lens/lens_overlay_permission_utils.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "ui/views/view_utils.h"
@@ -151,4 +153,17 @@
                                  base::BindRepeating([](GURL url) {}));
 }
 
+// Tests that the policy can be read and set browser-side.
+IN_PROC_BROWSER_TEST_F(SunfishBrowserTest, BrowserPolicy) {
+  ChromeCaptureModeDelegate* delegate = ChromeCaptureModeDelegate::Get();
+  EXPECT_TRUE(delegate->IsSearchAllowedByPolicy());
+  EXPECT_TRUE(IsSunfishAllowedAndEnabled());
+
+  browser()->profile()->GetPrefs()->SetInteger(
+      lens::prefs::kLensOverlaySettings,
+      static_cast<int>(lens::prefs::LensOverlaySettingsPolicyValue::kDisabled));
+  EXPECT_FALSE(delegate->IsSearchAllowedByPolicy());
+  EXPECT_FALSE(IsSunfishAllowedAndEnabled());
+}
+
 }  // namespace ash
diff --git a/chrome/browser/ui/ash/editor_menu/editor_menu_card_context.cc b/chrome/browser/ui/ash/editor_menu/editor_menu_card_context.cc
index 8d5449e..f2c70e99 100644
--- a/chrome/browser/ui/ash/editor_menu/editor_menu_card_context.cc
+++ b/chrome/browser/ui/ash/editor_menu/editor_menu_card_context.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_card_context.h"
 
-#include "ash/constants/ash_features.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_strings.h"
 #include "chrome/browser/ui/ash/editor_menu/utils/text_and_image_mode.h"
 
@@ -18,38 +17,32 @@
 EditorMenuCardContext::~EditorMenuCardContext() = default;
 
 TextAndImageMode EditorMenuCardContext::text_and_image_mode() const {
-  // TODO: b:389553095 - With the introduction of EditorTextSelectionMode,
-  // merge kRewrite and kWrite into a single enum value.
-  switch (editor_mode_) {
-    case EditorMode::kRewrite:
-    case EditorMode::kWrite:
-    case EditorMode::kConsentNeeded:
-      if (editor_mode_ == EditorMode::kConsentNeeded &&
-          !ash::features::IsMagicBoostRevampEnabled()) {
-        return TextAndImageMode::kPromoCard;
-      }
-      if (lobster_mode_ == LobsterMode::kBlocked) {
-        return text_selection_mode_ ==
-                       EditorMenuCardTextSelectionMode::kHasSelection
-                   ? TextAndImageMode::kEditorRewriteOnly
-                   : TextAndImageMode::kEditorWriteOnly;
-      }
-      return text_selection_mode_ ==
-                     EditorMenuCardTextSelectionMode::kHasSelection
-                 ? TextAndImageMode::kEditorRewriteAndLobster
-                 : TextAndImageMode::kEditorWriteAndLobster;
-
-    case EditorMode::kSoftBlocked:
-    case EditorMode::kHardBlocked:
-      if (lobster_mode_ == LobsterMode::kBlocked) {
-        return TextAndImageMode::kBlocked;
-      }
-
-      return text_selection_mode_ ==
-                     EditorMenuCardTextSelectionMode::kHasSelection
-                 ? TextAndImageMode::kLobsterWithSelectedText
-                 : TextAndImageMode::kLobsterWithNoSelectedText;
+  if (lobster_mode_ == LobsterMode::kBlocked) {
+    if (editor_mode_ == EditorMode::kRewrite) {
+      return TextAndImageMode::kEditorRewriteOnly;
+    }
+    if (editor_mode_ == EditorMode::kWrite) {
+      return TextAndImageMode::kEditorWriteOnly;
+    }
+    if (editor_mode_ == EditorMode::kConsentNeeded) {
+      return TextAndImageMode::kPromoCard;
+    }
+    return TextAndImageMode::kBlocked;
   }
+
+  if (editor_mode_ == EditorMode::kRewrite) {
+    return TextAndImageMode::kEditorRewriteAndLobster;
+  }
+  if (editor_mode_ == EditorMode::kWrite) {
+    return TextAndImageMode::kEditorWriteAndLobster;
+  }
+  if (editor_mode_ == EditorMode::kConsentNeeded) {
+    return TextAndImageMode::kPromoCard;
+  }
+  if (lobster_mode_ == LobsterMode::kNoSelectedText) {
+    return TextAndImageMode::kLobsterWithNoSelectedText;
+  }
+  return TextAndImageMode::kLobsterWithSelectedText;
 }
 
 bool EditorMenuCardContext::consent_status_settled() const {
diff --git a/chrome/browser/ui/ash/editor_menu/editor_menu_card_context_unittest.cc b/chrome/browser/ui/ash/editor_menu/editor_menu_card_context_unittest.cc
index b26966f..9e0f71e0 100644
--- a/chrome/browser/ui/ash/editor_menu/editor_menu_card_context_unittest.cc
+++ b/chrome/browser/ui/ash/editor_menu/editor_menu_card_context_unittest.cc
@@ -4,8 +4,6 @@
 
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_card_context.h"
 
-#include "ash/constants/ash_features.h"
-#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_strings.h"
 #include "chrome/browser/ui/ash/editor_menu/utils/text_and_image_mode.h"
 #include "chromeos/ash/components/editor_menu/public/cpp/preset_text_query.h"
@@ -17,375 +15,172 @@
 
 namespace {
 
-struct TextAndImageModeTestCase {
-  bool magic_boost_revamp_enabled;
-  EditorMode editor_mode;
-  LobsterMode lobster_mode;
-  EditorMenuCardTextSelectionMode text_selection_mode;
-  TextAndImageMode expected_mode;
+using ::testing::ElementsAre;
+
+class EditorMenuCardContextTest : public testing::Test {
+ public:
+  EditorMenuCardContextTest() = default;
+
+  EditorMenuCardContextTest(const EditorMenuCardContextTest&) = delete;
+  EditorMenuCardContextTest& operator=(const EditorMenuCardContextTest&) =
+      delete;
+
+  ~EditorMenuCardContextTest() override = default;
 };
 
-class EditorMenuCardContextTextAndImageModeTest
-    : public testing::TestWithParam<TextAndImageModeTestCase> {};
+TEST_F(EditorMenuCardContextTest, BlockedMode) {
+  EditorMenuCardContext context = EditorMenuCardContext()
+                                      .set_editor_mode(EditorMode::kHardBlocked)
+                                      .set_lobster_mode(LobsterMode::kBlocked)
+                                      .build();
 
-INSTANTIATE_TEST_SUITE_P(
-    ,
-    EditorMenuCardContextTextAndImageModeTest,
-    testing::Values(
-        // magic_boost_revamp_enabled=false
-        TextAndImageModeTestCase{/*magic_boost_revamp_enabled=*/false,
-                                 EditorMode::kHardBlocked,
-                                 LobsterMode::kBlocked,
-                                 EditorMenuCardTextSelectionMode::kNoSelection,
-                                 TextAndImageMode::kBlocked},
-        TextAndImageModeTestCase{/*magic_boost_revamp_enabled=*/false,
-                                 EditorMode::kConsentNeeded,
-                                 LobsterMode::kBlocked,
-                                 EditorMenuCardTextSelectionMode::kNoSelection,
-                                 TextAndImageMode::kPromoCard},
-        TextAndImageModeTestCase{/*magic_boost_revamp_enabled=*/false,
-                                 EditorMode::kWrite, LobsterMode::kBlocked,
-                                 EditorMenuCardTextSelectionMode::kNoSelection,
-                                 TextAndImageMode::kEditorWriteOnly},
-        TextAndImageModeTestCase{/*magic_boost_revamp_enabled=*/false,
-                                 EditorMode::kRewrite, LobsterMode::kBlocked,
-                                 EditorMenuCardTextSelectionMode::kHasSelection,
-                                 TextAndImageMode::kEditorRewriteOnly},
-        TextAndImageModeTestCase{/*magic_boost_revamp_enabled=*/false,
-                                 EditorMode::kHardBlocked,
-                                 LobsterMode::kSelectedText,
-                                 EditorMenuCardTextSelectionMode::kHasSelection,
-                                 TextAndImageMode::kLobsterWithSelectedText},
-        TextAndImageModeTestCase{/*magic_boost_revamp_enabled=*/false,
-                                 EditorMode::kWrite,
-                                 LobsterMode::kNoSelectedText,
-                                 EditorMenuCardTextSelectionMode::kNoSelection,
-                                 TextAndImageMode::kEditorWriteAndLobster},
-        TextAndImageModeTestCase{/*magic_boost_revamp_enabled=*/false,
-                                 EditorMode::kRewrite,
-                                 LobsterMode::kSelectedText,
-                                 EditorMenuCardTextSelectionMode::kHasSelection,
-                                 TextAndImageMode::kEditorRewriteAndLobster},
-        // magic_boost_revamp_enabled=true
-        TextAndImageModeTestCase{/*magic_boost_revamp_enabled=*/true,
-                                 EditorMode::kHardBlocked,
-                                 LobsterMode::kNoSelectedText,
-                                 EditorMenuCardTextSelectionMode::kNoSelection,
-                                 TextAndImageMode::kLobsterWithNoSelectedText},
-        TextAndImageModeTestCase{/*magic_boost_revamp_enabled=*/true,
-                                 EditorMode::kHardBlocked,
-                                 LobsterMode::kSelectedText,
-                                 EditorMenuCardTextSelectionMode::kHasSelection,
-                                 TextAndImageMode::kLobsterWithSelectedText},
-        TextAndImageModeTestCase{/*magic_boost_revamp_enabled=*/true,
-                                 EditorMode::kConsentNeeded,
-                                 LobsterMode::kBlocked,
-                                 EditorMenuCardTextSelectionMode::kHasSelection,
-                                 TextAndImageMode::kEditorRewriteOnly},
-        TextAndImageModeTestCase{/*magic_boost_revamp_enabled=*/true,
-                                 EditorMode::kConsentNeeded,
-                                 LobsterMode::kBlocked,
-                                 EditorMenuCardTextSelectionMode::kNoSelection,
-                                 TextAndImageMode::kEditorWriteOnly},
-        TextAndImageModeTestCase{/*magic_boost_revamp_enabled=*/true,
-                                 EditorMode::kConsentNeeded,
-                                 LobsterMode::kSelectedText,
-                                 EditorMenuCardTextSelectionMode::kHasSelection,
-                                 TextAndImageMode::kEditorRewriteAndLobster},
-        TextAndImageModeTestCase{/*magic_boost_revamp_enabled=*/true,
-                                 EditorMode::kConsentNeeded,
-                                 LobsterMode::kNoSelectedText,
-                                 EditorMenuCardTextSelectionMode::kNoSelection,
-                                 TextAndImageMode::kEditorWriteAndLobster},
-        TextAndImageModeTestCase{/*magic_boost_revamp_enabled=*/true,
-                                 EditorMode::kWrite,
-                                 LobsterMode::kNoSelectedText,
-                                 EditorMenuCardTextSelectionMode::kNoSelection,
-                                 TextAndImageMode::kEditorWriteAndLobster},
-        TextAndImageModeTestCase{
-            /*magic_boost_revamp_enabled=*/true, EditorMode::kRewrite,
-            LobsterMode::kSelectedText,
-            EditorMenuCardTextSelectionMode::kHasSelection,
-            TextAndImageMode::kEditorRewriteAndLobster}, ));
-
-TEST_P(EditorMenuCardContextTextAndImageModeTest, TextAndImageModeIsCorrect) {
-  base::test::ScopedFeatureList feature_list;
-
-  feature_list.InitWithFeatureState(ash::features::kMagicBoostRevamp,
-                                    GetParam().magic_boost_revamp_enabled);
-
-  EditorMenuCardContext context =
-      EditorMenuCardContext()
-          .set_editor_mode(GetParam().editor_mode)
-          .set_lobster_mode(GetParam().lobster_mode)
-          .set_text_selection_mode(GetParam().text_selection_mode)
-          .build();
-
-  EXPECT_EQ(context.text_and_image_mode(), GetParam().expected_mode);
+  EXPECT_EQ(context.text_and_image_mode(), TextAndImageMode::kBlocked);
 }
 
-struct PresetQueriesTestCase {
-  bool magic_boost_revamp_enabled;
-  EditorMode editor_mode;
-  LobsterMode lobster_mode;
-  EditorMenuCardTextSelectionMode text_selection_mode;
-  std::vector<PresetTextQuery> editor_preset_queries;
-  std::vector<PresetTextQuery> expected_queries;
-};
-
-class EditorMenuCardContextPresetQueriesTest
-    : public testing::TestWithParam<PresetQueriesTestCase> {};
-
-INSTANTIATE_TEST_SUITE_P(
-    ,
-    EditorMenuCardContextPresetQueriesTest,
-    testing::Values(
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/false,
-                              EditorMode::kHardBlocked, LobsterMode::kBlocked,
-                              EditorMenuCardTextSelectionMode::kNoSelection,
-                              /*editor_preset_queries=*/{},
-                              /*expected_queries=*/{}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/false,
-                              EditorMode::kConsentNeeded, LobsterMode::kBlocked,
-                              EditorMenuCardTextSelectionMode::kNoSelection,
-                              /*editor_preset_queries=*/{},
-                              /*expected_queries=*/{}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/false,
-                              EditorMode::kRewrite,
-                              LobsterMode::kBlocked,
-                              EditorMenuCardTextSelectionMode::kHasSelection,
-                              /*editor_preset_queries=*/
-                              {PresetTextQuery(/*preset_text_id=*/"1",
-                                               u"Query 1",
-                                               PresetQueryCategory::kUnknown),
-                               PresetTextQuery(/*preset_text_id=*/"2",
-                                               u"Query 2",
-                                               PresetQueryCategory::kUnknown),
-                               PresetTextQuery(/*preset_text_id=*/"3",
-                                               u"Query 3",
-                                               PresetQueryCategory::kUnknown)},
-                              /*expected_queries=*/
-                              {PresetTextQuery(/*preset_text_id=*/"1",
-                                               u"Query 1",
-                                               PresetQueryCategory::kUnknown),
-                               PresetTextQuery(/*preset_text_id=*/"2",
-                                               u"Query 2",
-                                               PresetQueryCategory::kUnknown),
-                               PresetTextQuery(/*preset_text_id=*/"3",
-                                               u"Query 3",
-                                               PresetQueryCategory::kUnknown)}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/false,
-                              EditorMode::kWrite, LobsterMode::kBlocked,
-                              EditorMenuCardTextSelectionMode::kNoSelection,
-                              /*editor_preset_queries=*/{},
-                              /*expected_queries=*/{}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/false,
-                              EditorMode::kHardBlocked,
-                              LobsterMode::kNoSelectedText,
-                              EditorMenuCardTextSelectionMode::kNoSelection,
-                              /*editor_preset_queries=*/{},
-                              /*expected_queries=*/{}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/false,
-                              EditorMode::kConsentNeeded,
-                              LobsterMode::kNoSelectedText,
-                              EditorMenuCardTextSelectionMode::kNoSelection,
-                              /*editor_preset_queries=*/{},
-                              /*expected_queries=*/{}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/false,
-                              EditorMode::kWrite, LobsterMode::kNoSelectedText,
-                              EditorMenuCardTextSelectionMode::kNoSelection,
-                              /*editor_preset_queries=*/{},
-                              /*expected_queries=*/{}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/false,
-                              EditorMode::kHardBlocked,
-                              LobsterMode::kSelectedText,
-                              EditorMenuCardTextSelectionMode::kHasSelection,
-                              /*editor_preset_queries=*/
-                              {},
-                              /*expected_queries=*/
-                              {PresetTextQuery(
-                                  /*preset_text_id=*/kLobsterPresetId,
-                                  GetEditorMenuLobsterChipLabel(),
-                                  PresetQueryCategory::kLobster)}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/false,
-                              EditorMode::kConsentNeeded,
-                              LobsterMode::kSelectedText,
-                              EditorMenuCardTextSelectionMode::kHasSelection,
-                              /*editor_preset_queries=*/{},
-                              /*expected_queries=*/
-                              {}},
-        PresetQueriesTestCase{
-            /*magic_boost_revamp_enabled=*/false,
-            EditorMode::kRewrite,
-            LobsterMode::kSelectedText,
-            EditorMenuCardTextSelectionMode::kHasSelection,
-            /*editor_preset_queries=*/
-            {PresetTextQuery(/*preset_text_id=*/"1",
-                             u"Query 1",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/"2",
-                             u"Query 2",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/"3",
-                             u"Query 3",
-                             PresetQueryCategory::kUnknown)},
-            /*expected_queries=*/
-            {PresetTextQuery(/*preset_text_id=*/"1",
-                             u"Query 1",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/"2",
-                             u"Query 2",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/"3",
-                             u"Query 3",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/kLobsterPresetId,
-                             GetEditorMenuLobsterChipLabel(),
-                             PresetQueryCategory::kLobster)}},
-
-        // MagicBoostRevamp enabled
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/true,
-                              EditorMode::kHardBlocked, LobsterMode::kBlocked,
-                              EditorMenuCardTextSelectionMode::kNoSelection,
-                              /*editor_preset_queries=*/{},
-                              /*expected_queries=*/{}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/true,
-                              EditorMode::kConsentNeeded, LobsterMode::kBlocked,
-                              EditorMenuCardTextSelectionMode::kNoSelection,
-                              /*editor_preset_queries=*/{},
-                              /*expected_queries=*/{}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/true,
-                              EditorMode::kRewrite,
-                              LobsterMode::kBlocked,
-                              EditorMenuCardTextSelectionMode::kHasSelection,
-                              /*editor_preset_queries=*/
-                              {PresetTextQuery(/*preset_text_id=*/"1",
-                                               u"Query 1",
-                                               PresetQueryCategory::kUnknown),
-                               PresetTextQuery(/*preset_text_id=*/"2",
-                                               u"Query 2",
-                                               PresetQueryCategory::kUnknown),
-                               PresetTextQuery(/*preset_text_id=*/"3",
-                                               u"Query 3",
-                                               PresetQueryCategory::kUnknown)},
-                              /*expected_queries=*/
-                              {PresetTextQuery(/*preset_text_id=*/"1",
-                                               u"Query 1",
-                                               PresetQueryCategory::kUnknown),
-                               PresetTextQuery(/*preset_text_id=*/"2",
-                                               u"Query 2",
-                                               PresetQueryCategory::kUnknown),
-                               PresetTextQuery(/*preset_text_id=*/"3",
-                                               u"Query 3",
-                                               PresetQueryCategory::kUnknown)}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/true,
-                              EditorMode::kWrite, LobsterMode::kBlocked,
-                              EditorMenuCardTextSelectionMode::kNoSelection,
-                              /*editor_preset_queries=*/{},
-                              /*expected_queries=*/{}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/true,
-                              EditorMode::kHardBlocked,
-                              LobsterMode::kNoSelectedText,
-                              EditorMenuCardTextSelectionMode::kNoSelection,
-                              /*editor_preset_queries=*/{},
-                              /*expected_queries=*/{}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/true,
-                              EditorMode::kConsentNeeded,
-                              LobsterMode::kNoSelectedText,
-                              EditorMenuCardTextSelectionMode::kNoSelection,
-                              /*editor_preset_queries=*/{},
-                              /*expected_queries=*/{}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/true,
-                              EditorMode::kWrite, LobsterMode::kNoSelectedText,
-                              EditorMenuCardTextSelectionMode::kNoSelection,
-                              /*editor_preset_queries=*/{},
-                              /*expected_queries=*/{}},
-        PresetQueriesTestCase{/*magic_boost_revamp_enabled=*/true,
-                              EditorMode::kHardBlocked,
-                              LobsterMode::kSelectedText,
-                              EditorMenuCardTextSelectionMode::kHasSelection,
-                              /*editor_preset_queries=*/
-                              {},
-                              /*expected_queries=*/
-                              {PresetTextQuery(
-                                  /*preset_text_id=*/kLobsterPresetId,
-                                  GetEditorMenuLobsterChipLabel(),
-                                  PresetQueryCategory::kLobster)}},
-        PresetQueriesTestCase{
-            /*magic_boost_revamp_enabled=*/true,
-            EditorMode::kConsentNeeded,
-            LobsterMode::kSelectedText,
-            EditorMenuCardTextSelectionMode::kHasSelection,
-            /*editor_preset_queries=*/
-            {PresetTextQuery(/*preset_text_id=*/"1",
-                             u"Query 1",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/"2",
-                             u"Query 2",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/"3",
-                             u"Query 3",
-                             PresetQueryCategory::kUnknown)},
-            /*expected_queries=*/
-            {PresetTextQuery(/*preset_text_id=*/"1",
-                             u"Query 1",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/"2",
-                             u"Query 2",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/"3",
-                             u"Query 3",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/kLobsterPresetId,
-                             GetEditorMenuLobsterChipLabel(),
-                             PresetQueryCategory::kLobster)}},
-        PresetQueriesTestCase{
-            /*magic_boost_revamp_enabled=*/true,
-            EditorMode::kRewrite,
-            LobsterMode::kSelectedText,
-            EditorMenuCardTextSelectionMode::kHasSelection,
-            /*editor_preset_queries=*/
-            {PresetTextQuery(/*preset_text_id=*/"1",
-                             u"Query 1",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/"2",
-                             u"Query 2",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/"3",
-                             u"Query 3",
-                             PresetQueryCategory::kUnknown)},
-            /*expected_queries=*/
-            {PresetTextQuery(/*preset_text_id=*/"1",
-                             u"Query 1",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/"2",
-                             u"Query 2",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/"3",
-                             u"Query 3",
-                             PresetQueryCategory::kUnknown),
-             PresetTextQuery(/*preset_text_id=*/kLobsterPresetId,
-                             GetEditorMenuLobsterChipLabel(),
-                             PresetQueryCategory::kLobster)}}, )
-
-);
-
-TEST_P(EditorMenuCardContextPresetQueriesTest, PresetQueriesAreCorrect) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatureState(
-      ash::features::kMagicBoostRevamp, GetParam().magic_boost_revamp_enabled);
-
+TEST_F(EditorMenuCardContextTest, PromoCardMode) {
   EditorMenuCardContext context =
       EditorMenuCardContext()
-          .set_editor_mode(GetParam().editor_mode)
-          .set_lobster_mode(GetParam().lobster_mode)
-          .set_text_selection_mode(
-              GetParam().text_selection_mode)  // Add this line
-          .set_editor_preset_queries(GetParam().editor_preset_queries)
+          .set_editor_mode(EditorMode::kConsentNeeded)
+          .set_lobster_mode(LobsterMode::kBlocked)
+          .build();
+
+  EXPECT_EQ(context.text_and_image_mode(), TextAndImageMode::kPromoCard);
+}
+
+TEST_F(EditorMenuCardContextTest, EditorWriteOnly) {
+  EditorMenuCardContext context = EditorMenuCardContext()
+                                      .set_editor_mode(EditorMode::kWrite)
+                                      .set_lobster_mode(LobsterMode::kBlocked)
+                                      .build();
+
+  EXPECT_EQ(context.text_and_image_mode(), TextAndImageMode::kEditorWriteOnly);
+}
+
+TEST_F(EditorMenuCardContextTest, EditorRewriteOnly) {
+  EditorMenuCardContext context = EditorMenuCardContext()
+                                      .set_editor_mode(EditorMode::kRewrite)
+                                      .set_lobster_mode(LobsterMode::kBlocked)
+                                      .build();
+
+  EXPECT_EQ(context.text_and_image_mode(),
+            TextAndImageMode::kEditorRewriteOnly);
+}
+
+TEST_F(EditorMenuCardContextTest, LobsterWithSelectedText) {
+  EditorMenuCardContext context =
+      EditorMenuCardContext()
+          .set_editor_mode(EditorMode::kHardBlocked)
+          .set_lobster_mode(LobsterMode::kSelectedText)
+          .build();
+
+  EXPECT_EQ(context.text_and_image_mode(),
+            TextAndImageMode::kLobsterWithSelectedText);
+}
+
+TEST_F(EditorMenuCardContextTest, EditorWriteAndLobster) {
+  EditorMenuCardContext context =
+      EditorMenuCardContext()
+          .set_editor_mode(EditorMode::kWrite)
+          .set_lobster_mode(LobsterMode::kNoSelectedText)
+          .build();
+
+  EXPECT_EQ(context.text_and_image_mode(),
+            TextAndImageMode::kEditorWriteAndLobster);
+}
+
+TEST_F(EditorMenuCardContextTest, EditorRewriteAndLobster) {
+  EditorMenuCardContext context =
+      EditorMenuCardContext()
+          .set_editor_mode(EditorMode::kRewrite)
+          .set_lobster_mode(LobsterMode::kSelectedText)
+          .build();
+
+  EXPECT_EQ(context.text_and_image_mode(),
+            TextAndImageMode::kEditorRewriteAndLobster);
+}
+
+TEST_F(EditorMenuCardContextTest,
+       ReturnsEditorPresetQueriesWhenLobsterIsBlocked) {
+  EditorMenuCardContext context =
+      EditorMenuCardContext()
+          .set_editor_mode(EditorMode::kRewrite)
+          .set_lobster_mode(LobsterMode::kBlocked)
+          .set_editor_preset_queries({
+              PresetTextQuery(/*preset_text_id=*/"1", u"Query 1",
+                              PresetQueryCategory::kUnknown),
+              PresetTextQuery(/*preset_text_id=*/"2", u"Query 2",
+                              PresetQueryCategory::kUnknown),
+              PresetTextQuery(/*preset_text_id=*/"3", u"Query 3",
+                              PresetQueryCategory::kUnknown),
+          })
           .build();
 
   EXPECT_THAT(context.preset_queries(),
-              testing::ElementsAreArray(GetParam().expected_queries));
+              ElementsAre(PresetTextQuery(/*preset_text_id=*/"1", u"Query 1",
+                                          PresetQueryCategory::kUnknown),
+                          PresetTextQuery(/*preset_text_id=*/"2", u"Query 2",
+                                          PresetQueryCategory::kUnknown),
+                          PresetTextQuery(/*preset_text_id=*/"3", u"Query 3",
+                                          PresetQueryCategory::kUnknown)));
+}
+
+TEST_F(EditorMenuCardContextTest, ReturnsNoChipsWhenBothFeaturesAreBlocked) {
+  EditorMenuCardContext context = EditorMenuCardContext()
+                                      .set_editor_mode(EditorMode::kHardBlocked)
+                                      .set_lobster_mode(LobsterMode::kBlocked)
+                                      .build();
+
+  EXPECT_TRUE(context.preset_queries().empty());
+}
+
+TEST_F(EditorMenuCardContextTest, ReturnsNoChipsInPromoCardMode) {
+  EditorMenuCardContext context =
+      EditorMenuCardContext()
+          .set_editor_mode(EditorMode::kConsentNeeded)
+          .set_lobster_mode(LobsterMode::kBlocked)
+          .build();
+
+  EXPECT_TRUE(context.preset_queries().empty());
+}
+
+TEST_F(EditorMenuCardContextTest,
+       ReturnsNoChipsWhenBothFeaturesAreEnabledInWriteMode) {
+  EditorMenuCardContext context =
+      EditorMenuCardContext()
+          .set_editor_mode(EditorMode::kWrite)
+          .set_lobster_mode(LobsterMode::kNoSelectedText)
+          .build();
+
+  EXPECT_TRUE(context.preset_queries().empty());
+}
+
+TEST_F(EditorMenuCardContextTest,
+       ReturnsEditorAndLobsterChipWhenBothFeaturesAreEnabledInRewriteMode) {
+  EditorMenuCardContext context =
+      EditorMenuCardContext()
+          .set_editor_mode(EditorMode::kRewrite)
+          .set_lobster_mode(LobsterMode::kSelectedText)
+          .set_editor_preset_queries({
+              PresetTextQuery(/*preset_text_id=*/"1", u"Query 1",
+                              PresetQueryCategory::kUnknown),
+              PresetTextQuery(/*preset_text_id=*/"2", u"Query 2",
+                              PresetQueryCategory::kUnknown),
+              PresetTextQuery(/*preset_text_id=*/"3", u"Query 3",
+                              PresetQueryCategory::kUnknown),
+          })
+          .build();
+
+  EXPECT_THAT(context.preset_queries(),
+              ElementsAre(PresetTextQuery(/*preset_text_id=*/"1", u"Query 1",
+                                          PresetQueryCategory::kUnknown),
+                          PresetTextQuery(/*preset_text_id=*/"2", u"Query 2",
+                                          PresetQueryCategory::kUnknown),
+                          PresetTextQuery(/*preset_text_id=*/"3", u"Query 3",
+                                          PresetQueryCategory::kUnknown),
+                          PresetTextQuery(
+                              /*preset_text_id=*/kLobsterPresetId,
+                              GetEditorMenuLobsterChipLabel(),
+                              PresetQueryCategory::kLobster)));
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.cc b/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.cc
index 00f8d8a..8ce920ee 100644
--- a/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.cc
+++ b/chrome/browser/ui/ash/editor_menu/editor_menu_controller_impl.cc
@@ -15,7 +15,6 @@
 #include "ash/webui/settings/public/constants/routes.mojom.h"
 #include "ash/webui/settings/public/constants/setting.mojom.h"
 #include "base/feature_list.h"
-#include "base/notreached.h"
 #include "base/strings/strcat.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
@@ -27,7 +26,6 @@
 #include "chrome/browser/ash/lobster/lobster_system_state_provider.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_manager_factory.h"
-#include "chrome/browser/ui/ash/editor_menu/editor_menu_card_context.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_promo_card_view.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_strings.h"
 #include "chrome/browser/ui/ash/editor_menu/editor_menu_view.h"
@@ -106,9 +104,7 @@
                       /*text_selection_mode=*/selected_text.length() > 0
                           ? EditorTextSelectionMode::kHasSelection
                           : EditorTextSelectionMode::kNoSelection,
-                      /*consent_status_settled=*/false,
-                      /*preset_queries=*/{}));
-    return;
+                      false, {}));
   }
 
   card_session_->editor_manager()->GetEditorPanelContext(base::BindOnce(
@@ -282,11 +278,6 @@
           .set_editor_preset_queries(editor_context.preset_queries)
           .set_editor_mode(editor_context.mode)
           .set_lobster_mode(lobster_mode)
-          .set_text_selection_mode(
-              editor_context.text_selection_mode ==
-                      EditorTextSelectionMode::kHasSelection
-                  ? EditorMenuCardTextSelectionMode::kHasSelection
-                  : EditorMenuCardTextSelectionMode::kNoSelection)
           .build());
 }
 
@@ -300,11 +291,6 @@
           .set_editor_preset_queries(editor_context.preset_queries)
           .set_editor_mode(editor_context.mode)
           .set_lobster_mode(lobster_mode)
-          .set_text_selection_mode(
-              editor_context.text_selection_mode ==
-                      EditorTextSelectionMode::kHasSelection
-                  ? EditorMenuCardTextSelectionMode::kHasSelection
-                  : EditorMenuCardTextSelectionMode::kNoSelection)
           .build();
 
   TextAndImageMode text_and_image_mode =
@@ -314,9 +300,6 @@
     case TextAndImageMode::kBlocked:
       break;
     case TextAndImageMode::kPromoCard:
-      if (ash::features::IsMagicBoostRevampEnabled()) {
-        NOTREACHED();
-      }
       editor_menu_widget_ =
           EditorMenuPromoCardView::CreateWidget(anchor_bounds, this);
       editor_menu_widget_->ShowInactive();
diff --git a/chrome/browser/ui/browser_tabrestore.cc b/chrome/browser/ui/browser_tabrestore.cc
index f4e0d31..73e9e6c 100644
--- a/chrome/browser/ui/browser_tabrestore.cc
+++ b/chrome/browser/ui/browser_tabrestore.cc
@@ -294,7 +294,8 @@
   int insertion_index = tab_strip->active_index();
   tab_strip->InsertWebContentsAt(
       insertion_index + 1, std::move(web_contents),
-      AddTabTypes::ADD_ACTIVE | AddTabTypes::ADD_INHERIT_OPENER);
+      AddTabTypes::ADD_ACTIVE | AddTabTypes::ADD_INHERIT_OPENER,
+      tab_strip->GetTabGroupForTab(insertion_index));
   tab_strip->CloseWebContentsAt(insertion_index, TabCloseTypes::CLOSE_NONE);
 
   LoadRestoredTabIfVisible(browser, raw_web_contents);
diff --git a/chrome/browser/ui/browser_window/browser_window_features.cc b/chrome/browser/ui/browser_window/browser_window_features.cc
index e40ffd46..1982aa5e 100644
--- a/chrome/browser/ui/browser_window/browser_window_features.cc
+++ b/chrome/browser/ui/browser_window/browser_window_features.cc
@@ -128,7 +128,8 @@
 #if BUILDFLAG(ENABLE_GLIC)
     if (GlicEnabling::IsProfileEligible(browser->GetProfile())) {
       DCHECK(features::IsTabstripComboButtonEnabled());
-      glic_nudge_controller_ = std::make_unique<tabs::GlicNudgeController>();
+      glic_nudge_controller_ =
+          std::make_unique<tabs::GlicNudgeController>(browser);
     }
 #endif  // BUILDFLAG(ENABLE_GLIC)
   }
diff --git a/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc b/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
index ddcec244..310038d 100644
--- a/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
+++ b/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
@@ -542,8 +542,7 @@
   cookie_controls()->Update(web_contents());
   testing::Mock::VerifyAndClearExpectations(mock());
 
-  // Create incognito web_contents and
-  // content_settings::CookieControlsController.
+  // Create incognito web_contents and CookieControlsController.
   std::unique_ptr<content::WebContents> incognito_web_contents =
       content::WebContentsTester::CreateTestWebContents(
           profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true), nullptr);
@@ -552,7 +551,7 @@
       std::make_unique<PageSpecificContentSettingsDelegate>(
           incognito_web_contents.get()));
   auto* tester = content::WebContentsTester::For(incognito_web_contents.get());
-  MockCookieControlsObserver incognito_mock_;
+  MockCookieControlsObserver incognito_mock;
   content_settings::CookieControlsController incognito_cookie_controls(
       CookieSettingsFactory::GetForProfile(
           profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true)),
@@ -560,11 +559,11 @@
       HostContentSettingsMapFactory::GetForProfile(profile()),
       TrackingProtectionSettingsFactory::GetForProfile(profile()),
       /*is_incognito_profile=*/true);
-  incognito_cookie_controls.AddObserver(&incognito_mock_);
+  incognito_cookie_controls.AddObserver(&incognito_mock);
 
   // Navigate incognito web_contents to the same URL.
   tester->NavigateAndCommit(GURL(kUrl));
-  EXPECT_CALL(incognito_mock_,
+  EXPECT_CALL(incognito_mock,
               OnStatusChanged(
                   /*controls_visible=*/true, /*protections_on=*/true,
                   CookieControlsEnforcement::kNoEnforcement,
@@ -574,13 +573,13 @@
                       BlockingStatus::kBlocked)));
 
   EXPECT_CALL(
-      incognito_mock_,
+      incognito_mock,
       OnCookieControlsIconStatusChanged(
           /*icon_visible=*/false, /*protections_on=*/true,
           CookieBlocking3pcdStatus::kNotIn3pcd, /*should_highlight=*/false));
   incognito_cookie_controls.Update(incognito_web_contents.get());
   testing::Mock::VerifyAndClearExpectations(mock());
-  testing::Mock::VerifyAndClearExpectations(&incognito_mock_);
+  testing::Mock::VerifyAndClearExpectations(&incognito_mock);
 
   // Allow cookies in regular mode should also allow in incognito but enforced
   // through regular mode.
@@ -597,7 +596,7 @@
                            CookieBlocking3pcdStatus::kNotIn3pcd,
                            /*should_highlight=*/false));
 
-  EXPECT_CALL(incognito_mock_,
+  EXPECT_CALL(incognito_mock,
               OnStatusChanged(
                   /*controls_visible=*/true, /*protections_on=*/false,
                   CookieControlsEnforcement::kEnforcedByCookieSetting,
@@ -607,13 +606,13 @@
                       BlockingStatus::kAllowed)));
 
   EXPECT_CALL(
-      incognito_mock_,
+      incognito_mock,
       OnCookieControlsIconStatusChanged(
           /*icon_visible=*/true, /*protections_on=*/false,
           CookieBlocking3pcdStatus::kNotIn3pcd, /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
   testing::Mock::VerifyAndClearExpectations(mock());
-  testing::Mock::VerifyAndClearExpectations(&incognito_mock_);
+  testing::Mock::VerifyAndClearExpectations(&incognito_mock);
 
   // This should be enforced regardless of the default cookie setting in the
   // default profile.
@@ -633,7 +632,7 @@
                            CookieBlocking3pcdStatus::kNotIn3pcd,
                            /*should_highlight=*/false));
 
-  EXPECT_CALL(incognito_mock_,
+  EXPECT_CALL(incognito_mock,
               OnStatusChanged(
                   /*controls_visible=*/true, /*protections_on=*/false,
                   CookieControlsEnforcement::kEnforcedByCookieSetting,
@@ -643,7 +642,7 @@
                       BlockingStatus::kAllowed)));
 
   EXPECT_CALL(
-      incognito_mock_,
+      incognito_mock,
       OnCookieControlsIconStatusChanged(
           /*icon_visible=*/true, /*protections_on=*/false,
           CookieBlocking3pcdStatus::kNotIn3pcd, /*should_highlight=*/false));
@@ -652,7 +651,7 @@
       static_cast<int>(content_settings::CookieControlsMode::kIncognitoOnly));
   incognito_cookie_controls.Update(incognito_web_contents.get());
   testing::Mock::VerifyAndClearExpectations(mock());
-  testing::Mock::VerifyAndClearExpectations(&incognito_mock_);
+  testing::Mock::VerifyAndClearExpectations(&incognito_mock);
 }
 
 TEST_F(CookieControlsUserBypassTest, ThirdPartyCookiesException) {
@@ -1766,15 +1765,6 @@
 
   void SetUp() override {
     CookieControlsUserBypassTest::SetUp();
-    if (std::get<0>(GetParam())) {
-      cookie_settings()->SetThirdPartyCookieSetting(
-          GURL(kUrl), ContentSetting::CONTENT_SETTING_BLOCK);
-    } else {
-      tracking_protection_settings()->AddTrackingProtectionException(
-          GURL(kUrl));
-      cookie_settings()->SetThirdPartyCookieSetting(
-          GURL(kUrl), ContentSetting::CONTENT_SETTING_ALLOW);
-    }
 
     std::vector<base::test::FeatureRef> enabled_features = {};
     if (std::get<1>(GetParam())) {
@@ -1814,22 +1804,53 @@
     return features_list;
   }
 
+  void AddUserBypassException(Profile* profile) {
+    if (std::get<0>(GetParam())) {
+      CookieSettingsFactory::GetForProfile(profile)->SetThirdPartyCookieSetting(
+          GURL(kUrl), ContentSetting::CONTENT_SETTING_BLOCK);
+    } else {
+      TrackingProtectionSettingsFactory::GetForProfile(profile)
+          ->AddTrackingProtectionException(GURL(kUrl));
+      CookieSettingsFactory::GetForProfile(profile)->SetThirdPartyCookieSetting(
+          GURL(kUrl), ContentSetting::CONTENT_SETTING_ALLOW);
+    }
+  }
+
  private:
   base::test::ScopedFeatureList feature_list_;
 };
 
 TEST_P(CookieControlsUserBypassTrackingProtectionUiTest,
-       AddsActFeaturesToVectorBasedOnFeatureAndExceptionStatus) {
-  NavigateAndCommit(GURL(kUrl));
-  EXPECT_CALL(*mock(),
+       AddsActFeaturesToVectorInIncognitoBasedOnFeatureAndExceptionStatus) {
+  auto* incognito_profile =
+      profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true);
+  std::unique_ptr<content::WebContents> incognito_web_contents =
+      content::WebContentsTester::CreateTestWebContents(incognito_profile,
+                                                        nullptr);
+  content_settings::PageSpecificContentSettings::CreateForWebContents(
+      incognito_web_contents.get(),
+      std::make_unique<PageSpecificContentSettingsDelegate>(
+          incognito_web_contents.get()));
+  content_settings::CookieControlsController incognito_cookie_controls(
+      CookieSettingsFactory::GetForProfile(incognito_profile),
+      CookieSettingsFactory::GetForProfile(profile()),
+      HostContentSettingsMapFactory::GetForProfile(incognito_profile),
+      TrackingProtectionSettingsFactory::GetForProfile(incognito_profile),
+      /*is_incognito_profile=*/true);
+  MockCookieControlsObserver incognito_mock;
+  incognito_cookie_controls.AddObserver(&incognito_mock);
+  AddUserBypassException(incognito_profile);
+  auto* tester = content::WebContentsTester::For(incognito_web_contents.get());
+  tester->NavigateAndCommit(GURL(kUrl));
+
+  EXPECT_CALL(incognito_mock,
               OnStatusChanged(
                   /*controls_visible=*/true, std::get<0>(GetParam()),
                   CookieControlsEnforcement::kNoEnforcement,
                   CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration(),
                   GetFeatureVector(CookieControlsEnforcement::kNoEnforcement)));
-
-  cookie_controls()->Update(web_contents());
-  testing::Mock::VerifyAndClearExpectations(mock());
+  incognito_cookie_controls.Update(incognito_web_contents.get());
+  testing::Mock::VerifyAndClearExpectations(&incognito_mock);
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/chrome/browser/ui/performance_controls/memory_saver_opt_in_iph_controller.cc b/chrome/browser/ui/performance_controls/memory_saver_opt_in_iph_controller.cc
index 69f46e68..b2f8e2da7 100644
--- a/chrome/browser/ui/performance_controls/memory_saver_opt_in_iph_controller.cc
+++ b/chrome/browser/ui/performance_controls/memory_saver_opt_in_iph_controller.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/performance_controls/memory_saver_opt_in_iph_controller.h"
 
+#include "base/system/sys_info.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
@@ -13,6 +14,10 @@
 #include "components/performance_manager/public/user_tuning/prefs.h"
 #include "components/prefs/pref_service.h"
 
+// Devices with at least 16 GB of total memory should never be shown the memory
+// saver opt-in IPH.
+constexpr int kMemoryCap16GB = 16 * 1024;
+
 MemorySaverOptInIPHController::MemorySaverOptInIPHController(
     BrowserWindowInterface* interface)
     : browser_window_interface_(interface) {
@@ -39,7 +44,8 @@
   auto* const manager = performance_manager::user_tuning::
       UserPerformanceTuningManager::GetInstance();
   if (manager->IsMemorySaverModeDefault() &&
-      !manager->IsMemorySaverModeActive()) {
+      !manager->IsMemorySaverModeActive() &&
+      base::SysInfo::AmountOfPhysicalMemoryMB() <= kMemoryCap16GB) {
     browser_window_interface_->GetUserEducationInterface()
         ->MaybeShowStartupFeaturePromo(
             feature_engagement::kIPHMemorySaverModeFeature);
diff --git a/chrome/browser/ui/tabs/glic_nudge_controller.cc b/chrome/browser/ui/tabs/glic_nudge_controller.cc
index 69b79fe..1624cec 100644
--- a/chrome/browser/ui/tabs/glic_nudge_controller.cc
+++ b/chrome/browser/ui/tabs/glic_nudge_controller.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/tabs/glic_nudge_controller.h"
 
+#include "chrome/browser/contextual_cueing/contextual_cueing_helper.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/views/tabs/tab_strip_action_container.h"
@@ -11,7 +12,13 @@
 
 namespace tabs {
 
-GlicNudgeController::GlicNudgeController() = default;
+GlicNudgeController::GlicNudgeController(
+    BrowserWindowInterface* browser_window_interface)
+    : browser_window_interface_(browser_window_interface) {
+  browser_subscriptions_.push_back(
+      browser_window_interface->RegisterActiveTabDidChange(base::BindRepeating(
+          &GlicNudgeController::OnActiveTabChanged, base::Unretained(this))));
+}
 
 GlicNudgeController::~GlicNudgeController() = default;
 
@@ -21,9 +28,29 @@
 
 void GlicNudgeController::UpdateNudgeLabel(content::WebContents* web_contents,
                                            const std::string& nudge_label) {
+  auto* const tab_interface =
+      browser_window_interface_->GetActiveTabInterface();
+  if (tab_interface->GetContents() != web_contents) {
+    return;
+  }
   for (auto& observer : observers_) {
     observer.OnTriggerGlicNudgeUI(nudge_label);
   }
 }
 
+void GlicNudgeController::OnActiveTabChanged(
+    BrowserWindowInterface* browser_interface) {
+  auto* const tab_interface = browser_interface->GetActiveTabInterface();
+  auto* web_contents = tab_interface->GetContents();
+  auto* contextual_cueing_helper =
+      contextual_cueing::ContextualCueingHelper::FromWebContents(web_contents);
+  if (!contextual_cueing_helper) {
+    return;
+  }
+  for (auto& observer : observers_) {
+    observer.OnTriggerGlicNudgeUI(
+        contextual_cueing_helper->last_navigation_cue_label());
+  }
+}
+
 }  // namespace tabs
diff --git a/chrome/browser/ui/tabs/glic_nudge_controller.h b/chrome/browser/ui/tabs/glic_nudge_controller.h
index 7665281..3c94654 100644
--- a/chrome/browser/ui/tabs/glic_nudge_controller.h
+++ b/chrome/browser/ui/tabs/glic_nudge_controller.h
@@ -10,9 +10,11 @@
 #include "chrome/browser/ui/tabs/glic_nudge_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
 
+class BrowserWindowInterface;
+
 namespace content {
 class WebContents;
-}
+}  // namespace content
 
 namespace tabs {
 
@@ -20,7 +22,8 @@
 // targeted.
 class GlicNudgeController {
  public:
-  GlicNudgeController();
+  explicit GlicNudgeController(
+      BrowserWindowInterface* browser_window_interface);
   GlicNudgeController(const GlicNudgeController&) = delete;
   GlicNudgeController& operator=(const GlicNudgeController& other) = delete;
   virtual ~GlicNudgeController();
@@ -37,14 +40,24 @@
     return observers_.HasObserver(observer);
   }
 
+  // Updates the `nudge_label` for `web_contents`, if the WebContents is active.
   void UpdateNudgeLabel(content::WebContents* web_contents,
                         const std::string& nudge_label);
 
  private:
+  // Called when the active tab changes, to update the nudge UI appropriate for
+  // the tab.
+  void OnActiveTabChanged(BrowserWindowInterface* browser_interface);
+
   // Returns whether the nudge should be shown in the tabstrip for glic.
   bool GlicNudgeCriteriaMet();
 
+  // The BrowserWindowInterface that owns `this`.
+  const raw_ptr<BrowserWindowInterface> browser_window_interface_;
+
   base::ObserverList<GlicNudgeObserver> observers_;
+
+  std::vector<base::CallbackListSubscription> browser_subscriptions_;
 };
 
 }  // namespace tabs
diff --git a/chrome/browser/ui/tabs/glic_nudge_observer.h b/chrome/browser/ui/tabs/glic_nudge_observer.h
index 6e355df..2ae0357 100644
--- a/chrome/browser/ui/tabs/glic_nudge_observer.h
+++ b/chrome/browser/ui/tabs/glic_nudge_observer.h
@@ -9,7 +9,9 @@
 
 class GlicNudgeObserver : public base::CheckedObserver {
  public:
-  // Called when all checks pass to be able to show the glic nudge UI.
+  // Called when the glic nudge UI needs to be triggered, or to be turned off.
+  // When the UI needs to be shown `label' holds the nudge label. When the nudge
+  // UI should be turned off, `label` is empty.
   virtual void OnTriggerGlicNudgeUI(std::string label) {}
 };
 
diff --git a/chrome/browser/ui/tabs/public/tab_features.h b/chrome/browser/ui/tabs/public/tab_features.h
index 23b47c2e..a7f2804 100644
--- a/chrome/browser/ui/tabs/public/tab_features.h
+++ b/chrome/browser/ui/tabs/public/tab_features.h
@@ -135,10 +135,6 @@
     return commerce_ui_tab_helper_.get();
   }
 
-  contextual_cueing::ContextualCueingHelper* contextual_cueing_tab_helper() {
-    return contextual_cueing_helper_.get();
-  }
-
   privacy_sandbox::PrivacySandboxTabObserver* privacy_sandbox_tab_observer() {
     return privacy_sandbox_tab_observer_.get();
   }
@@ -212,10 +208,6 @@
   // Responsible for commerce related features.
   std::unique_ptr<commerce::CommerceUiTabHelper> commerce_ui_tab_helper_;
 
-  // Responsible for contextual cueing features.
-  std::unique_ptr<contextual_cueing::ContextualCueingHelper>
-      contextual_cueing_helper_;
-
   // Responsible for updating status indicator of the pinned translate button.
   std::unique_ptr<PinnedTranslateActionListener>
       pinned_translate_action_listener_;
diff --git a/chrome/browser/ui/tabs/tab_features.cc b/chrome/browser/ui/tabs/tab_features.cc
index 2b5ac6d..b714aede 100644
--- a/chrome/browser/ui/tabs/tab_features.cc
+++ b/chrome/browser/ui/tabs/tab_features.cc
@@ -138,11 +138,8 @@
           CreateCommerceUiTabHelper(tab.GetContents(), profile);
     }
 
-    if (!profile->IsIncognitoProfile()) {
-      contextual_cueing_helper_ =
-          contextual_cueing::ContextualCueingHelper::MaybeCreateForWebContents(
-              tab.GetContents());
-    }
+    contextual_cueing::ContextualCueingHelper::MaybeCreateForWebContents(
+        tab.GetContents());
 
     privacy_sandbox_tab_observer_ =
         std::make_unique<privacy_sandbox::PrivacySandboxTabObserver>(
diff --git a/chrome/browser/ui/views/autofill/popup/popup_cell_utils.cc b/chrome/browser/ui/views/autofill/popup/popup_cell_utils.cc
index c14a754..032d3aa4 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_cell_utils.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_cell_utils.cc
@@ -143,6 +143,7 @@
     case Suggestion::Icon::kOfferTag:
     case Suggestion::Icon::kPenSpark:
     case Suggestion::Icon::kPlusAddress:
+    case Suggestion::Icon::kSaveAndFill:
     case Suggestion::Icon::kScanCreditCard:
     case Suggestion::Icon::kSettings:
     case Suggestion::Icon::kSettingsAndroid:
@@ -317,6 +318,8 @@
 #else
       return ImageModelFromVectorIcon(vector_icons::kEmailIcon, kIconSize);
 #endif
+    case Suggestion::Icon::kSaveAndFill:
+      return ImageModelFromVectorIcon(kCreditCardIcon, kIconSize);
     case Suggestion::Icon::kSettings:
       return ImageModelFromVectorIcon(omnibox::kProductIcon, kIconSize);
     case Suggestion::Icon::kUndo:
diff --git a/chrome/browser/ui/views/dark_mode_manager_linux.cc b/chrome/browser/ui/views/dark_mode_manager_linux.cc
index 968be4a..e58b7d3 100644
--- a/chrome/browser/ui/views/dark_mode_manager_linux.cc
+++ b/chrome/browser/ui/views/dark_mode_manager_linux.cc
@@ -18,21 +18,9 @@
 
 namespace ui {
 
-namespace {
-
-scoped_refptr<dbus::Bus> CreateBus() {
-  dbus::Bus::Options options;
-  options.bus_type = dbus::Bus::SESSION;
-  options.connection_type = dbus::Bus::PRIVATE;
-  options.dbus_task_runner = dbus_thread_linux::GetTaskRunner();
-  return base::MakeRefCounted<dbus::Bus>(options);
-}
-
-}  // namespace
-
 DarkModeManagerLinux::DarkModeManagerLinux()
     : DarkModeManagerLinux(
-          CreateBus(),
+          dbus_thread_linux::GetSharedSystemBus(),
           ui::GetDefaultLinuxUiTheme(),
           &ui::GetLinuxUiThemes(),
           std::vector<raw_ptr<ui::NativeTheme, VectorExperimental>>{
@@ -96,16 +84,7 @@
   }
 }
 
-DarkModeManagerLinux::~DarkModeManagerLinux() {
-  settings_proxy_ = nullptr;
-  dbus::Bus* const bus_ptr = bus_.get();
-  // `task_runner` may be nullptr in testing.
-  if (auto* task_runner = bus_ptr->GetDBusTaskRunner()) {
-    task_runner->PostTask(
-        FROM_HERE,
-        base::BindOnce(&dbus::Bus::ShutdownAndBlock, std::move(bus_)));
-  }
-}
+DarkModeManagerLinux::~DarkModeManagerLinux() = default;
 
 void DarkModeManagerLinux::OnNativeThemeUpdated(
     ui::NativeTheme* observed_theme) {
diff --git a/chrome/browser/ui/views/dark_mode_manager_linux_unittest.cc b/chrome/browser/ui/views/dark_mode_manager_linux_unittest.cc
index e928a27..313af3a 100644
--- a/chrome/browser/ui/views/dark_mode_manager_linux_unittest.cc
+++ b/chrome/browser/ui/views/dark_mode_manager_linux_unittest.cc
@@ -168,7 +168,6 @@
   }
 
   void TearDown() override {
-    EXPECT_CALL(*mock_bus_, GetDBusTaskRunner()).WillOnce(Return(nullptr));
     manager_.reset();
   }
 
diff --git a/chrome/browser/ui/views/frame/dbus_appmenu_registrar.cc b/chrome/browser/ui/views/frame/dbus_appmenu_registrar.cc
index 36466d1..ec58351 100644
--- a/chrome/browser/ui/views/frame/dbus_appmenu_registrar.cc
+++ b/chrome/browser/ui/views/frame/dbus_appmenu_registrar.cc
@@ -53,13 +53,8 @@
   menus_.erase(menu);
 }
 
-DbusAppmenuRegistrar::DbusAppmenuRegistrar() {
-  dbus::Bus::Options bus_options;
-  bus_options.bus_type = dbus::Bus::SESSION;
-  bus_options.connection_type = dbus::Bus::PRIVATE;
-  bus_options.dbus_task_runner = dbus_thread_linux::GetTaskRunner();
-  bus_ = base::MakeRefCounted<dbus::Bus>(bus_options);
-
+DbusAppmenuRegistrar::DbusAppmenuRegistrar()
+    : bus_(dbus_thread_linux::GetSharedSessionBus()) {
   registrar_proxy_ = bus_->GetObjectProxy(
       kAppMenuRegistrarName, dbus::ObjectPath(kAppMenuRegistrarPath));
 
diff --git a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view_interactive_uitest.cc b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view_interactive_uitest.cc
index 5e31ecc..3971b1c8 100644
--- a/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view_interactive_uitest.cc
@@ -16,13 +16,9 @@
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/frame/picture_in_picture_browser_frame_view.h"
-#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
-#include "chrome/browser/ui/views/permissions/chip/permission_dashboard_controller.h"
-#include "chrome/browser/ui/views/permissions/chip/permission_dashboard_view.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/base/ui_test_utils.h"
-#include "components/permissions/permission_request_manager.h"
 #include "content/public/browser/document_picture_in_picture_window_controller.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/isolated_world_ids.h"
@@ -110,49 +106,6 @@
   std::vector<gfx::Animation*> animations_;
 };
 
-class ChipAnimationObserver : PermissionChipView::Observer {
- public:
-  enum class QuitOnEvent {
-    kExpand,
-    kCollapse,
-    kVisibiltyTrue,
-    kVisibiltyFalse,
-  };
-
-  explicit ChipAnimationObserver(PermissionChipView* chip) {
-    observation_.Observe(chip);
-  }
-
-  void WaitForChip() { loop_.Run(); }
-
-  void OnExpandAnimationEnded() override {
-    if (quit_on_event == QuitOnEvent::kExpand) {
-      loop_.Quit();
-    }
-  }
-  void OnCollapseAnimationEnded() override {
-    if (quit_on_event == QuitOnEvent::kCollapse) {
-      loop_.Quit();
-    }
-  }
-
-  void OnChipVisibilityChanged(bool is_visible) override {
-    if (quit_on_event == QuitOnEvent::kVisibiltyTrue && is_visible) {
-      loop_.Quit();
-      return;
-    }
-
-    if (quit_on_event == QuitOnEvent::kVisibiltyFalse && !is_visible) {
-      loop_.Quit();
-    }
-  }
-
-  base::ScopedObservation<PermissionChipView, PermissionChipView::Observer>
-      observation_{this};
-  base::RunLoop loop_;
-  QuitOnEvent quit_on_event = QuitOnEvent::kExpand;
-};
-
 class PictureInPictureBrowserFrameViewTest : public WebRtcTestBase,
                                              public AnimationTimingTest {
  public:
@@ -726,83 +679,6 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_P(PictureInPictureBrowserFrameViewTest,
-                       TestMediaBlockedIndicators) {
-  ASSERT_NO_FATAL_FAILURE(
-      SetUpDocumentPIP({}, kPictureInPictureDocumentPipPage));
-
-  content::WebContents* pip_web_contents = pip_frame_view()
-                                               ->browser_view()
-                                               ->browser()
-                                               ->tab_strip_model()
-                                               ->GetActiveWebContents();
-  ASSERT_TRUE(pip_web_contents);
-
-  BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
-  ASSERT_TRUE(browser_view);
-  ASSERT_TRUE(browser_view->GetLocationBarView());
-  PermissionDashboardController* permission_dashboard_controller =
-      browser_view->GetLocationBarView()->permission_dashboard_controller();
-  PermissionDashboardView* permission_dashboard_view =
-      permission_dashboard_controller->permission_dashboard_view();
-
-  ASSERT_TRUE(permission_dashboard_view);
-
-  permissions::PermissionRequestManager::FromWebContents(pip_web_contents)
-      ->set_auto_response_for_test(
-          permissions::PermissionRequestManager::DISMISS);
-
-  // Request microphone permission and wait for the mic indicator to expand.
-  {
-    ChipAnimationObserver chip_animation_observer(
-        permission_dashboard_view->GetIndicatorChip());
-    chip_animation_observer.quit_on_event =
-        ChipAnimationObserver::QuitOnEvent::kExpand;
-
-    constexpr char kRequestMicrophone[] = R"(
-    new Promise(async resolve => {
-      var constraints = { audio: true };
-      window.focus();
-      try {
-        const stream = await navigator.mediaDevices.getUserMedia(constraints);
-        resolve('granted');
-      } catch(error) {
-        resolve('denied')
-      }
-    })
-    )";
-
-    EXPECT_TRUE(content::ExecJs(
-        pip_web_contents->GetPrimaryMainFrame(), kRequestMicrophone,
-        content::EvalJsOptions::EXECUTE_SCRIPT_NO_RESOLVE_PROMISES));
-
-    chip_animation_observer.WaitForChip();
-  }
-
-  // Blocked LHS indicator should be visible.
-  EXPECT_TRUE(permission_dashboard_view->GetVisible());
-  // Blocked media indicator is not supported by PiP window, hence it should not
-  // be shown.
-  EXPECT_FALSE(pip_frame_view()->HasAnyVisibleContentSettingViews());
-
-  // Wait for the LHS indicator to disappear.
-  {
-    ChipAnimationObserver chip_animation_observer(
-        permission_dashboard_view->GetIndicatorChip());
-    chip_animation_observer.quit_on_event =
-        ChipAnimationObserver::QuitOnEvent::kVisibiltyFalse;
-
-    // Wait until chip hides.
-    chip_animation_observer.WaitForChip();
-  }
-
-  // Blocked LHS indicator is hidden.
-  EXPECT_FALSE(permission_dashboard_view->GetVisible());
-  // Blocked media indicator is not supported by PiP window, hence it should not
-  // be shown.
-  EXPECT_FALSE(pip_frame_view()->HasAnyVisibleContentSettingViews());
-}
-
 INSTANTIATE_TEST_SUITE_P(
     AnimationTimingTestSuiteInstantiation,
     PictureInPictureBrowserFrameViewTest,
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view_interactive_uitest.cc b/chrome/browser/ui/views/frame/tab_strip_region_view_interactive_uitest.cc
index 1d13459..957a14f 100644
--- a/chrome/browser/ui/views/frame/tab_strip_region_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/frame/tab_strip_region_view_interactive_uitest.cc
@@ -113,10 +113,18 @@
   EXPECT_TRUE(tab_2->HasFocus());
 
   move_forward_over_tab(tab_2);
-  EXPECT_TRUE(new_tab_button()->HasFocus());
+  if (features::HasTabstripComboButtonWithReverseButtonOrder()) {
+    EXPECT_TRUE(tab_search_button()->HasFocus());
+  } else {
+    EXPECT_TRUE(new_tab_button()->HasFocus());
+  }
 
   press_right();
-  EXPECT_TRUE(tab_search_button()->HasFocus());
+  if (features::HasTabstripComboButtonWithReverseButtonOrder()) {
+    EXPECT_TRUE(new_tab_button()->HasFocus());
+  } else {
+    EXPECT_TRUE(tab_search_button()->HasFocus());
+  }
 
   // Focus should cycle back around to tab_0.
   press_right();
@@ -153,10 +161,18 @@
 
   // Pressing left should immediately cycle back around to the last button.
   press_left();
-  EXPECT_TRUE(tab_search_button()->HasFocus());
+  if (features::HasTabstripComboButtonWithReverseButtonOrder()) {
+    EXPECT_TRUE(new_tab_button()->HasFocus());
+  } else {
+    EXPECT_TRUE(tab_search_button()->HasFocus());
+  }
 
   press_left();
-  EXPECT_TRUE(new_tab_button()->HasFocus());
+  if (features::HasTabstripComboButtonWithReverseButtonOrder()) {
+    EXPECT_TRUE(tab_search_button()->HasFocus());
+  } else {
+    EXPECT_TRUE(new_tab_button()->HasFocus());
+  }
 
   move_back_to_tab(tab_2);
   EXPECT_TRUE(tab_2->HasFocus());
@@ -186,12 +202,20 @@
 #if !BUILDFLAG(IS_WIN)
     EXPECT_TRUE(tab_strip_region_view()->AcceleratorPressed(
         tab_strip_region_view()->end_key()));
-    EXPECT_TRUE(new_tab_button()->HasFocus());
+    if (features::HasTabstripComboButtonWithReverseButtonOrder()) {
+      EXPECT_TRUE(tab_search_button()->HasFocus());
+    } else {
+      EXPECT_TRUE(new_tab_button()->HasFocus());
+    }
 #endif  // !BUILDFLAG(IS_WIN)
 
     EXPECT_TRUE(tab_strip_region_view()->AcceleratorPressed(
         tab_strip_region_view()->home_key()));
-    EXPECT_TRUE(tab_search_button()->HasFocus());
+    if (features::HasTabstripComboButtonWithReverseButtonOrder()) {
+      EXPECT_TRUE(new_tab_button()->HasFocus());
+    } else {
+      EXPECT_TRUE(tab_search_button()->HasFocus());
+    }
 
   } else {
     // The first tab should be active.
@@ -200,7 +224,11 @@
 #if !BUILDFLAG(IS_WIN)
     EXPECT_TRUE(tab_strip_region_view()->AcceleratorPressed(
         tab_strip_region_view()->end_key()));
-    EXPECT_TRUE(tab_search_button()->HasFocus());
+    if (features::HasTabstripComboButtonWithReverseButtonOrder()) {
+      EXPECT_TRUE(new_tab_button()->HasFocus());
+    } else {
+      EXPECT_TRUE(tab_search_button()->HasFocus());
+    }
 #endif  // !BUILDFLAG(IS_WIN)
 
     EXPECT_TRUE(tab_strip_region_view()->AcceleratorPressed(
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_interactive_uitest.cc b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_interactive_uitest.cc
index aad0726e..e8d9005 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_interactive_uitest.cc
+++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_interactive_uitest.cc
@@ -168,36 +168,42 @@
                   views::kEyeCrossedRefreshIcon));
   }
 
-  auto CheckTrackingProtectionAllowedState() {
+  auto CheckTrackingProtectionAllowedState(bool incognito = false,
+                                           bool with_act = false) {
     return Steps(
         CheckViewProperty(CookieControlsContentView::kToggleButton,
-                          &views::ToggleButton::GetIsOn, !has_act_features_),
+                          &views::ToggleButton::GetIsOn, !with_act),
         CheckViewProperty(
             CookieControlsContentView::kTitle, &views::Label::GetText,
-            l10n_util::GetPluralStringFUTF16(
-                browser()->profile()->GetPrefs()->GetBoolean(
-                    prefs::kBlockAll3pcToggleEnabled)
-                    ? IDS_TRACKING_PROTECTION_BUBBLE_BLOCKING_RESTART_TITLE
-                    : IDS_TRACKING_PROTECTION_BUBBLE_LIMITING_RESTART_TITLE,
-                ExceptionDurationInDays())),
+            incognito
+                ? l10n_util::GetStringUTF16(
+                      IDS_TRACKING_PROTECTION_BUBBLE_PERMANENT_ALLOWED_TITLE)
+                : l10n_util::GetPluralStringFUTF16(
+                      browser()->profile()->GetPrefs()->GetBoolean(
+                          prefs::kBlockAll3pcToggleEnabled)
+                          ? IDS_TRACKING_PROTECTION_BUBBLE_BLOCKING_RESTART_TITLE
+                          : IDS_TRACKING_PROTECTION_BUBBLE_LIMITING_RESTART_TITLE,
+                      ExceptionDurationInDays())),
         CheckViewProperty(
             CookieControlsContentView::kDescription, &views::Label::GetText,
             l10n_util::GetStringUTF16(
-                IDS_TRACKING_PROTECTION_BUBBLE_BLOCKING_RESTART_DESCRIPTION)),
+                incognito
+                    ? IDS_TRACKING_PROTECTION_BUBBLE_PERMANENT_ALLOWED_DESCRIPTION
+                    : IDS_TRACKING_PROTECTION_BUBBLE_BLOCKING_RESTART_DESCRIPTION)),
         CheckViewProperty(
-            has_act_features_
-                ? CookieControlsContentView::kThirdPartyCookiesLabel
-                : CookieControlsContentView::kToggleLabel,
+            with_act ? CookieControlsContentView::kThirdPartyCookiesLabel
+                     : CookieControlsContentView::kToggleLabel,
             &views::Label::GetText,
             l10n_util::GetStringUTF16(
                 IDS_TRACKING_PROTECTION_BUBBLE_3PC_ALLOWED_SUBTITLE)),
         CheckIcon(RichControlsContainerView::kIcon, views::kEyeRefreshIcon));
   }
 
-  auto CheckTrackingProtectionBlockedState() {
+  auto CheckTrackingProtectionBlockedState(bool incognito = false,
+                                           bool with_act = false) {
     return Steps(
         CheckViewProperty(CookieControlsContentView::kToggleButton,
-                          &views::ToggleButton::GetIsOn, has_act_features_),
+                          &views::ToggleButton::GetIsOn, with_act),
         CheckViewProperty(
             CookieControlsContentView::kTitle, &views::Label::GetText,
             l10n_util::GetStringUTF16(
@@ -207,13 +213,13 @@
             l10n_util::GetStringUTF16(
                 IDS_TRACKING_PROTECTION_BUBBLE_SITE_NOT_WORKING_DESCRIPTION)),
         CheckViewProperty(
-            has_act_features_
-                ? CookieControlsContentView::kThirdPartyCookiesLabel
-                : CookieControlsContentView::kToggleLabel,
+            with_act ? CookieControlsContentView::kThirdPartyCookiesLabel
+                     : CookieControlsContentView::kToggleLabel,
             &views::Label::GetText,
             l10n_util::GetStringUTF16(
                 browser()->profile()->GetPrefs()->GetBoolean(
-                    prefs::kBlockAll3pcToggleEnabled)
+                    prefs::kBlockAll3pcToggleEnabled) ||
+                        incognito
                     ? IDS_TRACKING_PROTECTION_BUBBLE_3PC_BLOCKED_SUBTITLE
                     : IDS_TRACKING_PROTECTION_BUBBLE_3PC_LIMITED_SUBTITLE)),
         CheckIcon(RichControlsContainerView::kIcon,
@@ -293,7 +299,6 @@
       &CookieControlsInteractiveTestBase::GetReferenceTime,
       /*time_ticks_override=*/nullptr, /*thread_ticks_override=*/nullptr};
 
-  bool has_act_features_ = false;
   base::UserActionTester user_actions_;
   base::test::ScopedFeatureList disabled_features_;
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
@@ -702,25 +707,23 @@
   }
 };
 
-IN_PROC_BROWSER_TEST_P(CookieControlsInteractiveUi3pcdTest, CreateException) {
+IN_PROC_BROWSER_TEST_P(CookieControlsInteractiveUi3pcdTest,
+                       CreateExceptionIncognito) {
   BlockThirdPartyCookies(/*use_3pcd=*/true);
   SetBlockAll3pcToggle(std::get<0>(GetParam()));
-  profile_metrics::SetBrowserProfileType(
-      browser()->profile(), profile_metrics::BrowserProfileType::kIncognito);
-  RunTestSequence(
-      InstrumentTab(kWebContentsElementId),
-      NavigateWebContents(kWebContentsElementId, third_party_cookie_page_url()),
-      PressButton(kCookieControlsIconElementId),
-      InAnyContext(WaitForShow(CookieControlsBubbleView::kContentView)),
-      CheckTrackingProtectionBlockedState(),
-      PressButton(CookieControlsContentView::kToggleButton),
-      CheckFeedbackButtonVisible(testing::get<1>(GetParam())),
-      EnsureNotPresent(CookieControlsBubbleView::kReloadingView),
-      CheckTrackingProtectionAllowedState());
-
-  // Reset browser profile before teardown to avoid profile_destroyer errors.
-  profile_metrics::SetBrowserProfileType(
-      browser()->profile(), profile_metrics::BrowserProfileType::kRegular);
+  auto* const incognito_browser = CreateIncognitoBrowser(browser()->profile());
+  RunTestSequence(InContext(
+      incognito_browser->window()->GetElementContext(),
+      Steps(InstrumentTab(kWebContentsElementId),
+            NavigateWebContents(kWebContentsElementId,
+                                third_party_cookie_page_url()),
+            PressButton(kCookieControlsIconElementId),
+            InAnyContext(WaitForShow(CookieControlsBubbleView::kContentView)),
+            CheckTrackingProtectionBlockedState(/*incognito=*/true),
+            PressButton(CookieControlsContentView::kToggleButton),
+            CheckFeedbackButtonVisible(testing::get<1>(GetParam())),
+            EnsureNotPresent(CookieControlsBubbleView::kReloadingView),
+            CheckTrackingProtectionAllowedState(/*incognito=*/true))));
 }
 
 IN_PROC_BROWSER_TEST_P(CookieControlsInteractiveUi3pcdTest, RemoveException) {
@@ -752,17 +755,11 @@
                      /*show_feedback_button*/ testing::Bool()));
 
 class CookieControlsInteractiveUiTrackingProtectionTest
-    : public CookieControlsInteractiveTestBase,
-      public testing::WithParamInterface<bool> {
+    : public CookieControlsInteractiveTestBase {
  public:
   CookieControlsInteractiveUiTrackingProtectionTest() = default;
   ~CookieControlsInteractiveUiTrackingProtectionTest() override = default;
 
-  privacy_sandbox::TrackingProtectionSettings* tracking_protection_settings() {
-    return TrackingProtectionSettingsFactory::GetForProfile(
-        browser()->profile());
-  }
-
  protected:
   std::vector<base::test::FeatureRef> EnabledFeatures() override {
     return {privacy_sandbox::kActUserBypassUx,
@@ -772,47 +769,22 @@
   std::vector<base::test::FeatureRef> DisabledFeatures() override { return {}; }
 };
 
-IN_PROC_BROWSER_TEST_P(CookieControlsInteractiveUiTrackingProtectionTest,
-                       CreateException) {
+IN_PROC_BROWSER_TEST_F(CookieControlsInteractiveUiTrackingProtectionTest,
+                       CreateAndRemoveExceptionIncognitoAct) {
   BlockThirdPartyCookies(/*use_3pcd=*/true);
-  has_act_features_ = true;
   EnableFpProtection();
-  SetBlockAll3pcToggle(GetParam());
-  RunTestSequence(
-      InstrumentTab(kWebContentsElementId),
-      NavigateWebContents(kWebContentsElementId, third_party_cookie_page_url()),
-      PressButton(kCookieControlsIconElementId),
-      InAnyContext(WaitForShow(CookieControlsBubbleView::kContentView)),
-      CheckTrackingProtectionBlockedState(),
-      PressButton(CookieControlsContentView::kToggleButton),
-      EnsureNotPresent(CookieControlsBubbleView::kReloadingView),
-      CheckTrackingProtectionAllowedState());
+  auto* const incognito_browser = CreateIncognitoBrowser(browser()->profile());
+  RunTestSequence(InContext(
+      incognito_browser->window()->GetElementContext(),
+      Steps(InstrumentTab(kWebContentsElementId),
+            NavigateWebContents(kWebContentsElementId,
+                                third_party_cookie_page_url()),
+            PressButton(kCookieControlsIconElementId),
+            InAnyContext(WaitForShow(CookieControlsBubbleView::kContentView)),
+            CheckTrackingProtectionBlockedState(/*incognito=*/true,
+                                                /*with_act=*/true),
+            PressButton(CookieControlsContentView::kToggleButton),
+            EnsureNotPresent(CookieControlsBubbleView::kReloadingView),
+            CheckTrackingProtectionAllowedState(/*incognito=*/true,
+                                                /*with_act=*/true))));
 }
-
-IN_PROC_BROWSER_TEST_P(CookieControlsInteractiveUiTrackingProtectionTest,
-                       RemoveException) {
-  // Open the bubble while 3PC are blocked, but the page already has an
-  // exception. Disable 3PC for the page, and confirm the exception is removed.
-  BlockThirdPartyCookies(/*use_3pcd=*/true);
-  has_act_features_ = true;
-  EnableFpProtection();
-  SetBlockAll3pcToggle(GetParam());
-  cookie_settings()->SetCookieSettingForUserBypass(
-      third_party_cookie_page_url());
-  tracking_protection_settings()->AddTrackingProtectionException(
-      third_party_cookie_page_url());
-  RunTestSequence(
-      InstrumentTab(kWebContentsElementId),
-      NavigateWebContents(kWebContentsElementId, third_party_cookie_page_url()),
-      PressButton(kCookieControlsIconElementId),
-      InAnyContext(WaitForShow(CookieControlsContentView::kToggleButton)),
-      CheckTrackingProtectionAllowedState(),
-      PressButton(CookieControlsContentView::kToggleButton),
-      CheckViewProperty(kCookieControlsIconElementId,
-                        &CookieControlsIconView::is_animating_label, false),
-      CheckTrackingProtectionBlockedState());
-}
-
-INSTANTIATE_TEST_SUITE_P(All,
-                         CookieControlsInteractiveUiTrackingProtectionTest,
-                         (/*block_all_third_party_cookies*/ testing::Bool()));
diff --git a/chrome/browser/ui/views/media_picker_utils.cc b/chrome/browser/ui/views/media_picker_utils.cc
index 285b366..b94e768 100644
--- a/chrome/browser/ui/views/media_picker_utils.cc
+++ b/chrome/browser/ui/views/media_picker_utils.cc
@@ -10,7 +10,6 @@
 #include "components/constrained_window/constrained_window_views.h"
 #include "content/public/browser/web_contents.h"
 #include "ui/views/widget/widget.h"
-#include "ui/views/widget/widget_delegate.h"
 #include "ui/views/window/dialog_delegate.h"
 
 bool IsMediaPickerModalWindow(content::WebContents* web_contents) {
@@ -20,7 +19,7 @@
 
 views::Widget* CreateMediaPickerDialogWidget(Browser* browser,
                                              content::WebContents* web_contents,
-                                             views::WidgetDelegate* delegate,
+                                             views::DialogDelegate* delegate,
                                              gfx::NativeWindow context,
                                              gfx::NativeView parent) {
   // If |web_contents| is not a background page then the picker will be shown
@@ -36,11 +35,12 @@
     widget =
         constrained_window::ShowWebModalDialogViews(delegate, web_contents);
   } else {
-#if BUILDFLAG(IS_MAC)
-    // On Mac, ModalType::kChild with a null parent isn't allowed - fall back to
-    // ModalType::kWindow.
-    delegate->SetModalType(ui::mojom::ModalType::kWindow);
-#endif
+    // This becomes a top-level window if it does not have a parent.
+    // Top-level windows should usually be draggable.
+    if (!parent) {
+      delegate->set_draggable(true);
+      delegate->SetModalType(ui::mojom::ModalType::kNone);
+    }
     widget =
         views::DialogDelegate::CreateDialogWidget(delegate, context, parent);
     widget->Show();
diff --git a/chrome/browser/ui/views/media_picker_utils.h b/chrome/browser/ui/views/media_picker_utils.h
index 9e4dae387..a5925496 100644
--- a/chrome/browser/ui/views/media_picker_utils.h
+++ b/chrome/browser/ui/views/media_picker_utils.h
@@ -12,8 +12,8 @@
 }
 
 namespace views {
+class DialogDelegate;
 class Widget;
-class WidgetDelegate;
 }  // namespace views
 
 class Browser;
@@ -26,7 +26,7 @@
 // window.
 views::Widget* CreateMediaPickerDialogWidget(Browser* browser,
                                              content::WebContents* web_contents,
-                                             views::WidgetDelegate* delegate,
+                                             views::DialogDelegate* delegate,
                                              gfx::NativeWindow context,
                                              gfx::NativeView parent);
 
diff --git a/chrome/browser/ui/views/page_action/OWNERS b/chrome/browser/ui/views/page_action/OWNERS
index c54b1d7..35987916 100644
--- a/chrome/browser/ui/views/page_action/OWNERS
+++ b/chrome/browser/ui/views/page_action/OWNERS
@@ -1 +1,2 @@
 file://chrome/browser/ui/page_action/OWNERS
+alsan@chromium.org
diff --git a/chrome/browser/ui/views/performance_controls/memory_saver_iph_interactive_uitest.cc b/chrome/browser/ui/views/performance_controls/memory_saver_iph_interactive_uitest.cc
index 42a416a..262fd54 100644
--- a/chrome/browser/ui/views/performance_controls/memory_saver_iph_interactive_uitest.cc
+++ b/chrome/browser/ui/views/performance_controls/memory_saver_iph_interactive_uitest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/strings/stringprintf.h"
+#include "base/test/scoped_amount_of_physical_memory_override.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/performance_manager/public/user_tuning/user_performance_tuning_manager.h"
@@ -62,6 +63,12 @@
     AddDescriptionPrefix(steps, "TriggerMemorySaverPromo()");
     return steps;
   }
+
+ private:
+  // Pretend to have 1GB of memory, to ensure the memory saver promo will
+  // show.
+  base::test::ScopedAmountOfPhysicalMemoryOverride
+      scoped_amount_of_physical_memory_override_{1024};
 };
 
 // Check that the memory saver mode in-product help promo is shown when
diff --git a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
index 731be8f..1ab5ed0 100644
--- a/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
+++ b/chrome/browser/ui/views/status_icons/status_icon_linux_dbus.cc
@@ -216,16 +216,12 @@
 }  // namespace
 
 StatusIconLinuxDbus::StatusIconLinuxDbus()
-    : should_write_icon_to_file_(ShouldWriteIconToFile()),
+    : bus_(dbus_thread_linux::GetSharedSessionBus()),
+      should_write_icon_to_file_(ShouldWriteIconToFile()),
       icon_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
           {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
            base::TaskShutdownBehavior::BLOCK_SHUTDOWN})) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  dbus::Bus::Options bus_options;
-  bus_options.bus_type = dbus::Bus::SESSION;
-  bus_options.connection_type = dbus::Bus::PRIVATE;
-  bus_options.dbus_task_runner = dbus_thread_linux::GetTaskRunner();
-  bus_ = base::MakeRefCounted<dbus::Bus>(bus_options);
   CheckStatusNotifierWatcherHasOwner();
 }
 
@@ -297,8 +293,6 @@
 
 StatusIconLinuxDbus::~StatusIconLinuxDbus() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  bus_->GetDBusTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, bus_));
   CleanupIconFile();
 }
 
diff --git a/chrome/browser/ui/views/tabs/tab_strip_action_container.cc b/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
index 77e0df5..e8b4a83 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
@@ -409,7 +409,12 @@
 void TabStripActionContainer::OnTriggerGlicNudgeUI(std::string label) {
   CHECK(glic_nudge_button_);
   glic_nudge_button_->SetText(base::UTF8ToUTF16(label));
-  ShowTabStripNudge(glic_nudge_button_);
+
+  if (!label.empty()) {
+    ShowTabStripNudge(glic_nudge_button_);
+  } else {
+    HideTabStripNudge(glic_nudge_button_);
+  }
 }
 
 DeclutterTriggerCTRBucket TabStripActionContainer::GetDeclutterTriggerBucket(
diff --git a/chrome/browser/ui/views/tabs/tab_strip_action_container_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_action_container_unittest.cc
index 079f9ad7..fcd222e 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_action_container_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_action_container_unittest.cc
@@ -9,6 +9,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h"
+#include "chrome/browser/ui/tabs/test/mock_tab_interface.h"
 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
 #include "chrome/browser/ui/views/commerce/product_specifications_button.h"
 #include "chrome/browser/ui/views/frame/tab_strip_region_view.h"
@@ -85,6 +86,7 @@
   void TearDown() override {
     ChromeViewsTestBase::TearDown();
     tab_strip_action_container_.reset();
+    glic_nudge_controller_.reset();
   }
 
   void BuildGlicContainer(bool use_otr_profile) {
@@ -97,17 +99,24 @@
     tab_strip_model_ = std::make_unique<TabStripModel>(
         &tab_strip_model_delegate_, tab_strip_->controller()->GetProfile());
 
+    tab_interface_ = std::make_unique<tabs::MockTabInterface>();
+
     browser_window_interface_ = std::make_unique<MockBrowserWindowInterface>();
     ON_CALL(*browser_window_interface_, GetTabStripModel)
         .WillByDefault(::testing::Return(tab_strip_model_.get()));
     ON_CALL(*browser_window_interface_, GetProfile)
         .WillByDefault(
             ::testing::Return(tab_strip_->controller()->GetProfile()));
+    ON_CALL(*browser_window_interface_, GetActiveTabInterface)
+        .WillByDefault(::testing::Return(tab_interface_.get()));
+    ON_CALL(*tab_interface_, GetContents)
+        .WillByDefault(::testing::Return(web_contents_.get()));
 
     tab_declutter_controller_ = std::make_unique<tabs::TabDeclutterController>(
         browser_window_interface_.get());
 
-    glic_nudge_controller_ = std::make_unique<tabs::GlicNudgeController>();
+    glic_nudge_controller_ = std::make_unique<tabs::GlicNudgeController>(
+        browser_window_interface_.get());
 
     locked_expansion_view_ = std::make_unique<views::View>();
 
@@ -121,6 +130,7 @@
   std::unique_ptr<TabStripModel> tab_strip_model_;
   std::unique_ptr<tabs::TabDeclutterController> tab_declutter_controller_;
   std::unique_ptr<tabs::GlicNudgeController> glic_nudge_controller_;
+  std::unique_ptr<tabs::MockTabInterface> tab_interface_;
   std::unique_ptr<MockBrowserWindowInterface> browser_window_interface_;
   TestTabStripModelDelegate tab_strip_model_delegate_;
   base::test::ScopedFeatureList scoped_feature_list_;
diff --git a/chrome/browser/ui/views/tabs/tab_strip_combo_button.cc b/chrome/browser/ui/views/tabs/tab_strip_combo_button.cc
index 7414003..613a861 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_combo_button.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_combo_button.cc
@@ -49,7 +49,13 @@
                                          TabStrip* tab_strip) {
   Edge new_tab_button_flat_edge = Edge::kNone;
   if (features::HasTabstripComboButtonWithBackground()) {
-    new_tab_button_flat_edge = base::i18n::IsRTL() ? Edge::kLeft : Edge::kRight;
+    if (features::HasTabstripComboButtonWithReverseButtonOrder()) {
+      new_tab_button_flat_edge =
+          base::i18n::IsRTL() ? Edge::kRight : Edge::kLeft;
+    } else {
+      new_tab_button_flat_edge =
+          base::i18n::IsRTL() ? Edge::kLeft : Edge::kRight;
+    }
   }
   std::unique_ptr<TabStripControlButton> new_tab_button =
       std::make_unique<TabStripControlButton>(
@@ -70,9 +76,14 @@
     new_tab_button->SetBackgroundFrameInactiveColorId(
         kColorNewTabButtonCRBackgroundFrameInactive);
   } else {
-    // Add a gap between the new tab button and tab search container.
-    new_tab_button->SetProperty(
-        views::kMarginsKey, gfx::Insets::TLBR(0, 0, 0, kButtonGapNoBackground));
+    // Add a gap between the new tab button and tab search button.
+    gfx::Insets button_margins;
+    if (features::HasTabstripComboButtonWithReverseButtonOrder()) {
+      button_margins = gfx::Insets::TLBR(0, kButtonGapNoBackground, 0, 0);
+    } else {
+      button_margins = gfx::Insets::TLBR(0, 0, 0, kButtonGapNoBackground);
+    }
+    new_tab_button->SetProperty(views::kMarginsKey, button_margins);
   }
 
   new_tab_button->SetTooltipText(
@@ -105,8 +116,13 @@
 
   Edge tab_search_button_flat_edge = Edge::kNone;
   if (features::HasTabstripComboButtonWithBackground()) {
-    tab_search_button_flat_edge =
-        base::i18n::IsRTL() ? Edge::kRight : Edge::kLeft;
+    if (features::HasTabstripComboButtonWithReverseButtonOrder()) {
+      tab_search_button_flat_edge =
+          base::i18n::IsRTL() ? Edge::kLeft : Edge::kRight;
+    } else {
+      tab_search_button_flat_edge =
+          base::i18n::IsRTL() ? Edge::kRight : Edge::kLeft;
+    }
   }
   std::unique_ptr<TabSearchButton> tab_search_button =
       std::make_unique<TabSearchButton>(tab_strip->controller(), browser,
@@ -128,9 +144,15 @@
       .SetCrossAxisAlignment(views::LayoutAlignment::kCenter);
   separator_container->SetCanProcessEventsWithinSubtree(false);
 
-  new_tab_button_ = button_container->AddChildView(std::move(new_tab_button));
-  tab_search_button_ =
-      button_container->AddChildView(std::move(tab_search_button));
+  if (features::HasTabstripComboButtonWithReverseButtonOrder()) {
+    tab_search_button_ =
+        button_container->AddChildView(std::move(tab_search_button));
+    new_tab_button_ = button_container->AddChildView(std::move(new_tab_button));
+  } else {
+    new_tab_button_ = button_container->AddChildView(std::move(new_tab_button));
+    tab_search_button_ =
+        button_container->AddChildView(std::move(tab_search_button));
+  }
   separator_ = separator_container->AddChildView(std::move(separator));
 
   SetLayoutManager(std::make_unique<views::FillLayout>());
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler_unittest.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler_unittest.cc
index 1327767..fd2bb7f 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler_unittest.cc
@@ -618,19 +618,23 @@
   auto web_app_info = web_app::WebAppInstallInfo::CreateWithStartUrlForTesting(
       GURL("https://example.com/"));
   web_app_info->title = u"app_name";
-  web_app_info->scope_extensions = web_app::ScopeExtensions(
-      {web_app::ScopeExtensionInfo::CreateForScope(GURL("https://sitea.com")),
-       web_app::ScopeExtensionInfo::CreateForScope(
-           GURL("https://app.siteb.com")),
-       web_app::ScopeExtensionInfo::CreateForScope(
-           GURL("https://sitec.com"),
-           /*has_origin_wildcard=*/true),
-       web_app::ScopeExtensionInfo::CreateForScope(
-           GURL("http://☃.net/")) /* Unicode */,
-       web_app::ScopeExtensionInfo::CreateForScope(
-           GURL("https://localhost:443")),
-       web_app::ScopeExtensionInfo::CreateForScope(
-           GURL("https://localhost:9999"))});
+  web_app_info->scope_extensions = web_app::ScopeExtensions({
+      web_app::ScopeExtensionInfo::CreateForScope(GURL("https://sitea.com")),
+      web_app::ScopeExtensionInfo::CreateForScope(
+          GURL("https://app.siteb.com")),
+      web_app::ScopeExtensionInfo::CreateForScope(GURL("https://sitec.com"),
+                                                  /*has_origin_wildcard=*/true),
+      web_app::ScopeExtensionInfo::CreateForScope(
+          GURL("http://☃.net/")) /* Unicode */,
+      web_app::ScopeExtensionInfo::CreateForScope(
+          GURL("https://localhost:443")),
+      web_app::ScopeExtensionInfo::CreateForScope(
+          GURL("https://localhost:9999")),
+      web_app::ScopeExtensionInfo::CreateForScope(
+          GURL("https://google.com/search?q=search+query")),
+      web_app::ScopeExtensionInfo::CreateForScope(
+          GURL("https://google.com/search?q=search+query#fragment")),
+  });
 
   web_app::WebAppInstallParams install_params;
   // Skip origin association validation for testing.
@@ -655,6 +659,7 @@
   std::vector<std::string> expected_scope_extensions = {
       "xn--n3h.net" /* Unicode */,
       "app.siteb.com",
+      "google.com",
       "localhost",
       "sitea.com",
       "*.sitec.com",
diff --git a/chrome/browser/ui/webui/commerce/product_specifications_ui.cc b/chrome/browser/ui/webui/commerce/product_specifications_ui.cc
index 8f81c04..296788b 100644
--- a/chrome/browser/ui/webui/commerce/product_specifications_ui.cc
+++ b/chrome/browser/ui/webui/commerce/product_specifications_ui.cc
@@ -104,7 +104,10 @@
       {"learnMoreA11yLabel", IDS_COMPARE_LEARN_MORE_A11Y_LABEL},
       {"menuDelete", IDS_COMPARE_CONTEXT_MENU_DELETE},
       {"menuOpenAll", IDS_COMPARE_CONTEXT_MENU_OPEN_ALL_WITH_COUNT},
+      {"menuOpenAllInNewWindow",
+       IDS_COMPARE_CONTEXT_MENU_OPEN_ALL_IN_NEW_WINDOW_WITH_COUNT},
       {"menuOpenInNewTab", IDS_COMPARE_CONTEXT_MENU_OPEN_IN_NEW_TAB},
+      {"menuOpenInNewWindow", IDS_COMPARE_CONTEXT_MENU_OPEN_IN_NEW_WINDOW},
       {"menuRename", IDS_COMPARE_CONTEXT_MENU_RENAME},
       {"menuTooltipMore", IDS_COMPARE_EDIT_MORE},
       {"numSelected", IDS_COMPARE_NUM_ITEMS_SELECTED},
diff --git a/chrome/browser/ui/webui/commerce/product_specifications_ui_handler_delegate.cc b/chrome/browser/ui/webui/commerce/product_specifications_ui_handler_delegate.cc
index e1a6084..4a2a0c2 100644
--- a/chrome/browser/ui/webui/commerce/product_specifications_ui_handler_delegate.cc
+++ b/chrome/browser/ui/webui/commerce/product_specifications_ui_handler_delegate.cc
@@ -42,26 +42,67 @@
 void ProductSpecificationsUIHandlerDelegate::
     ShowProductSpecificationsSetForUuid(const base::Uuid& uuid,
                                         bool in_new_tab) {
-  const GURL product_spec_url = commerce::GetProductSpecsTabUrlForID(uuid);
   auto* browser =
       chrome::FindLastActiveWithProfile(Profile::FromWebUI(web_ui_));
   if (!browser) {
     return;
   }
+
   if (in_new_tab) {
-    content::OpenURLParams params(product_spec_url, content::Referrer(),
-                                  WindowOpenDisposition::NEW_FOREGROUND_TAB,
-                                  ui::PAGE_TRANSITION_LINK, false);
-    browser->OpenURL(params, /*navigation_handle_callback=*/{});
-  } else {
-    content::WebContents* web_contents =
-        browser->tab_strip_model()->GetActiveWebContents();
-    if (!web_contents) {
-      return;
+    OpenProductSpecificationsSetForUuidInBrowser(
+        uuid, *browser, WindowOpenDisposition::NEW_FOREGROUND_TAB);
+    return;
+  }
+
+  const GURL product_spec_url = commerce::GetProductSpecsTabUrlForID(uuid);
+  content::WebContents* web_contents =
+      browser->tab_strip_model()->GetActiveWebContents();
+  if (!web_contents) {
+    return;
+  }
+  web_contents->GetController().LoadURL(product_spec_url, content::Referrer(),
+                                        ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
+                                        /*extra_headers=*/std::string());
+}
+
+void ProductSpecificationsUIHandlerDelegate::
+    ShowProductSpecificationsSetsForUuids(
+        const std::vector<base::Uuid>& uuids,
+        const product_specifications::mojom::ShowSetDisposition disposition) {
+  if (uuids.empty()) {
+    return;
+  }
+
+  auto* browser =
+      chrome::FindLastActiveWithProfile(Profile::FromWebUI(web_ui_));
+  if (!browser) {
+    return;
+  }
+
+  if (disposition ==
+      product_specifications::mojom::ShowSetDisposition::kInNewTabs) {
+    for (const auto& uuid : uuids) {
+      OpenProductSpecificationsSetForUuidInBrowser(
+          uuid, *browser, WindowOpenDisposition::NEW_BACKGROUND_TAB);
     }
-    web_contents->GetController().LoadURL(product_spec_url, content::Referrer(),
-                                          ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
-                                          /*extra_headers=*/std::string());
+  }
+
+  if (disposition ==
+      product_specifications::mojom::ShowSetDisposition::kInNewWindow) {
+    auto tab_disposition = WindowOpenDisposition::NEW_WINDOW;
+    for (const auto& uuid : uuids) {
+      const auto* web_contents = OpenProductSpecificationsSetForUuidInBrowser(
+          uuid, *browser, tab_disposition);
+
+      // Open the rest of the tabs in the same window.
+      browser = chrome::FindBrowserWithTab(web_contents);
+      if (!browser) {
+        LOG(ERROR) << "Failed to open product specifications sets in the same "
+                      "new window.";
+        return;
+      }
+      tab_disposition = WindowOpenDisposition::NEW_BACKGROUND_TAB;
+    }
   }
 }
 
@@ -99,4 +140,15 @@
       signin_metrics::AccessPoint::ACCESS_POINT_PRODUCT_SPECIFICATIONS);
 }
 
+content::WebContents* ProductSpecificationsUIHandlerDelegate::
+    OpenProductSpecificationsSetForUuidInBrowser(
+        const base::Uuid& uuid,
+        Browser& browser,
+        const WindowOpenDisposition& disposition) {
+  const GURL product_spec_url = commerce::GetProductSpecsTabUrlForID(uuid);
+  content::OpenURLParams params(product_spec_url, content::Referrer(),
+                                disposition, ui::PAGE_TRANSITION_LINK, false);
+  return browser.OpenURL(params, /*navigation_handle_callback=*/{});
+}
+
 }  // namespace commerce
diff --git a/chrome/browser/ui/webui/commerce/product_specifications_ui_handler_delegate.h b/chrome/browser/ui/webui/commerce/product_specifications_ui_handler_delegate.h
index 98a276e..63b5292 100644
--- a/chrome/browser/ui/webui/commerce/product_specifications_ui_handler_delegate.h
+++ b/chrome/browser/ui/webui/commerce/product_specifications_ui_handler_delegate.h
@@ -7,6 +7,9 @@
 
 #include "components/commerce/core/webui/product_specifications_handler.h"
 #include "content/public/browser/web_ui.h"
+#include "ui/base/window_open_disposition.h"
+
+class Browser;
 
 namespace commerce {
 
@@ -29,9 +32,19 @@
   void ShowProductSpecificationsSetForUuid(const base::Uuid& uuid,
                                            bool in_new_tab) override;
 
+  void ShowProductSpecificationsSetsForUuids(
+      const std::vector<base::Uuid>& uuids,
+      const product_specifications::mojom::ShowSetDisposition disposition)
+      override;
+
   void ShowComparePage(bool in_new_tab) override;
 
  private:
+  content::WebContents* OpenProductSpecificationsSetForUuidInBrowser(
+      const base::Uuid& uuid,
+      Browser& browser,
+      const WindowOpenDisposition& disposition);
+
   raw_ptr<content::WebUI> web_ui_;
 };
 
diff --git a/chrome/browser/ui/webui/commerce/product_specifications_ui_handler_delegate_browsertest.cc b/chrome/browser/ui/webui/commerce/product_specifications_ui_handler_delegate_browsertest.cc
index ce5f9b7..004b83a 100644
--- a/chrome/browser/ui/webui/commerce/product_specifications_ui_handler_delegate_browsertest.cc
+++ b/chrome/browser/ui/webui/commerce/product_specifications_ui_handler_delegate_browsertest.cc
@@ -8,6 +8,8 @@
 #include "base/uuid.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/webui/commerce/product_specifications_disclosure_dialog.h"
 #include "chrome/test/base/chrome_test_utils.h"
 #include "chrome/test/base/in_process_browser_test.h"
@@ -18,8 +20,38 @@
 #include "content/public/test/test_navigation_observer.h"
 #include "content/public/test/test_web_ui.h"
 
+namespace commerce {
 namespace {
+
 const char kExampleUrl[] = "http://example.com/";
+
+class NewBrowserObserver : public BrowserListObserver {
+ public:
+  NewBrowserObserver() { BrowserList::AddObserver(this); }
+  NewBrowserObserver(const NewBrowserObserver&) = delete;
+  NewBrowserObserver& operator=(const NewBrowserObserver&) = delete;
+  ~NewBrowserObserver() override { BrowserList::RemoveObserver(this); }
+
+  void Wait() {
+    if (browsers_.size() == 0) {
+      run_loop_.Run();
+    }
+  }
+
+  std::vector<Browser*>& GetBrowsers() { return browsers_; }
+
+  // BrowserListObserver:
+  void OnBrowserAdded(Browser* browser) override {
+    LOG(ERROR) << "browser added";
+    browsers_.push_back(browser);
+    run_loop_.Quit();
+  }
+
+ private:
+  std::vector<Browser*> browsers_;
+  base::RunLoop run_loop_;
+};
+
 }  // namespace
 
 class ProductSpecificationsUIHandlerDelegateBrowserTest
@@ -45,28 +77,27 @@
   std::string name = "test_name";
   std::string id = "test_id";
   auto delegate =
-      std::make_unique<commerce::ProductSpecificationsUIHandlerDelegate>(
-          web_ui_.get());
+      std::make_unique<ProductSpecificationsUIHandlerDelegate>(web_ui_.get());
   delegate->ShowDisclosureDialog(urls, name, id);
 
-  auto* dialog = commerce::ProductSpecificationsDisclosureDialog::
-      current_instance_for_testing();
+  auto* dialog =
+      ProductSpecificationsDisclosureDialog::current_instance_for_testing();
   ASSERT_TRUE(dialog);
 
   // Check dialog args.
   auto dict = base::JSONReader::ReadDict(dialog->GetDialogArgs());
   ASSERT_TRUE(dict.has_value());
-  auto* set_name = dict->FindString(commerce::kDialogArgsName);
+  auto* set_name = dict->FindString(kDialogArgsName);
   ASSERT_TRUE(set_name);
   ASSERT_EQ(name, *set_name);
-  auto* url_list = dict->FindList(commerce::kDialogArgsUrls);
+  auto* url_list = dict->FindList(kDialogArgsUrls);
   ASSERT_TRUE(url_list);
   ASSERT_EQ(1u, url_list->size());
   ASSERT_EQ(urls[0].spec(), (*url_list)[0].GetString());
-  auto* set_id = dict->FindString(commerce::kDialogArgsSetId);
+  auto* set_id = dict->FindString(kDialogArgsSetId);
   ASSERT_TRUE(set_id);
   ASSERT_EQ(id, *set_id);
-  auto new_tab = dict->FindBool(commerce::kDialogArgsInNewTab);
+  auto new_tab = dict->FindBool(kDialogArgsInNewTab);
   ASSERT_TRUE(new_tab.has_value());
   ASSERT_FALSE(new_tab.value());
 }
@@ -75,47 +106,43 @@
                        TestShowProductSpecificationsSetForUuid) {
   const base::Uuid uuid = base::Uuid::GenerateRandomV4();
   auto delegate =
-      std::make_unique<commerce::ProductSpecificationsUIHandlerDelegate>(
-          web_ui_.get());
+      std::make_unique<ProductSpecificationsUIHandlerDelegate>(web_ui_.get());
   ASSERT_EQ(1, browser()->tab_strip_model()->count());
 
   content::TestNavigationObserver observer_one(
-      commerce::GetProductSpecsTabUrlForID(uuid));
+      GetProductSpecsTabUrlForID(uuid));
   observer_one.WatchExistingWebContents();
 
   delegate->ShowProductSpecificationsSetForUuid(uuid, false);
 
   observer_one.Wait();
   ASSERT_EQ(1, browser()->tab_strip_model()->count());
-  ASSERT_EQ(commerce::GetProductSpecsTabUrlForID(uuid),
-            browser()
-                ->tab_strip_model()
-                ->GetActiveWebContents()
-                ->GetLastCommittedURL());
+  ASSERT_EQ(GetProductSpecsTabUrlForID(uuid), browser()
+                                                  ->tab_strip_model()
+                                                  ->GetActiveWebContents()
+                                                  ->GetLastCommittedURL());
 
   content::TestNavigationObserver observer_two(
-      commerce::GetProductSpecsTabUrlForID(uuid));
+      GetProductSpecsTabUrlForID(uuid));
   observer_two.StartWatchingNewWebContents();
 
   delegate->ShowProductSpecificationsSetForUuid(uuid, true);
 
   observer_two.WaitForNavigationFinished();
   ASSERT_EQ(2, browser()->tab_strip_model()->count());
-  ASSERT_EQ(commerce::GetProductSpecsTabUrlForID(uuid),
-            browser()
-                ->tab_strip_model()
-                ->GetActiveWebContents()
-                ->GetLastCommittedURL());
+  ASSERT_EQ(GetProductSpecsTabUrlForID(uuid), browser()
+                                                  ->tab_strip_model()
+                                                  ->GetActiveWebContents()
+                                                  ->GetLastCommittedURL());
 }
 
 IN_PROC_BROWSER_TEST_F(ProductSpecificationsUIHandlerDelegateBrowserTest,
                        TestShowComparePage_InCurrentTab) {
   auto delegate =
-      std::make_unique<commerce::ProductSpecificationsUIHandlerDelegate>(
-          web_ui_.get());
+      std::make_unique<ProductSpecificationsUIHandlerDelegate>(web_ui_.get());
   ASSERT_EQ(1, browser()->tab_strip_model()->count());
 
-  const auto compare_url = GURL(commerce::kChromeUICompareUrl);
+  const auto compare_url = GURL(kChromeUICompareUrl);
   content::TestNavigationObserver observer(compare_url);
   observer.WatchExistingWebContents();
 
@@ -132,10 +159,9 @@
 IN_PROC_BROWSER_TEST_F(ProductSpecificationsUIHandlerDelegateBrowserTest,
                        TestShowComparePage_InNewTab) {
   auto delegate =
-      std::make_unique<commerce::ProductSpecificationsUIHandlerDelegate>(
-          web_ui_.get());
+      std::make_unique<ProductSpecificationsUIHandlerDelegate>(web_ui_.get());
 
-  const auto compare_url = GURL(commerce::kChromeUICompareUrl);
+  const auto compare_url = GURL(kChromeUICompareUrl);
   content::TestNavigationObserver observer(compare_url);
   observer.StartWatchingNewWebContents();
 
@@ -148,3 +174,78 @@
                              ->GetActiveWebContents()
                              ->GetLastCommittedURL());
 }
+
+IN_PROC_BROWSER_TEST_F(ProductSpecificationsUIHandlerDelegateBrowserTest,
+                       TestShowProductSpecificationSetsForUuids_InNewTabs) {
+  const base::Uuid uuid_one = base::Uuid::GenerateRandomV4();
+  const base::Uuid uuid_two = base::Uuid::GenerateRandomV4();
+  auto delegate =
+      std::make_unique<ProductSpecificationsUIHandlerDelegate>(web_ui_.get());
+  ASSERT_EQ(1, browser()->tab_strip_model()->count());
+
+  content::TestNavigationObserver observer_one(
+      GetProductSpecsTabUrlForID(uuid_one));
+  observer_one.StartWatchingNewWebContents();
+  content::TestNavigationObserver observer_two(
+      GetProductSpecsTabUrlForID(uuid_two));
+  observer_two.StartWatchingNewWebContents();
+
+  delegate->ShowProductSpecificationsSetsForUuids(
+      {uuid_one, uuid_two},
+      product_specifications::mojom::ShowSetDisposition::kInNewTabs);
+
+  observer_one.Wait();
+  observer_two.Wait();
+
+  // First tab is the originally open tab.
+  ASSERT_EQ(3, browser()->tab_strip_model()->count());
+  ASSERT_EQ(
+      GetProductSpecsTabUrlForID(uuid_one),
+      browser()->tab_strip_model()->GetWebContentsAt(1)->GetLastCommittedURL());
+  ASSERT_EQ(
+      GetProductSpecsTabUrlForID(uuid_two),
+      browser()->tab_strip_model()->GetWebContentsAt(2)->GetLastCommittedURL());
+}
+
+IN_PROC_BROWSER_TEST_F(ProductSpecificationsUIHandlerDelegateBrowserTest,
+                       TestShowProductSpecificationSetsForUuids_InNewWindow) {
+  const base::Uuid uuid_one = base::Uuid::GenerateRandomV4();
+  const base::Uuid uuid_two = base::Uuid::GenerateRandomV4();
+  auto delegate =
+      std::make_unique<ProductSpecificationsUIHandlerDelegate>(web_ui_.get());
+  ASSERT_EQ(1, browser()->tab_strip_model()->count());
+
+  NewBrowserObserver browser_observer;
+
+  content::TestNavigationObserver observer_one(
+      GetProductSpecsTabUrlForID(uuid_one));
+  observer_one.StartWatchingNewWebContents();
+  content::TestNavigationObserver observer_two(
+      GetProductSpecsTabUrlForID(uuid_two));
+  observer_two.StartWatchingNewWebContents();
+
+  delegate->ShowProductSpecificationsSetsForUuids(
+      {uuid_one, uuid_two},
+      product_specifications::mojom::ShowSetDisposition::kInNewWindow);
+
+  observer_one.Wait();
+  observer_two.Wait();
+  browser_observer.Wait();
+
+  // The original browser window should still only have one tab.
+  ASSERT_EQ(1, browser()->tab_strip_model()->count());
+
+  // Only one new browser should have been opened with the new tabs.
+  const auto& browsers = browser_observer.GetBrowsers();
+  ASSERT_EQ(1UL, browsers.size());
+  const auto* new_browser = browsers.at(0);
+  ASSERT_EQ(2, new_browser->tab_strip_model()->count());
+  ASSERT_EQ(GetProductSpecsTabUrlForID(uuid_one), new_browser->tab_strip_model()
+                                                      ->GetWebContentsAt(0)
+                                                      ->GetLastCommittedURL());
+  ASSERT_EQ(GetProductSpecsTabUrlForID(uuid_two), new_browser->tab_strip_model()
+                                                      ->GetWebContentsAt(1)
+                                                      ->GetLastCommittedURL());
+}
+
+}  // namespace commerce
diff --git a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
index fb55218..d09161f7 100644
--- a/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
+++ b/chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.cc
@@ -29,6 +29,7 @@
 #include "chrome/grit/nearby_share_dialog_resources_map.h"
 #include "chrome/grit/theme_resources.h"
 #include "chromeos/components/sharesheet/constants.h"
+#include "chromeos/constants/chromeos_features.h"
 #include "content/public/browser/url_data_source.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_ui.h"
@@ -122,6 +123,9 @@
 
   const GURL& url = web_ui->GetWebContents()->GetVisibleURL();
   SetAttachmentFromQueryParameter(url);
+
+  html_source->AddBoolean("isQuickShareV2Enabled",
+                          chromeos::features::IsQuickShareV2Enabled());
 }
 
 NearbyShareDialogUI::~NearbyShareDialogUI() = default;
diff --git a/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc b/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc
index 2f5b125..512b72d6 100644
--- a/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc
+++ b/chrome/browser/ui/webui/new_tab_page/untrusted_source.cc
@@ -16,6 +16,7 @@
 #include "base/memory/ref_counted_memory.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/metrics/histogram_macros.h"
+#include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/task_traits.h"
@@ -181,7 +182,7 @@
         IDR_NEW_TAB_PAGE_UNTRUSTED_BACKGROUND_IMAGE_JS));
     return;
   }
-  if (base::Contains(path, "background.jpg")) {
+  if (base::EndsWith(path, "background.jpg")) {
     base::ThreadPool::PostTaskAndReplyWithResult(
         FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
         base::BindOnce(&ReadBackgroundImageData,
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_uninstall_browsertest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_uninstall_browsertest.cc
index 7832c2c..033b96d8 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_uninstall_browsertest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_uninstall_browsertest.cc
@@ -47,31 +47,8 @@
 
   void SetUp() override {
     ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
-
     src_bundle_path_ = scoped_temp_dir_.GetPath().Append(
         base::FilePath::FromASCII("bundle.swbn"));
-    switch (mode_and_file_op_) {
-      case IwaSourceBundleModeAndFileOp::kDevModeCopy:
-        src_source_ = IsolatedWebAppInstallSource::FromDevUi(
-            IwaSourceBundleDevModeWithFileOp(src_bundle_path_,
-                                             IwaSourceBundleDevFileOp::kCopy));
-        break;
-      case IwaSourceBundleModeAndFileOp::kDevModeMove:
-        src_source_ = IsolatedWebAppInstallSource::FromDevUi(
-            IwaSourceBundleDevModeWithFileOp(src_bundle_path_,
-                                             IwaSourceBundleDevFileOp::kMove));
-        break;
-      case IwaSourceBundleModeAndFileOp::kProdModeCopy:
-        src_source_ = IsolatedWebAppInstallSource::FromGraphicalInstaller(
-            IwaSourceBundleProdModeWithFileOp(
-                src_bundle_path_, IwaSourceBundleProdFileOp::kCopy));
-        break;
-      case IwaSourceBundleModeAndFileOp::kProdModeMove:
-        src_source_ = IsolatedWebAppInstallSource::FromGraphicalInstaller(
-            IwaSourceBundleProdModeWithFileOp(
-                src_bundle_path_, IwaSourceBundleProdFileOp::kMove));
-        break;
-    }
 
     IsolatedWebAppBrowserTestHarness::SetUp();
   }
@@ -82,9 +59,28 @@
             ManifestBuilder().SetName("app-1.0.0").SetVersion("1.0.0"))
             .BuildBundle(src_bundle_path_, key_pair_);
 
-    bundle->TrustSigningKey();
-    ASSERT_THAT(bundle->InstallWithSource(profile(), *src_source_),
-                base::test::HasValue());
+    switch (mode_and_file_op_) {
+      case IwaSourceBundleModeAndFileOp::kDevModeCopy:
+        ASSERT_THAT(bundle->InstallWithSource(
+                        profile(), &IsolatedWebAppInstallSource::FromDevUi,
+                        IwaSourceBundleDevFileOp::kCopy),
+                    base::test::HasValue());
+        break;
+      case IwaSourceBundleModeAndFileOp::kDevModeMove:
+        ASSERT_THAT(bundle->InstallWithSource(
+                        profile(), &IsolatedWebAppInstallSource::FromDevUi,
+                        IwaSourceBundleDevFileOp::kMove),
+                    base::test::HasValue());
+        break;
+      case IwaSourceBundleModeAndFileOp::kProdModeCopy:
+      case IwaSourceBundleModeAndFileOp::kProdModeMove:
+        ASSERT_THAT(
+            bundle->InstallWithSource(
+                profile(), &IsolatedWebAppInstallSource::FromGraphicalInstaller,
+                mode_and_file_op_),
+            base::test::HasValue());
+        break;
+    }
 
     const WebApp* web_app =
         provider()->registrar_unsafe().GetAppById(url_info_.app_id());
@@ -129,7 +125,6 @@
           test::GetDefaultEd25519WebBundleId());
 
   base::FilePath src_bundle_path_;
-  std::optional<IsolatedWebAppInstallSource> src_source_;
 };
 
 IN_PROC_BROWSER_TEST_P(IsolatedWebAppUninstallBrowserTest, Succeeds) {
diff --git a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_unittest.cc
index 2c5cc80..48d0f47 100644
--- a/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_unittest.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/isolated_web_app_update_manager_unittest.cc
@@ -326,10 +326,7 @@
 
   ASSERT_OK_AND_ASSIGN(IsolatedWebAppUrlInfo url_info,
                        initial_bundle->InstallWithSource(
-                           profile(), IsolatedWebAppInstallSource::FromDevUi(
-                                          IwaSourceBundleDevModeWithFileOp(
-                                              initial_bundle->path(),
-                                              kDefaultBundleDevFileOp))));
+                           profile(), &IsolatedWebAppInstallSource::FromDevUi));
 
   page_state.manifest_before_default_processing = CreateDefaultManifest(
       url_info.origin().GetURL(), u"updated iwa", base::Version("2.0.0"));
diff --git a/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.cc b/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.cc
index 4f7410b2..6cf64cc8 100644
--- a/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.cc
+++ b/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.cc
@@ -25,6 +25,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/test_future.h"
 #include "base/threading/thread_restrictions.h"
+#include "base/types/optional_ref.h"
 #include "chrome/browser/web_applications/isolated_web_apps/commands/install_isolated_web_app_command.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_install_source.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_source.h"
@@ -121,7 +122,10 @@
 base::expected<IsolatedWebAppUrlInfo, std::string> Install(
     Profile* profile,
     const web_package::SignedWebBundleId& web_bundle_id,
-    const IsolatedWebAppInstallSource& install_source) {
+    const IsolatedWebAppInstallSource& install_source,
+    const ManifestBuilder& manifest_builder,
+    bool fake_install_page,
+    bool trust_key) {
   auto url_info =
       IsolatedWebAppUrlInfo::CreateFromSignedWebBundleId(web_bundle_id);
   if (FakeWebAppProvider* fake_provider = GetFakeWebAppProvider(profile)) {
@@ -137,12 +141,22 @@
     auto& web_contents_manager = static_cast<FakeWebContentsManager&>(
         fake_provider->web_contents_manager());
     if (!web_contents_manager.HasPageState(install_url)) {
-      LOG(WARNING) << "The install page for this IWA has not been faked. "
-                   << "You likely need to call FakeInstallPageState before "
-                   << "Install.";
+      if (fake_install_page) {
+        FakeInstallPageState(
+            profile, url_info,
+            manifest_builder.ToBlinkManifest(url_info.origin()));
+      } else {
+        LOG(WARNING) << "The install page for this IWA has not been faked. "
+                     << "You likely need to remove DoNotFakeInstallPage or "
+                     << "manually call FakeInstallPageState before Install.";
+      }
     }
   }
 
+  if (trust_key) {
+    AddTrustedWebBundleIdForTesting(web_bundle_id);
+  }
+
   base::test::TestFuture<InstallResult> future;
   WebAppProvider::GetForWebApps(profile)->scheduler().InstallIsolatedWebApp(
       url_info, install_source,
@@ -170,161 +184,6 @@
 
 }  // namespace
 
-BundledIsolatedWebApp::BundledIsolatedWebApp(
-    const web_package::SignedWebBundleId& web_bundle_id,
-    const std::vector<uint8_t> serialized_bundle,
-    const base::FilePath path,
-    ManifestBuilder manifest_builder)
-    : web_bundle_id_(web_bundle_id),
-      path_(std::move(path)),
-      manifest_builder_(manifest_builder) {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-  CHECK(base::WriteFile(path_, std::move(serialized_bundle)));
-}
-
-BundledIsolatedWebApp::~BundledIsolatedWebApp() = default;
-
-void BundledIsolatedWebApp::TrustSigningKey() {
-  AddTrustedWebBundleIdForTesting(web_bundle_id_);
-}
-
-std::string BundledIsolatedWebApp::GetBundleData() const {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-  std::string content;
-  CHECK(base::ReadFileToString(path_, &content));
-  return content;
-}
-
-IsolatedWebAppUrlInfo BundledIsolatedWebApp::InstallChecked(Profile* profile) {
-  auto result = Install(profile);
-  CHECK(result.has_value()) << result.error();
-  return *result;
-}
-
-base::expected<IsolatedWebAppUrlInfo, std::string>
-BundledIsolatedWebApp::TrustBundleAndInstall(Profile* profile) {
-  TrustSigningKey();
-  return Install(profile);
-}
-
-base::expected<IsolatedWebAppUrlInfo, std::string>
-BundledIsolatedWebApp::Install(Profile* profile) {
-  return ::web_app::Install(
-      profile, web_bundle_id_,
-      IsolatedWebAppInstallSource::FromGraphicalInstaller(
-          web_app::IwaSourceBundleProdModeWithFileOp(
-              path(), web_app::IwaSourceBundleProdFileOp::kCopy)));
-}
-
-base::expected<IsolatedWebAppUrlInfo, std::string>
-BundledIsolatedWebApp::InstallWithSource(Profile* profile,
-                                         IsolatedWebAppInstallSource source) {
-  return ::web_app::Install(profile, web_bundle_id_, source);
-}
-
-base::expected<IsolatedWebAppUrlInfo, std::string>
-BundledIsolatedWebApp::InstallWithSource(
-    Profile* profile,
-    base::FunctionRef<IsolatedWebAppInstallSource(IwaSourceDevModeWithFileOp)>
-        install_source_provider,
-    IwaSourceBundleDevFileOp file_op) {
-  return ::web_app::Install(
-      profile, web_bundle_id_,
-      install_source_provider(web_app::IwaSourceDevModeWithFileOp(
-          web_app::IwaSourceBundleDevModeWithFileOp(path(), file_op))));
-}
-
-base::expected<IsolatedWebAppUrlInfo, std::string>
-BundledIsolatedWebApp::InstallWithSource(
-    Profile* profile,
-    base::FunctionRef<IsolatedWebAppInstallSource(IwaSourceProdModeWithFileOp)>
-        install_source_provider,
-    IwaSourceBundleProdFileOp file_op) {
-  return ::web_app::Install(
-      profile, web_bundle_id_,
-      install_source_provider(web_app::IwaSourceProdModeWithFileOp(
-          web_app::IwaSourceBundleProdModeWithFileOp(path(), file_op))));
-}
-
-FakeWebContentsManager::FakePageState&
-BundledIsolatedWebApp::FakeInstallPageState(Profile* profile) {
-  auto url_info =
-      IsolatedWebAppUrlInfo::CreateFromSignedWebBundleId(web_bundle_id_);
-  return ::web_app::FakeInstallPageState(
-      profile, url_info, manifest_builder_.ToBlinkManifest(url_info.origin()));
-}
-
-// static
-std::unique_ptr<ScopedBundledIsolatedWebApp>
-ScopedBundledIsolatedWebApp::Create(
-    const web_package::SignedWebBundleId& web_bundle_id,
-    const std::vector<uint8_t> serialized_bundle,
-    ManifestBuilder manifest_builder) {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-  base::ScopedTempFile bundle_file;
-  CHECK(bundle_file.Create());
-
-  return base::WrapUnique(new ScopedBundledIsolatedWebApp(
-      web_bundle_id, std::move(serialized_bundle), std::move(bundle_file),
-      std::move(manifest_builder)));
-}
-
-ScopedBundledIsolatedWebApp::ScopedBundledIsolatedWebApp(
-    const web_package::SignedWebBundleId& web_bundle_id,
-    const std::vector<uint8_t> serialized_bundle,
-    base::ScopedTempFile bundle_file,
-    ManifestBuilder manifest_builder)
-    : BundledIsolatedWebApp(web_bundle_id,
-                            std::move(serialized_bundle),
-                            bundle_file.path(),
-                            std::move(manifest_builder)),
-      bundle_file_(std::move(bundle_file)) {}
-
-ScopedBundledIsolatedWebApp::~ScopedBundledIsolatedWebApp() {
-  base::ScopedAllowBlockingForTesting allow_blocking;
-  bundle_file_.Reset();
-}
-
-ScopedProxyIsolatedWebApp::ScopedProxyIsolatedWebApp(
-    std::unique_ptr<net::EmbeddedTestServer> proxy_server,
-    std::optional<ManifestBuilder> manifest_builder)
-    : proxy_server_(std::move(proxy_server)),
-      manifest_builder_(manifest_builder) {}
-
-ScopedProxyIsolatedWebApp::~ScopedProxyIsolatedWebApp() = default;
-
-IsolatedWebAppUrlInfo ScopedProxyIsolatedWebApp::InstallChecked(
-    Profile* profile) {
-  auto result = Install(profile);
-  CHECK(result.has_value()) << result.error();
-  return *result;
-}
-
-base::expected<IsolatedWebAppUrlInfo, std::string>
-ScopedProxyIsolatedWebApp::Install(Profile* profile) {
-  return Install(profile,
-                 web_package::SignedWebBundleId::CreateRandomForProxyMode());
-}
-
-base::expected<IsolatedWebAppUrlInfo, std::string>
-ScopedProxyIsolatedWebApp::Install(
-    Profile* profile,
-    const web_package::SignedWebBundleId& web_bundle_id) {
-  return ::web_app::Install(profile, web_bundle_id,
-                            IsolatedWebAppInstallSource::FromDevUi(
-                                IwaSourceProxy(proxy_server_->GetOrigin())));
-}
-
-FakeWebContentsManager::FakePageState&
-ScopedProxyIsolatedWebApp::FakeInstallPageState(
-    Profile* profile,
-    const web_package::SignedWebBundleId& web_bundle_id) {
-  auto url_info =
-      IsolatedWebAppUrlInfo::CreateFromSignedWebBundleId(web_bundle_id);
-  return ::web_app::FakeInstallPageState(
-      profile, url_info, manifest_builder_->ToBlinkManifest(url_info.origin()));
-}
-
 ManifestBuilder::PermissionsPolicy::PermissionsPolicy(
     bool wildcard,
     bool self,
@@ -877,4 +736,126 @@
   return response;
 }
 
+BundledIsolatedWebApp::BundledIsolatedWebApp(
+    const web_package::SignedWebBundleId& web_bundle_id,
+    const std::vector<uint8_t> serialized_bundle,
+    const base::FilePath path,
+    ManifestBuilder manifest_builder)
+    : web_bundle_id_(web_bundle_id),
+      path_(std::move(path)),
+      manifest_builder_(manifest_builder) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  CHECK(base::WriteFile(path_, std::move(serialized_bundle)));
+}
+
+BundledIsolatedWebApp::~BundledIsolatedWebApp() = default;
+
+std::string BundledIsolatedWebApp::GetBundleData() const {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  std::string content;
+  CHECK(base::ReadFileToString(path_, &content));
+  return content;
+}
+
+void BundledIsolatedWebApp::TrustSigningKey() {
+  AddTrustedWebBundleIdForTesting(web_bundle_id_);
+}
+
+FakeWebContentsManager::FakePageState&
+BundledIsolatedWebApp::FakeInstallPageState(Profile* profile) {
+  auto url_info =
+      IsolatedWebAppUrlInfo::CreateFromSignedWebBundleId(web_bundle_id_);
+  return ::web_app::FakeInstallPageState(
+      profile, url_info, manifest_builder_.ToBlinkManifest(url_info.origin()));
+}
+
+base::expected<IsolatedWebAppUrlInfo, std::string>
+BundledIsolatedWebApp::TrustBundleAndInstall(Profile* profile) {
+  TrustSigningKey();
+  return Install(profile);
+}
+
+base::expected<IsolatedWebAppUrlInfo, std::string>
+BundledIsolatedWebApp::InstallWithSource(Profile* profile,
+                                         IsolatedWebAppInstallSource source,
+                                         bool fake_install_page,
+                                         bool trust_key) {
+  return ::web_app::Install(profile, web_bundle_id_, source, manifest_builder_,
+                            fake_install_page, trust_key);
+}
+
+// static
+std::unique_ptr<ScopedBundledIsolatedWebApp>
+ScopedBundledIsolatedWebApp::Create(
+    const web_package::SignedWebBundleId& web_bundle_id,
+    const std::vector<uint8_t> serialized_bundle,
+    ManifestBuilder manifest_builder) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::ScopedTempFile bundle_file;
+  CHECK(bundle_file.Create());
+
+  return base::WrapUnique(new ScopedBundledIsolatedWebApp(
+      web_bundle_id, std::move(serialized_bundle), std::move(bundle_file),
+      std::move(manifest_builder)));
+}
+
+ScopedBundledIsolatedWebApp::ScopedBundledIsolatedWebApp(
+    const web_package::SignedWebBundleId& web_bundle_id,
+    const std::vector<uint8_t> serialized_bundle,
+    base::ScopedTempFile bundle_file,
+    ManifestBuilder manifest_builder)
+    : BundledIsolatedWebApp(web_bundle_id,
+                            std::move(serialized_bundle),
+                            bundle_file.path(),
+                            std::move(manifest_builder)),
+      bundle_file_(std::move(bundle_file)) {}
+
+ScopedBundledIsolatedWebApp::~ScopedBundledIsolatedWebApp() {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  bundle_file_.Reset();
+}
+
+ScopedProxyIsolatedWebApp::ScopedProxyIsolatedWebApp(
+    std::unique_ptr<net::EmbeddedTestServer> proxy_server,
+    const ManifestBuilder& manifest_builder)
+    : proxy_server_(std::move(proxy_server)),
+      manifest_builder_(manifest_builder) {}
+
+ScopedProxyIsolatedWebApp::~ScopedProxyIsolatedWebApp() = default;
+
+IsolatedWebAppUrlInfo ScopedProxyIsolatedWebApp::InstallChecked(
+    Profile* profile) {
+  auto result = Install(profile);
+  CHECK(result.has_value()) << result.error();
+  return *result;
+}
+
+base::expected<IsolatedWebAppUrlInfo, std::string>
+ScopedProxyIsolatedWebApp::Install(Profile* profile) {
+  return Install(profile,
+                 web_package::SignedWebBundleId::CreateRandomForProxyMode());
+}
+
+base::expected<IsolatedWebAppUrlInfo, std::string>
+ScopedProxyIsolatedWebApp::Install(
+    Profile* profile,
+    const web_package::SignedWebBundleId& web_bundle_id) {
+  return ::web_app::Install(profile, web_bundle_id,
+                            IsolatedWebAppInstallSource::FromDevUi(
+                                IwaSourceProxy(proxy_server_->GetOrigin())),
+                            manifest_builder_,
+                            /*fake_install_page=*/true,
+                            /*trust_key=*/false);
+}
+
+FakeWebContentsManager::FakePageState&
+ScopedProxyIsolatedWebApp::FakeInstallPageState(
+    Profile* profile,
+    const web_package::SignedWebBundleId& web_bundle_id) {
+  auto url_info =
+      IsolatedWebAppUrlInfo::CreateFromSignedWebBundleId(web_bundle_id);
+  return ::web_app::FakeInstallPageState(
+      profile, url_info, manifest_builder_.ToBlinkManifest(url_info.origin()));
+}
+
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h b/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h
index 82290141..074464d 100644
--- a/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h
+++ b/chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h
@@ -16,6 +16,7 @@
 #include "base/files/scoped_temp_file.h"
 #include "base/functional/function_ref.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/traits_bag.h"
 #include "base/types/expected.h"
 #include "base/version.h"
 #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_install_source.h"
@@ -50,6 +51,10 @@
 
 namespace web_app {
 
+class BundledIsolatedWebApp;
+class ScopedBundledIsolatedWebApp;
+class ScopedProxyIsolatedWebApp;
+
 // A builder for a subset of the Web Manifest spec.
 class ManifestBuilder {
  public:
@@ -132,106 +137,6 @@
   std::map<std::string, FileHandlerAccept> file_handlers_;
 };
 
-class BundledIsolatedWebApp {
- public:
-  BundledIsolatedWebApp(const web_package::SignedWebBundleId& web_bundle_id,
-                        const std::vector<uint8_t> serialized_bundle,
-                        const base::FilePath path,
-                        ManifestBuilder manifest_builder);
-
-  virtual ~BundledIsolatedWebApp();
-
-  const base::FilePath& path() const { return path_; }
-
-  const web_package::SignedWebBundleId& web_bundle_id() const {
-    return web_bundle_id_;
-  }
-
-  base::Version version() const { return manifest_builder_.version(); }
-
-  std::string GetBundleData() const;
-
-  // Saves this app's signing key in Chrome's list of trusted keys, which will
-  // allow the app to be installed with dev mode disabled.
-  void TrustSigningKey();
-
-  web_app::FakeWebContentsManager::FakePageState& FakeInstallPageState(
-      Profile* profile);
-
-  // uses GraphicalInstaller source
-  base::expected<IsolatedWebAppUrlInfo, std::string> Install(Profile* profile);
-
-  base::expected<IsolatedWebAppUrlInfo, std::string> InstallWithSource(
-      Profile* profile,
-      IsolatedWebAppInstallSource source);
-
-  base::expected<IsolatedWebAppUrlInfo, std::string> InstallWithSource(
-      Profile* profile,
-      base::FunctionRef<IsolatedWebAppInstallSource(IwaSourceDevModeWithFileOp)>
-          install_source_provider,
-      IwaSourceBundleDevFileOp file_op = IwaSourceBundleDevFileOp::kCopy);
-
-  base::expected<IsolatedWebAppUrlInfo, std::string> InstallWithSource(
-      Profile* profile,
-      base::FunctionRef<IsolatedWebAppInstallSource(
-          IwaSourceProdModeWithFileOp)> install_source_provider,
-      IwaSourceBundleProdFileOp file_op = IwaSourceBundleProdFileOp::kCopy);
-
-  IsolatedWebAppUrlInfo InstallChecked(Profile* profile);
-  base::expected<IsolatedWebAppUrlInfo, std::string> TrustBundleAndInstall(
-      Profile* profile);
-
- private:
-  web_package::SignedWebBundleId web_bundle_id_;
-  base::FilePath path_;
-  ManifestBuilder manifest_builder_;
-};
-
-class ScopedBundledIsolatedWebApp : public BundledIsolatedWebApp {
- public:
-  static std::unique_ptr<ScopedBundledIsolatedWebApp> Create(
-      const web_package::SignedWebBundleId& web_bundle_id,
-      const std::vector<uint8_t> serialized_bundle,
-      ManifestBuilder manifest_builder);
-
-  ~ScopedBundledIsolatedWebApp() override;
-
- private:
-  ScopedBundledIsolatedWebApp(
-      const web_package::SignedWebBundleId& web_bundle_id,
-      const std::vector<uint8_t> serialized_bundle,
-      base::ScopedTempFile bundle_file,
-      ManifestBuilder manifest_builder);
-
-  base::ScopedTempFile bundle_file_;
-};
-
-class ScopedProxyIsolatedWebApp {
- public:
-  explicit ScopedProxyIsolatedWebApp(
-      std::unique_ptr<net::EmbeddedTestServer> proxy_server,
-      std::optional<ManifestBuilder> manifest_builder = std::nullopt);
-
-  ~ScopedProxyIsolatedWebApp();
-
-  net::EmbeddedTestServer& proxy_server() { return *proxy_server_; }
-
-  IsolatedWebAppUrlInfo InstallChecked(Profile* profile);
-
-  base::expected<IsolatedWebAppUrlInfo, std::string> Install(Profile* profile);
-  base::expected<IsolatedWebAppUrlInfo, std::string> Install(
-      Profile* profile,
-      const web_package::SignedWebBundleId& web_bundle_id);
-
-  web_app::FakeWebContentsManager::FakePageState& FakeInstallPageState(
-      Profile* profile,
-      const web_package::SignedWebBundleId& web_bundle_id);
-
- private:
-  std::unique_ptr<net::EmbeddedTestServer> proxy_server_;
-  std::optional<ManifestBuilder> manifest_builder_;
-};
-
 // A builder for Isolated Web Apps that supports adding resources from disk
 // (typically from //chrome/test/data), or from strings provided by the test,
 // and allows callers to create a dev proxy server or signed web bundle file
@@ -397,6 +302,238 @@
   std::map<std::string, Resource> resources_;
 };
 
+class BundledIsolatedWebApp {
+ public:
+#define WITH_TRAITS(TraitNames)                   \
+  template <typename... TraitNames>               \
+    requires base::trait_helpers::AreValidTraits< \
+        BundledIsolatedWebApp::ValidTraits, TraitNames...>
+  struct DoNotFakeInstallPage {};
+  struct DoNotTrustKey {};
+  struct ValidTraits {
+    explicit ValidTraits(DoNotFakeInstallPage);
+    explicit ValidTraits(DoNotTrustKey);
+  };
+
+  BundledIsolatedWebApp(const web_package::SignedWebBundleId& web_bundle_id,
+                        const std::vector<uint8_t> serialized_bundle,
+                        const base::FilePath path,
+                        ManifestBuilder manifest_builder);
+
+  virtual ~BundledIsolatedWebApp();
+
+  const base::FilePath& path() const { return path_; }
+
+  const web_package::SignedWebBundleId& web_bundle_id() const {
+    return web_bundle_id_;
+  }
+
+  base::Version version() const { return manifest_builder_.version(); }
+
+  std::string GetBundleData() const;
+
+  // Saves this app's signing key in Chrome's list of trusted keys, which will
+  // allow the app to be installed with dev mode disabled.
+  void TrustSigningKey();
+
+  // Configures FakeWebContentsManager with a fake installation page matching
+  // the data from this bundle. This is necessary for unit tests that can't
+  // load the auto-generated install page in a real renderer process.
+  web_app::FakeWebContentsManager::FakePageState& FakeInstallPageState(
+      Profile* profile);
+
+  // Installs the IWA contained in this bundle with the IWA_GRAPHICAL_INSTALLER
+  // install source.
+  //
+  // The bundle's signing key will be marked as trusted prior to installation
+  // unless the `DoNotTrustKey` trait is provided.
+  // A fake installation page will be configured if `FakeWebAppProvider` is
+  // being used unless the `DoNotFakeInstallPage` trait is provided.
+  //
+  // Callers should generally wrap this function in ASSERT_OK_AND_ASSIGN when
+  // possible:
+  //     ASSERT_OK_AND_ASSIGN(web_app::IsolatedWebAppUrlInfo url_info,
+  //          iwa->Install(profile()));
+  WITH_TRAITS(InstallationTraits)
+  base::expected<IsolatedWebAppUrlInfo, std::string> Install(
+      Profile* profile,
+      InstallationTraits&&... traits);
+
+  // Like `Install`, but CHECKs that the installation was successful.
+  // Prefer using `Install` whenever possible for better error messages.
+  WITH_TRAITS(InstallationTraits)
+  IsolatedWebAppUrlInfo InstallChecked(Profile* profile,
+                                       InstallationTraits&&... traits);
+
+  // Installs the IWA contained in this bundle with the install source
+  // returned by `install_source_provider`:
+  //     ASSERT_OK_AND_ASSIGN(web_app::IsolatedWebAppUrlInfo url_info,
+  //          iwa->InstallWithSource(profile(),
+  //              &IsolatedWebAppInstallSource::FromExternalPolicy));
+  WITH_TRAITS(InstallationTraits)
+  base::expected<IsolatedWebAppUrlInfo, std::string> InstallWithSource(
+      Profile* profile,
+      base::FunctionRef<IsolatedWebAppInstallSource(
+          IwaSourceProdModeWithFileOp)> install_source_provider,
+      IwaSourceBundleProdFileOp file_op = IwaSourceBundleProdFileOp::kCopy,
+      InstallationTraits&&... traits);
+
+  WITH_TRAITS(InstallationTraits)
+  base::expected<IsolatedWebAppUrlInfo, std::string> InstallWithSource(
+      Profile* profile,
+      base::FunctionRef<IsolatedWebAppInstallSource(IwaSourceDevModeWithFileOp)>
+          install_source_provider,
+      IwaSourceBundleDevFileOp file_op = kDefaultBundleDevFileOp,
+      InstallationTraits&&... traits);
+
+  WITH_TRAITS(InstallationTraits)
+  base::expected<IsolatedWebAppUrlInfo, std::string> InstallWithSource(
+      Profile* profile,
+      base::FunctionRef<IsolatedWebAppInstallSource(
+          IwaSourceBundleWithModeAndFileOp)> install_source_provider,
+      IwaSourceBundleModeAndFileOp file_op =
+          IwaSourceBundleModeAndFileOp::kProdModeCopy,
+      InstallationTraits&&... traits);
+
+  // TODO(crbug.com/390443309): Delete this
+  base::expected<IsolatedWebAppUrlInfo, std::string> TrustBundleAndInstall(
+      Profile* profile);
+
+ private:
+  WITH_TRAITS(InstallationTraits)
+  base::expected<IsolatedWebAppUrlInfo, std::string> InstallWithSource(
+      Profile* profile,
+      IsolatedWebAppInstallSource source,
+      InstallationTraits&&... traits) {
+    return InstallWithSource(
+        profile, source,
+        /*fake_install_page=*/
+        !base::trait_helpers::HasTrait<DoNotFakeInstallPage,
+                                       InstallationTraits...>(),
+        /*trust_key=*/
+        !base::trait_helpers::HasTrait<DoNotTrustKey, InstallationTraits...>());
+  }
+
+  base::expected<IsolatedWebAppUrlInfo, std::string> InstallWithSource(
+      Profile* profile,
+      IsolatedWebAppInstallSource source,
+      bool fake_install_page,
+      bool trust_key);
+
+  web_package::SignedWebBundleId web_bundle_id_;
+  base::FilePath path_;
+  ManifestBuilder manifest_builder_;
+};
+
+WITH_TRAITS(InstallationTraits)
+base::expected<IsolatedWebAppUrlInfo, std::string>
+BundledIsolatedWebApp::Install(Profile* profile,
+                               InstallationTraits&&... traits) {
+  return InstallWithSource(
+      profile, &IsolatedWebAppInstallSource::FromGraphicalInstaller,
+      IwaSourceBundleModeAndFileOp::kProdModeCopy, traits...);
+}
+
+WITH_TRAITS(InstallationTraits)
+IsolatedWebAppUrlInfo BundledIsolatedWebApp::InstallChecked(
+    Profile* profile,
+    InstallationTraits&&... traits) {
+  auto result = Install(profile, traits...);
+  CHECK(result.has_value()) << result.error();
+  return *result;
+}
+
+WITH_TRAITS(InstallationTraits)
+base::expected<IsolatedWebAppUrlInfo, std::string>
+BundledIsolatedWebApp::InstallWithSource(
+    Profile* profile,
+    base::FunctionRef<IsolatedWebAppInstallSource(IwaSourceProdModeWithFileOp)>
+        install_source_provider,
+    IwaSourceBundleProdFileOp file_op,
+    InstallationTraits&&... traits) {
+  return InstallWithSource(
+      profile,
+      install_source_provider(IwaSourceProdModeWithFileOp(
+          IwaSourceBundleProdModeWithFileOp(path(), file_op))),
+      traits...);
+}
+
+WITH_TRAITS(InstallationTraits)
+base::expected<IsolatedWebAppUrlInfo, std::string>
+BundledIsolatedWebApp::InstallWithSource(
+    Profile* profile,
+    base::FunctionRef<IsolatedWebAppInstallSource(IwaSourceDevModeWithFileOp)>
+        install_source_provider,
+    IwaSourceBundleDevFileOp file_op,
+    InstallationTraits&&... traits) {
+  return InstallWithSource(
+      profile,
+      install_source_provider(IwaSourceDevModeWithFileOp(
+          IwaSourceBundleDevModeWithFileOp(path(), file_op))),
+      traits...);
+}
+
+WITH_TRAITS(InstallationTraits)
+base::expected<IsolatedWebAppUrlInfo, std::string>
+BundledIsolatedWebApp::InstallWithSource(
+    Profile* profile,
+    base::FunctionRef<IsolatedWebAppInstallSource(
+        IwaSourceBundleWithModeAndFileOp)> install_source_provider,
+    IwaSourceBundleModeAndFileOp file_op,
+    InstallationTraits&&... traits) {
+  return InstallWithSource(
+      profile,
+      install_source_provider(
+          IwaSourceBundleWithModeAndFileOp(path(), file_op)),
+      traits...);
+}
+#undef WITH_TRAITS
+
+class ScopedBundledIsolatedWebApp : public BundledIsolatedWebApp {
+ public:
+  static std::unique_ptr<ScopedBundledIsolatedWebApp> Create(
+      const web_package::SignedWebBundleId& web_bundle_id,
+      const std::vector<uint8_t> serialized_bundle,
+      ManifestBuilder manifest_builder);
+
+  ~ScopedBundledIsolatedWebApp() override;
+
+ private:
+  ScopedBundledIsolatedWebApp(
+      const web_package::SignedWebBundleId& web_bundle_id,
+      const std::vector<uint8_t> serialized_bundle,
+      base::ScopedTempFile bundle_file,
+      ManifestBuilder manifest_builder);
+
+  base::ScopedTempFile bundle_file_;
+};
+
+class ScopedProxyIsolatedWebApp {
+ public:
+  ScopedProxyIsolatedWebApp(
+      std::unique_ptr<net::EmbeddedTestServer> proxy_server,
+      const ManifestBuilder& manifest_builder);
+
+  ~ScopedProxyIsolatedWebApp();
+
+  net::EmbeddedTestServer& proxy_server() { return *proxy_server_; }
+
+  IsolatedWebAppUrlInfo InstallChecked(Profile* profile);
+
+  base::expected<IsolatedWebAppUrlInfo, std::string> Install(Profile* profile);
+  base::expected<IsolatedWebAppUrlInfo, std::string> Install(
+      Profile* profile,
+      const web_package::SignedWebBundleId& web_bundle_id);
+
+  web_app::FakeWebContentsManager::FakePageState& FakeInstallPageState(
+      Profile* profile,
+      const web_package::SignedWebBundleId& web_bundle_id);
+
+ private:
+  std::unique_ptr<net::EmbeddedTestServer> proxy_server_;
+  ManifestBuilder manifest_builder_;
+};
+
 }  // namespace web_app
 
 #endif  // CHROME_BROWSER_WEB_APPLICATIONS_ISOLATED_WEB_APPS_TEST_ISOLATED_WEB_APP_BUILDER_H_
diff --git a/chrome/browser/web_applications/os_integration/mac/BUILD.gn b/chrome/browser/web_applications/os_integration/mac/BUILD.gn
index 44469c3b..a76bf301 100644
--- a/chrome/browser/web_applications/os_integration/mac/BUILD.gn
+++ b/chrome/browser/web_applications/os_integration/mac/BUILD.gn
@@ -2,6 +2,7 @@
 # Copyright 2018 The Chromium Authors
 # found in the LICENSE file.
 
+import("//build/config/sanitizers/sanitizers.gni")
 import("//build/util/branding.gni")
 import("//chrome/version.gni")
 
@@ -42,5 +43,12 @@
       # relative path is correct given the executable's location.
       "-Wcrl,installnametool,-change,@executable_path/../Frameworks/${chrome_product_full_name} Framework.framework/Versions/${chrome_version_full}/${chrome_product_full_name} Framework,@executable_path/../${chrome_product_full_name} Framework",
     ]
+
+    if (is_asan || is_ubsan_any) {
+      # When sanitizers are used their runtime libraries are found in the
+      # versioned subdirectory of the framework, one level above where the
+      # helper is installed.
+      ldflags += [ "-Wl,-rpath,@executable_path/.." ]
+    }
   }
 }
diff --git a/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_copier_mac.mm b/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_copier_mac.mm
index dcdbc93d..4d31e20d 100644
--- a/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_copier_mac.mm
+++ b/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_copier_mac.mm
@@ -13,6 +13,7 @@
 #include "base/apple/mach_port_rendezvous.h"
 #include "base/base_paths.h"
 #include "base/command_line.h"
+#include "base/debug/leak_annotations.h"
 #include "base/feature_list.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -90,7 +91,12 @@
   const auto& command_line = *base::CommandLine::ForCurrentProcess();
   base::HistogramSharedMemory::InitFromLaunchParameters(command_line);
 
-  base::FieldTrialList field_trial_list;
+  // This is intentionally leaked since it needs to live for the duration of
+  // the process and there's no benefit in cleaning it up at exit.
+  base::FieldTrialList* leaked_field_trial_list = new base::FieldTrialList();
+  ANNOTATE_LEAKING_OBJECT_PTR(leaked_field_trial_list);
+  std::ignore = leaked_field_trial_list;
+
   base::FieldTrialList::CreateTrialsInChildProcess(command_line);
   auto feature_list = std::make_unique<base::FeatureList>();
   base::FieldTrialList::ApplyFeatureOverridesInChildProcess(feature_list.get());
diff --git a/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_creator.mm b/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_creator.mm
index baf60f7..8ef2499 100644
--- a/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_creator.mm
+++ b/chrome/browser/web_applications/os_integration/mac/web_app_shortcut_creator.mm
@@ -662,9 +662,7 @@
 
 #if defined(ADDRESS_SANITIZER)
   const base::FilePath asan_library_path =
-      framework_bundle_path.Append("Versions")
-          .Append("Current")
-          .Append("libclang_rt.asan_osx_dynamic.dylib");
+      framework_bundle_path.Append("libclang_rt.asan_osx_dynamic.dylib");
   if (!base::CopyFile(asan_library_path, destination_executable_path.Append(
                                              asan_library_path.BaseName()))) {
     LOG(ERROR) << "Failed to copy asan library: " << asan_library_path;
diff --git a/chrome/browser/web_applications/scope_extension_info.cc b/chrome/browser/web_applications/scope_extension_info.cc
index 4096d10..a3dc600 100644
--- a/chrome/browser/web_applications/scope_extension_info.cc
+++ b/chrome/browser/web_applications/scope_extension_info.cc
@@ -50,7 +50,13 @@
 ScopeExtensionInfo::ScopeExtensionInfo(url::Origin origin,
                                        GURL scope,
                                        bool has_origin_wildcard)
-    : origin(origin), scope(scope), has_origin_wildcard(has_origin_wildcard) {}
+    : origin(origin), has_origin_wildcard(has_origin_wildcard) {
+  GURL::Replacements replacements;
+  replacements.ClearRef();
+  replacements.ClearQuery();
+  this->scope = scope.ReplaceComponents(replacements);
+  CHECK(this->scope.is_valid());
+}
 
 void ScopeExtensionInfo::Reset() {
   origin = url::Origin();
diff --git a/chrome/browser/web_applications/scope_extension_info.h b/chrome/browser/web_applications/scope_extension_info.h
index 022ed21..a424533 100644
--- a/chrome/browser/web_applications/scope_extension_info.h
+++ b/chrome/browser/web_applications/scope_extension_info.h
@@ -43,7 +43,9 @@
 
   url::Origin origin;
 
-  // Must be the same origin as `origin` else CHECK-fails
+  // Must be the same origin as `origin` else CHECK-fails.
+  // `scope` will also drop any queries or fragments from the URL per manifest
+  // scope rules: https://w3c.github.io/manifest/#scope-member
   GURL scope;
 
   bool has_origin_wildcard = false;
diff --git a/chrome/browser/web_applications/web_app_install_finalizer.cc b/chrome/browser/web_applications/web_app_install_finalizer.cc
index bd2fc28..df5c202 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer.cc
@@ -40,6 +40,7 @@
 #include "chrome/browser/web_applications/os_integration/web_app_shortcuts_menu.h"
 #include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
 #include "chrome/browser/web_applications/proto/web_app_install_state.pb.h"
+#include "chrome/browser/web_applications/scope_extension_info.h"
 #include "chrome/browser/web_applications/web_app.h"
 #include "chrome/browser/web_applications/web_app_command_manager.h"
 #include "chrome/browser/web_applications/web_app_constants.h"
@@ -310,6 +311,12 @@
     web_app->SetManifestId(web_app_info.manifest_id());
   }
 
+  for (auto& scope_extension : validated_scope_extensions) {
+    // This is done to prune any queries or fragments from the scope URL which
+    // may have been skipped by WebAppOriginAssociationManager validation.
+    scope_extension = ScopeExtensionInfo::CreateForScope(
+        scope_extension.scope, scope_extension.has_origin_wildcard);
+  }
   web_app->SetValidatedScopeExtensions(validated_scope_extensions);
 
   const base::Time now_time = base::Time::Now();
diff --git a/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc b/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc
index 2133362..e8a8525 100644
--- a/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc
+++ b/chrome/browser/web_applications/web_app_install_finalizer_unittest.cc
@@ -585,4 +585,68 @@
   EXPECT_EQ(ScopeExtensions(), installed_app->validated_scope_extensions());
 }
 
+class WebAppInstallFinalizerUnitTestQueriesAndFragments
+    : public WebAppInstallFinalizerUnitTest,
+      public testing::WithParamInterface<std::tuple<std::string, std::string>> {
+ public:
+  WebAppInstallFinalizerUnitTestQueriesAndFragments() = default;
+  WebAppInstallFinalizerUnitTestQueriesAndFragments(
+      const WebAppInstallFinalizerUnitTestQueriesAndFragments&) = delete;
+  WebAppInstallFinalizerUnitTestQueriesAndFragments& operator=(
+      const WebAppInstallFinalizerUnitTestQueriesAndFragments&) = delete;
+  ~WebAppInstallFinalizerUnitTestQueriesAndFragments() override = default;
+};
+
+TEST_P(WebAppInstallFinalizerUnitTestQueriesAndFragments,
+       ValidateOriginAssociationsDropQueriesAndFragments) {
+  std::string start_url_str, expected_sanitized_start_url_str;
+  std::tie(start_url_str, expected_sanitized_start_url_str) = GetParam();
+  GURL start_url(start_url_str);
+  GURL expected_sanitized_start_url(expected_sanitized_start_url_str);
+  auto info = WebAppInstallInfo::CreateWithStartUrlForTesting(start_url);
+  info->title = u"Foo Title";
+  WebAppInstallFinalizer::FinalizeOptions options(
+      webapps::WebappInstallSource::INTERNAL_DEFAULT);
+
+  auto scope_extension =
+      ScopeExtensionInfo::CreateForScope(start_url,
+                                         /*has_origin_wildcard=*/true);
+  CHECK(!scope_extension.origin.opaque());
+  info->scope_extensions = {scope_extension};
+
+  // Set data such that scope_extension will be returned in validated data.
+  std::map<ScopeExtensionInfo, ScopeExtensionInfo> data = {
+      {scope_extension, scope_extension}};
+  static_cast<FakeWebAppOriginAssociationManager&>(
+      provider().origin_association_manager())
+      .SetData(data);
+
+  FinalizeInstallResult result = AwaitFinalizeInstall(*info, options);
+
+  EXPECT_EQ(webapps::InstallResultCode::kSuccessNewInstall, result.code);
+  const WebApp* installed_app = registrar().GetAppById(result.installed_app_id);
+  EXPECT_EQ(installed_app->install_state(),
+            proto::InstallState::INSTALLED_WITH_OS_INTEGRATION);
+  EXPECT_EQ(ScopeExtensions({scope_extension}),
+            installed_app->validated_scope_extensions());
+  for (const auto& scope_ext_info :
+       installed_app->validated_scope_extensions()) {
+    ASSERT_EQ(expected_sanitized_start_url, scope_ext_info.scope);
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    WebAppInstallFinalizerUnitTestQueriesAndFragments,
+    testing::Values(
+        std::tuple<std::string, std::string>("https://foo.example/path",
+                                             "https://foo.example/path"),
+        std::tuple<std::string, std::string>(
+            "https://foo.example/search?q=querystring",
+            "https://foo.example/search"),
+        std::tuple<std::string, std::string>("https://foo.example/#hello",
+                                             "https://foo.example"),
+        std::tuple<std::string, std::string>(
+            "https://foo.example/search?q=querystring#hello",
+            "https://foo.example/search")));
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/web_app_install_info.h b/chrome/browser/web_applications/web_app_install_info.h
index 42909cf6..c20ad82b 100644
--- a/chrome/browser/web_applications/web_app_install_info.h
+++ b/chrome/browser/web_applications/web_app_install_info.h
@@ -389,6 +389,9 @@
 
   // The app intends to have an extended scope containing URLs described by this
   // information.
+  // Note: All specified 'scope' members of these extensions will have queries
+  // and fragments stripped, per specification.
+  // https://w3c.github.io/manifest/#scope-member
   base::flat_set<web_app::ScopeExtensionInfo> scope_extensions;
 
   // `scope_extensions` after going through validation with associated origins.
@@ -396,6 +399,9 @@
   // See
   // https://github.com/WICG/manifest-incubations/blob/gh-pages/scope_extensions-explainer.md
   // for association requirements.
+  // Note: All specified 'scope' members of these extensions will have queries
+  // and fragments stripped, per specification.
+  // https://w3c.github.io/manifest/#scope-member
   std::optional<base::flat_set<web_app::ScopeExtensionInfo>>
       validated_scope_extensions;
 
diff --git a/chrome/browser/web_applications/web_app_origin_association_manager_browsertest.cc b/chrome/browser/web_applications/web_app_origin_association_manager_browsertest.cc
index 9830c773..a309add 100644
--- a/chrome/browser/web_applications/web_app_origin_association_manager_browsertest.cc
+++ b/chrome/browser/web_applications/web_app_origin_association_manager_browsertest.cc
@@ -23,7 +23,7 @@
 const std::string& kWebAppIdentity = "https://foo.com/index";
 const std::string& kInvalidFileUrl = "https://a.com";
 const std::string& kValidAppUrl = "https://b.com";
-const std::string& kValidAndInvalidAppsUrl = "https://c.com";
+const std::string& kValidAndInvalidAppsUrl = "https://c.com/search";
 
 constexpr char kInvalidFileContent[] = "invalid";
 constexpr char kValidAppFileContent[] =
@@ -33,7 +33,7 @@
 constexpr char kValidAndInvalidAppsFileContent[] =
     R"({
     // 1st app is valid.
-      "https://foo.com/index": {},
+      "https://foo.com/index": { "scope": "/search?q=some+text#frag"},
     // 2nd app is invalid since kWebAppIdentity doesn't match.
       "https://bar.com/": {}
     })";
@@ -76,8 +76,7 @@
 
     valid_and_invalid_app_scope_extension_ =
         std::make_unique<ScopeExtensionInfo>(
-            ScopeExtensionInfo::CreateForOrigin(
-                url::Origin::Create(GURL(kValidAndInvalidAppsUrl))));
+            ScopeExtensionInfo::CreateForScope(GURL(kValidAndInvalidAppsUrl)));
   }
 
   void VerifyValidAndInvalidAppsResult(int expected_callback_count,
@@ -89,8 +88,8 @@
     auto valid_app_scope_extension =
         ScopeExtensionInfo::CreateForOrigin(valid_app_scope_extension_->origin);
     auto valid_and_invalid_app_scope_extension =
-        ScopeExtensionInfo::CreateForOrigin(
-            valid_and_invalid_app_scope_extension_->origin,
+        ScopeExtensionInfo::CreateForScope(
+            valid_and_invalid_app_scope_extension_->scope,
             /*has_origin_wildcard*/ valid_and_invalid_app_scope_extension_
                 ->has_origin_wildcard);
 
diff --git a/chrome/browser/web_applications/web_app_origin_association_task.cc b/chrome/browser/web_applications/web_app_origin_association_task.cc
index ab5c0ea..5fbaa427 100644
--- a/chrome/browser/web_applications/web_app_origin_association_task.cc
+++ b/chrome/browser/web_applications/web_app_origin_association_task.cc
@@ -87,7 +87,13 @@
   for (webapps::mojom::AssociatedWebAppPtr& associated_app :
        association->apps) {
     if (associated_app->web_app_identity == web_app_identity_) {
-      scope_extension.scope = associated_app->scope;
+      // Must drop the fragments and queries per `scope` rules
+      // https://w3c.github.io/manifest/#scope-member
+      GURL::Replacements replacements;
+      replacements.ClearRef();
+      replacements.ClearQuery();
+      scope_extension.scope =
+          associated_app->scope.ReplaceComponents(replacements);
       result_.insert(scope_extension);
       scope_extension.Reset();
       // Only information in the first valid app is saved.
diff --git a/chrome/browser/web_applications/web_app_registrar_unittest.cc b/chrome/browser/web_applications/web_app_registrar_unittest.cc
index bab9537..b3074cf 100644
--- a/chrome/browser/web_applications/web_app_registrar_unittest.cc
+++ b/chrome/browser/web_applications/web_app_registrar_unittest.cc
@@ -33,6 +33,7 @@
 #include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
 #include "chrome/browser/web_applications/proto/web_app_install_state.pb.h"
 #include "chrome/browser/web_applications/proto/web_app_proto_package.pb.h"
+#include "chrome/browser/web_applications/scope_extension_info.h"
 #include "chrome/browser/web_applications/test/fake_web_app_database_factory.h"
 #include "chrome/browser/web_applications/test/fake_web_app_provider.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
@@ -1344,13 +1345,22 @@
 
   // Full scope
   auto associate_url3 = GURL("https://example.co.uk");
-  auto associate_extended_scope3 = GURL(associate_url3.spec());
+
+  // Scope with query and fragment
+  auto associate_url4 =
+      GURL("https://example.com.jp/search?q=asdf+text#fragment");
+  auto associate_extended_scope4 = GURL(associate_url4.spec());
+
+  auto associate_url5 = GURL("https://example.com/index.html/#fragment");
+  auto associate_extended_scope5 = GURL(associate_url5.spec());
 
   web_app->SetValidatedScopeExtensions(
       {ScopeExtensionInfo::CreateForScope(associate_extended_scope),
        ScopeExtensionInfo::CreateForScope(associate_url2,
                                           /*has_origin_wildcard=*/true),
-       ScopeExtensionInfo::CreateForScope(associate_extended_scope3)});
+       ScopeExtensionInfo::CreateForScope(associate_url3),
+       ScopeExtensionInfo::CreateForScope(associate_extended_scope4),
+       ScopeExtensionInfo::CreateForScope(associate_extended_scope5)});
   RegisterAppUnsafe(std::move(web_app));
 
   EXPECT_EQ(
@@ -1379,6 +1389,18 @@
   EXPECT_GT(registrar().GetAppExtendedScopeScore(GURL("https://example.co.uk"),
                                                  app_id),
             0);
+  EXPECT_GT(registrar().GetAppExtendedScopeScore(
+                GURL("https://example.com.jp/search"), app_id),
+            0);
+  EXPECT_GT(registrar().GetAppExtendedScopeScore(
+                GURL("https://example.com.jp/search?q=something"), app_id),
+            0);
+  EXPECT_GT(registrar().GetAppExtendedScopeScore(
+                GURL("https://example.com/index.html"), app_id),
+            0);
+  EXPECT_GT(registrar().GetAppExtendedScopeScore(
+                GURL("https://example.com/index.html#asdfragment"), app_id),
+            0);
 
   // Scope is extended to the example.app domain but not to the sub-domain
   // test.example.app as there was no wildcard prefix.
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index d2630ce..78c4f4f4 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1737115005-482922d9175421026dc3dd21473ffb1d4fb98113-623d6c39e745b0364b5e0878f07acda7defaa7b9.profdata
+chrome-android32-main-1737158320-fedaacbe63d5f10467f6a8daeb6b35b0eadfb65b-af448557a34dd74fb5ecff008e7999e0032d502c.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index 47071da..4a55cafe8 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1737128294-a16742f5aada72b0516cd0254efc9628e4323f7f-c6fb21258973e36f73e7a28e5fbce765b0cc8d8f.profdata
+chrome-android64-main-1737170057-2dd81d1cf4f2b655382e77fb55984ff716f0dc2b-df6266822515c4ebd6cae97bca7257d72a26dfb6.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index f69c667..a1359ac 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1737115005-dccb6b20b4c7d87c7a0672baf66db81cafd9702a-623d6c39e745b0364b5e0878f07acda7defaa7b9.profdata
+chrome-linux-main-1737158320-51227ec4a7ea4a3fcbfe4585edff23ca8f1732db-af448557a34dd74fb5ecff008e7999e0032d502c.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 03ccd5836..28ebeb0 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1737129594-4345a568799efb7b99a130f4db7478c0198e763e-e3cb317bc792776896d9cb072a3f174336d4d405.profdata
+chrome-mac-arm-main-1737172188-dca06392eebd4d654067abf45435ee079219e98d-67931b1c9c05ced5e20cfef2de06fb29089603ee.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index c886529..5f08d0f 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1737115005-dd692b949c6b28ad25571111290df5c3159bb2e5-623d6c39e745b0364b5e0878f07acda7defaa7b9.profdata
+chrome-mac-main-1737158320-dd36e26034ea2677c08e52381f5271471f93fcc5-af448557a34dd74fb5ecff008e7999e0032d502c.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 8784040..f30d1d2 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1737115005-27a96aefc900efe8f0fcb81a754e4faa7675a8a2-623d6c39e745b0364b5e0878f07acda7defaa7b9.profdata
+chrome-win-arm64-main-1737136754-25e26133ca72705eeeea0b076b82527747fcce10-df4518b150c1be94e9cf81d4cec18313497d1fc0.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index 36380ade..ab5ffc91 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1737103675-4468051c31afb340fbe369b56ccd4c70c30fce44-c19fabb3b4ddf650867ee9d59c91fbef047c018a.profdata
+chrome-win32-main-1737158320-3e4d855371ca00cb22fdb7c6f16859c279e7cf38-af448557a34dd74fb5ecff008e7999e0032d502c.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index fec2eab..41688ad 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1737103675-37144d3f5279fbc49c2f576567ba1a1f21eaa6cc-c19fabb3b4ddf650867ee9d59c91fbef047c018a.profdata
+chrome-win64-main-1737158320-2009e7a28046a0c5772eadbbf60cfccdb952dd01-af448557a34dd74fb5ecff008e7999e0032d502c.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 6c57db8..127f3872 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -333,11 +333,19 @@
 const base::FeatureParam<bool> kTabstripComboButtonHasBackground{
     &kTabstripComboButton, "has_background", true};
 
+const base::FeatureParam<bool> kTabstripComboButtonHasReverseButtonOrder{
+    &kTabstripComboButton, "reverse_button_order", false};
+
 bool HasTabstripComboButtonWithBackground() {
   return IsTabstripComboButtonEnabled() &&
          features::kTabstripComboButtonHasBackground.Get();
 }
 
+bool HasTabstripComboButtonWithReverseButtonOrder() {
+  return IsTabstripComboButtonEnabled() &&
+         features::kTabstripComboButtonHasReverseButtonOrder.Get();
+}
+
 // Force Privacy Guide to be available even if it would be unavailable
 // otherwise. This is meant for development and test purposes only.
 BASE_FEATURE(kPrivacyGuideForceAvailable,
@@ -757,7 +765,7 @@
 // Minimum amount of time allowed between requesting k-anonymity status from the
 // Query server for a distinct group.
 constexpr base::FeatureParam<base::TimeDelta> kKAnonymityServiceQueryInterval{
-    &kKAnonymityService, "KAnonymityServiceJoinInterval", base::Days(1)};
+    &kKAnonymityService, "KAnonymityServiceQueryInterval", base::Days(1)};
 
 // When enabled, the k-Anonymity Service will send requests to the Join and
 // Query k-anonymity servers.
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 988276d..4348c5f 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -216,6 +216,10 @@
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::FeatureParam<bool> kTabstripComboButtonHasBackground;
 COMPONENT_EXPORT(CHROME_FEATURES) bool HasTabstripComboButtonWithBackground();
+COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::FeatureParam<bool> kTabstripComboButtonHasReverseButtonOrder;
+COMPONENT_EXPORT(CHROME_FEATURES)
+bool HasTabstripComboButtonWithReverseButtonOrder();
 
 COMPONENT_EXPORT(CHROME_FEATURES)
 BASE_DECLARE_FEATURE(kPrivacyGuideForceAvailable);
diff --git a/chrome/common/extensions/api/bookmarks.json b/chrome/common/extensions/api/bookmarks.json
index 68525ab..f6581db6 100644
--- a/chrome/common/extensions/api/bookmarks.json
+++ b/chrome/common/extensions/api/bookmarks.json
@@ -23,8 +23,6 @@
         "id": "FolderType",
         "type": "string",
         "enum": ["bookmarks-bar", "other", "mobile", "managed"],
-        // TODO: crbug.com/383970322 - Remove once API naming is finalized.
-        "nodoc": true,
         "description": "Indicates the type of folder. <ul><li>bookmarks-bar: The folder whose contents is displayed at the top of the browser window<li>other: Bookmarks which are displayed in the full list of bookmarks on all platforms<li>mobile: bookmarks generally available on the user's mobile devices, but modifiable by extension or in the bookmarks manager<li>managed: a top-level folder that may be present if the system administrator or the custodian of a supervised user has configured bookmarks.</ul>"
       },
       {
@@ -81,8 +79,6 @@
           "folderType": {
             "$ref": "FolderType",
             "optional": true,
-            // TODO: crbug.com/383970322 - Remove once API naming is finalized.
-            "nodoc": true,
             "description": "If present, this is a folder that is added by the browser and that cannot be modified by the user or the extension. Child nodes may be modified, if this node does not have the <code>unmodifiable</code> property set. Omitted if the node can be modified by the user and the extension (default).<p>There may be zero, one or multiple nodes of each folder type. A folder may be added or removed by the browser, but not via the extensions API."
           },
           "unmodifiable": {
@@ -92,8 +88,6 @@
           },
           "syncing": {
             "type": "boolean",
-            // TODO: crbug.com/384024391 - Remove once API naming is finalized.
-            "nodoc": true,
             "description": "Whether this node is synced with the user's remote account storage by the browser. This can be used to distinguish between account and local-only versions of the same $(ref:FolderType). The value of this property may change for an existing node, for example as a result of user action.<p>Note: this reflects whether the node is saved to the browser's built-in account provider. It is possible that a node could be synced via a third-party, even if this value is false.</p><p>For managed nodes (nodes where <code>unmodifiable</code> is set to <code>true</code>), this property will always be <code>false</code>.</p>"
           },
           "children": {
diff --git a/chrome/common/extensions/permissions/chrome_permission_message_rules.cc b/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
index ea48951..754a4e6 100644
--- a/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
+++ b/chrome/common/extensions/permissions/chrome_permission_message_rules.cc
@@ -16,7 +16,6 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/grit/generated_resources.h"
-#include "components/device_signals/core/common/signals_features.h"
 #include "extensions/common/mojom/api_permission_id.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -279,10 +278,6 @@
 };
 
 int GetEnterpriseReportingPrivatePermissionMessageId() {
-  if (!base::FeatureList::IsEnabled(
-          enterprise_signals::features::kNewEvSignalsEnabled)) {
-    return IDS_EXTENSION_PROMPT_WARNING_ENTERPRISE_REPORTING_PRIVATE;
-  }
 #if BUILDFLAG(IS_WIN)
   return IDS_EXTENSION_PROMPT_WARNING_ENTERPRISE_REPORTING_PRIVATE_ENABLED_WIN;
 #elif BUILDFLAG(IS_LINUX) or BUILDFLAG(IS_MAC)
diff --git a/chrome/release_scripts b/chrome/release_scripts
index b37cf13..cc3ac97 160000
--- a/chrome/release_scripts
+++ b/chrome/release_scripts
@@ -1 +1 @@
-Subproject commit b37cf13dae4ea8bdbff22cee2b4820c94c83bab8
+Subproject commit cc3ac97c66a56f4218fee70f28958eaf11ef5d66
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 0d874d4..ed56bd48 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1808,6 +1808,7 @@
         "../browser/extensions/system_cpu_apitest.cc",
         "../browser/extensions/system_memory_apitest.cc",
         "../browser/extensions/test_resources_browsertest.cc",
+        "../browser/extensions/web_accessible_resources_browsertest.cc",
       ]
 
       deps += [
@@ -4066,6 +4067,7 @@
         "../browser/apps/platform_apps/platform_app_navigation_redirector_browsertest.cc",
         "../browser/apps/platform_apps/service_worker_browsertest.cc",
         "../browser/controlled_frame/controlled_frame_apitest.cc",
+        "../browser/controlled_frame/controlled_frame_contextmenus_browsertest.cc",
         "../browser/controlled_frame/controlled_frame_dialog_browsertest.cc",
         "../browser/controlled_frame/controlled_frame_disabled_permission_browsertest.cc",
         "../browser/controlled_frame/controlled_frame_new_window_browsertest.cc",
@@ -5869,7 +5871,6 @@
     "../browser/component_updater/subresource_filter_component_installer_unittest.cc",
     "../browser/component_updater/tpcd_metadata_component_installer_unittest.cc",
     "../browser/content_index/content_index_provider_unittest.cc",
-    "../browser/contextual_cueing/contextual_cueing_helper_unittest.cc",
     "../browser/custom_handlers/chrome_protocol_handler_registry_unittest.cc",
     "../browser/data_sharing/data_sharing_navigation_throttle_unittest.cc",
     "../browser/data_sharing/data_sharing_service_factory_unittest.cc",
@@ -6389,7 +6390,6 @@
     "//chrome/browser/content_extraction",
     "//chrome/browser/content_settings",
     "//chrome/browser/content_settings:content_settings_factory",
-    "//chrome/browser/contextual_cueing",
     "//chrome/browser/crash_upload_list:unit_tests",
     "//chrome/browser/data_saver",
     "//chrome/browser/devtools",
@@ -7449,6 +7449,7 @@
       "../browser/component_updater/soda_component_installer_unittest.cc",
       "../browser/component_updater/soda_language_pack_component_installer_unittest.cc",
       "../browser/component_updater/zxcvbn_data_component_installer_unittest.cc",
+      "../browser/contextual_cueing/contextual_cueing_helper_unittest.cc",
       "../browser/data_sharing/desktop/data_sharing_conversion_utils_unittest.cc",
       "../browser/device_identity/device_oauth2_token_service_unittest.cc",
       "../browser/device_notifications/device_connection_tracker_unittest.cc",
@@ -7850,6 +7851,7 @@
     ]
 
     deps += [
+      "//chrome/browser/contextual_cueing",
       "//chrome/browser/ui/bookmarks:unit_tests",
       "//chrome/browser/ui/safety_hub",
       "//chrome/browser/ui/startup:startup_tab",
@@ -9489,9 +9491,6 @@
       "//chrome/browser/ui/device_signals_consent:test_support",
       "//components/device_signals/core/browser",
       "//components/device_signals/core/browser:test_support",
-      "//components/device_signals/core/common",
-      "//components/device_signals/core/common:features",
-      "//components/device_signals/core/common:unit_tests",
       "//components/enterprise/device_trust",
       "//services/data_decoder/public/cpp:test_support",
     ]
@@ -10602,6 +10601,7 @@
       "../browser/autofill_ai/autofill_ai_browsertest.cc",
       "../browser/browser_keyevents_browsertest.cc",
       "../browser/chrome_render_widget_host_browsertests.cc",
+      "../browser/contextual_cueing/contextual_cueing_helper_interactive_uitest.cc",
       "../browser/controlled_frame/controlled_frame_interactive_browsertest.cc",
       "../browser/devtools/devtools_interactive_browsertest.cc",
       "../browser/download/download_interactive_uitest.cc",
@@ -10758,6 +10758,7 @@
       "//chrome/browser/autofill:interactive_ui_tests",
       "//chrome/browser/autofill:test_support_ui",
       "//chrome/browser/content_settings:content_settings_factory",
+      "//chrome/browser/contextual_cueing",
       "//chrome/browser/devtools",
       "//chrome/browser/devtools:test_support",
       "//chrome/browser/file_system_access",
diff --git a/chrome/test/data/accessibility/extension/manifest.json b/chrome/test/data/accessibility/extension/manifest.json
new file mode 100644
index 0000000..6156b98
--- /dev/null
+++ b/chrome/test/data/accessibility/extension/manifest.json
@@ -0,0 +1,7 @@
+{
+    "name": "test extension",
+    "manifest_version": 3,
+    "version": "1.0",
+    "key": "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDCQ+ePDzpLth/ZnUewmbuutBlIOU7RFD27Cy7Z/hFVAGA4wns+Xzj8SJ4YfcQQ1qIH7XfcPe6L+lT+IF8dMwHd5uXIFQWd8gHxeaQoIJcsPjnNgxbVR+TsAuzrgbCWz+gNz0/dDNjqVs+3Dv1QKGf6XE/+iJ4LfkzahIMtsUFp2UaOy+bYT7eh8FGmO8zuQmjAv5gM2I3K3C/8gUBvwm9yhipv/Dn7OtHLcBNHbCq0fbGIHLythTrhBTT81Z+RLAdQy787rS2gQH3IuFqLxuLfXFNBUI30QBdu2c5bL1PbePP+4w+UY8rBQ4ltqO8RDgIJl6zathCsBNtCLt3Yt5hnAgMBAAECggEAMHmkUwjwOYtJOFKsXG+IK0iKlICkX7zGyKE6QDBLX+QXnqI9AaveOund9WuQnMkKJqNFiQI1P92oDr/CLDWZricbSImiXK7SeFCru02im2otn1AqRu9JOvFh8ERs7b0UgTmtipHVoOgnrdzCLhnfFFSrq26ozWJnSBHP7/tuwLwERmBtRldo5HbV7SZWSd54vS2BimijBo4nob7vC+QZf+1p8KEWzi7GvjLxMfpaMMjaC6BcIfvUJPHvPETEMCaLgjb42d4fjr/HECge4elI1dKcXt+IiCJ0pP6Axo1VglIxNqkHbCXlkb/E+F0mST0drYz+Yix4WpxX6eSMg4aEfQKBgQD+E3mFXa3E73Fc5SoZjdvj3KnmEv7CrM3BC79fI9Ljc52PTpp7LKEpl7ywH3vAl06XW0ktwCL+fOth+frgJgRfAMQsq8u8vl9842+yl81i/yoPRRsxM8cri84d3AJRExffY6eddjc6WHqYUauhTz38gUXNvaz+bMNtGXlE2bTBJQKBgQDDvHyhAdWkZa9wTADbmL5UTlVZb/9jS8+ENUpHISqUkOKEwoxvaa071CccurIpxflfP9T+kzdreXGuKHqrziPl4lVfYCDJXo8UmvB9x7/BXvrm0+CGs8rt2i7gL66wVnf9YsGPs5B7KBK/Z8TwS7gie3MNMFLZS8qdcEvc+xzbmwKBgQDRntOlurJBRqOq1s8zIh0HE+mAjq9tghCHct/C6NV3Hs4hi+JcOWgF3tCoJnF9ZdhLe98WRe0ZNYsl3I6lG/iDQSiZCmwfHpm9eg+PszqasJbM3mEe6O8r+D5n2Dp5FV2eyqNhgET0eEc9IDSP88baavViV1lA2A8sFdY9fbhpmQKBgQCjkrDUHKRdblzei6Vr3omwCoZo55+Va5VP9vjL2HutDCdAqxSRRs7uYK0O5TZekoODheSJmp2Fw0etM0bQrMRzKGIQAlVj8xG/NnwjoPouryEeJZJM/5Nmkh76Wt6xnpFHv2/ilzz2rtZ7/kwmRCDtMB1FuEyEK0J3r5C2a2QCYQKBgASVezK2dt9N9kpNxFSJEdW5k7DUZAmAqMVndgIEWWbsurXot5rbgYfhezoST94XVa0K3juIY3lBigvC3ZTafEcLbNEfL8eYmcgb4zK85F2biyIw9Hv5fxqOPsMJ6Ltub3FaORlJGzHPz2783ZTirBsBQgSvQCqKtISVZlwrzXtl",
+    "description": "A simple extension with a defined id (cjlaeehoipngghikfjogbdkpbdgebppb) for accessibility testing."
+}
diff --git a/chrome/test/data/pdf/ink2_viewer_toolbar_test.ts b/chrome/test/data/pdf/ink2_viewer_toolbar_test.ts
index 3546423b..201cb24fb 100644
--- a/chrome/test/data/pdf/ink2_viewer_toolbar_test.ts
+++ b/chrome/test/data/pdf/ink2_viewer_toolbar_test.ts
@@ -55,6 +55,34 @@
         !viewerToolbar.shadowRoot!.querySelector('viewer-annotations-bar'));
     chrome.test.succeed();
   },
+  // Ink1 disables some toolbar buttons when in annotation mode, but Ink2 does
+  // not have the same limitations. Test that these buttons are still enabled in
+  // Ink2 annotation mode.
+  async function testInk1DisabledButtonsAreEnabled() {
+    chrome.test.assertFalse(viewerToolbar.annotationMode);
+
+    viewerToolbar.toggleAnnotation();
+    await microtasksFinished();
+
+    const rotationButton =
+        getRequiredElement<HTMLButtonElement>(viewerToolbar, '#rotate');
+    const twoPageViewButton = getRequiredElement<HTMLButtonElement>(
+        viewerToolbar, '#two-page-view-button');
+
+    chrome.test.assertTrue(viewerToolbar.annotationMode);
+    chrome.test.assertFalse(viewerToolbar.$.sidenavToggle.disabled);
+    chrome.test.assertFalse(rotationButton.disabled);
+    chrome.test.assertFalse(twoPageViewButton.disabled);
+
+    viewerToolbar.toggleAnnotation();
+    await microtasksFinished();
+
+    chrome.test.assertFalse(viewerToolbar.annotationMode);
+    chrome.test.assertFalse(viewerToolbar.$.sidenavToggle.disabled);
+    chrome.test.assertFalse(rotationButton.disabled);
+    chrome.test.assertFalse(twoPageViewButton.disabled);
+    chrome.test.succeed();
+  },
   // </if>
   // Test that toggling annotation mode does not affect displaying annotations.
   async function testTogglingAnnotationModeDoesNotAffectDisplayAnnotations() {
diff --git a/chrome/test/data/webui/chromeos/BUILD.gn b/chrome/test/data/webui/chromeos/BUILD.gn
index 6ac5ca85..6bb4ff9 100644
--- a/chrome/test/data/webui/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/chromeos/BUILD.gn
@@ -187,7 +187,7 @@
     "multidevice_setup/integration_test.js",
     "multidevice_setup/setup_succeeded_page_test.js",
     "multidevice_setup/start_setup_page_test.js",
-    "network/apn_list_item_test.js",
+    "network/apn_list_item_test.ts",
     "network/apn_list_test.ts",
     "network/apn_selection_dialog_list_item_test.js",
     "network/apn_selection_dialog_test.ts",
diff --git a/chrome/test/data/webui/chromeos/network/apn_list_item_test.js b/chrome/test/data/webui/chromeos/network/apn_list_item_test.js
deleted file mode 100644
index 707b4f6..0000000
--- a/chrome/test/data/webui/chromeos/network/apn_list_item_test.js
+++ /dev/null
@@ -1,535 +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.
-
-import 'chrome://os-settings/strings.m.js';
-import 'chrome://resources/ash/common/network/apn_list_item.js';
-import 'chrome://resources/ash/common/cr_elements/cr_action_menu/cr_action_menu.js';
-
-import {ApnDetailDialogMode, ApnEventData} from 'chrome://resources/ash/common/network/cellular_utils.js';
-import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js';
-import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {ApnState, ApnType, CrosNetworkConfigRemote} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
-import {NetworkType, PortalState} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
-import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {FakeNetworkConfig} from 'chrome://webui-test/chromeos/fake_network_config_mojom.js';
-import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
-import {eventToPromise} from 'chrome://webui-test/test_util.js';
-
-/** @type {!ApnEventData} */
-const TEST_APN_EVENT_DATA = {
-  apn: {
-    name: 'test_apn',
-  },
-  guid: 'test-guid',
-  mode: ApnDetailDialogMode.VIEW,
-};
-
-suite('ApnListItemTest', function() {
-  /** @type {ApnListItemElement} */
-  let apnListItem = null;
-
-  /** @type {?CrosNetworkConfigRemote} */
-  let mojoApi_ = null;
-
-  setup(async function() {
-    mojoApi_ = new FakeNetworkConfig();
-    MojoInterfaceProviderImpl.getInstance().remote_ = mojoApi_;
-  });
-
-  async function init() {
-    apnListItem = document.createElement('apn-list-item');
-    apnListItem.apn = {
-      accessPointName: 'apn1',
-    };
-    apnListItem.guid = 'cellular_guid';
-    document.body.appendChild(apnListItem);
-    return flushTasks();
-  }
-
-  function openThreeDotMenu() {
-    const menuButton =
-        apnListItem.shadowRoot.querySelector('#actionMenuButton');
-    assertTrue(!!menuButton);
-    assertFalse(apnListItem.$.dotsMenu.open);
-
-    menuButton.click();
-    return flushTasks();
-  }
-
-  test('Check if APN list item exists', async function() {
-    await init();
-    assertTrue(!!apnListItem);
-    apnListItem.apn = {
-      accessPointName: 'apn1',
-    };
-    await flushTasks();
-    assertEquals(
-        apnListItem.$.apnName.innerText, apnListItem.apn.accessPointName);
-
-    apnListItem.apn = {
-      accessPointName: apnListItem.apn.accessPointName,
-      name: 'name',
-    };
-    await flushTasks();
-    assertEquals(apnListItem.$.apnName.innerText, apnListItem.apn.name);
-
-    apnListItem.apn = {};
-    await flushTasks();
-    assertEquals(
-        apnListItem.$.apnName.innerText, apnListItem.i18n('apnNameModem'));
-  });
-
-  test('Check if connected sublabel is shown', async function() {
-    await init();
-    apnListItem.isConnected = false;
-    await flushTasks();
-
-    const subLabel = apnListItem.shadowRoot.querySelector('#subLabel');
-    assertTrue(!!subLabel);
-    assertTrue(subLabel.hasAttribute('hidden'));
-    apnListItem.isConnected = true;
-    await flushTasks();
-
-    assertFalse(subLabel.hasAttribute('hidden'));
-    assertFalse(subLabel.hasAttribute('warning'));
-    assertEquals(
-        apnListItem.i18n('NetworkHealthStateConnected'), subLabel.innerText);
-
-    apnListItem.portalState = PortalState.kNoInternet;
-    assertFalse(subLabel.hasAttribute('hidden'));
-    assertTrue(subLabel.hasAttribute('warning'));
-    assertEquals(
-        apnListItem.i18n('networkListItemConnectedNoConnectivity'),
-        subLabel.innerText);
-  });
-
-  test('Check if APN three dot menu shows', async function() {
-    await init();
-    await openThreeDotMenu();
-    assertTrue(apnListItem.$.dotsMenu.open);
-  });
-
-  test('Check disabled state.', async function() {
-    await init();
-    apnListItem.apn = {
-      state: ApnState.kDisabled,
-      accessPointName: 'apn',
-    };
-    await flushTasks();
-    assertFalse(apnListItem.hasAttribute('is-disabled_'));
-
-    apnListItem.apn = {
-      state: ApnState.kEnabled,
-      accessPointName: 'apn',
-      id: '1',
-    };
-    await flushTasks();
-    assertFalse(apnListItem.hasAttribute('is-disabled_'));
-
-    apnListItem.apn = {
-      state: ApnState.kDisabled,
-      accessPointName: 'apn',
-      id: '1',
-    };
-    await flushTasks();
-    assertTrue(apnListItem.hasAttribute('is-disabled_'));
-  });
-
-  test('Check if three dot menu remove APN works', async function() {
-    await init();
-    const guid = 'cellular_guid';
-    await openThreeDotMenu();
-    const getRemoveButton = () =>
-        apnListItem.$.dotsMenu.querySelector('#removeButton');
-    assertFalse(!!getRemoveButton());
-
-    apnListItem.apn = {
-      accessPointName: 'name1',
-      id: '1',
-    };
-    await flushTasks();
-    assertTrue(!!getRemoveButton());
-    assertFalse(!!getRemoveButton().disabled);
-
-    apnListItem.shouldDisallowApnModification = true;
-    assertTrue(!!getRemoveButton().disabled);
-
-    apnListItem.shouldDisallowApnModification = false;
-    mojoApi_.setNetworkTypeEnabledState(NetworkType.kCellular, true);
-    const props = OncMojo.getDefaultManagedProperties(
-        NetworkType.kCellular, guid, 'cellular');
-
-    props.typeProperties.cellular = {
-      customApnList: [{
-        accessPointName: 'name1',
-        id: '1',
-      }],
-    };
-    mojoApi_.setManagedPropertiesForTest(props);
-    let managedProps = await mojoApi_.getManagedProperties(guid);
-    assertEquals(
-        1, managedProps.result.typeProperties.cellular.customApnList.length);
-    getRemoveButton().click();
-    await mojoApi_.whenCalled('removeCustomApn');
-
-    managedProps = await mojoApi_.getManagedProperties(guid);
-    assertEquals(
-        0, managedProps.result.typeProperties.cellular.customApnList.length);
-    assertFalse(apnListItem.$.dotsMenu.open);
-  });
-
-  test('Check if three dot menu disable/enable APN works', async function() {
-    await init();
-    const guid = 'cellular_guid';
-    await openThreeDotMenu();
-    const getEnableButton = () =>
-        apnListItem.$.dotsMenu.querySelector('#enableButton');
-    const getDisableButton = () =>
-        apnListItem.$.dotsMenu.querySelector('#disableButton');
-    assertFalse(!!getEnableButton());
-    assertFalse(!!getDisableButton());
-
-    const createApn = (disabled) => {
-      return {
-        accessPointName: 'name1',
-        id: '1',
-        state: disabled ? ApnState.kDisabled : ApnState.kEnabled,
-      };
-    };
-
-    mojoApi_.setNetworkTypeEnabledState(NetworkType.kCellular, true);
-    const props = OncMojo.getDefaultManagedProperties(
-        NetworkType.kCellular, guid, 'cellular');
-    const disabledApn = createApn(/*disabled=*/ true);
-    props.typeProperties.cellular = {
-      customApnList: [disabledApn],
-    };
-    mojoApi_.setManagedPropertiesForTest(props);
-
-    apnListItem.apn = disabledApn;
-    await flushTasks();
-    assertTrue(!!getEnableButton());
-    assertFalse(!!getEnableButton().disabled);
-    assertFalse(!!getDisableButton());
-
-    apnListItem.shouldDisallowApnModification = true;
-    assertTrue(!!getEnableButton().disabled);
-
-    apnListItem.shouldDisallowApnModification = false;
-    getEnableButton().click();
-    await mojoApi_.whenCalled('modifyCustomApn');
-    let managedProps = await mojoApi_.getManagedProperties(guid);
-    assertEquals(
-        ApnState.kEnabled,
-        managedProps.result.typeProperties.cellular.customApnList[0].state);
-    assertFalse(apnListItem.$.dotsMenu.open);
-
-    apnListItem.apn = createApn(/*disabled=*/ false);
-    await flushTasks();
-    assertTrue(!!getDisableButton());
-    assertFalse(!!getDisableButton().disabled);
-    assertFalse(!!getEnableButton());
-
-    apnListItem.shouldDisallowApnModification = true;
-    assertTrue(!!getDisableButton().disabled);
-
-    apnListItem.shouldDisallowApnModification = false;
-    getDisableButton().click();
-    await mojoApi_.whenCalled('modifyCustomApn');
-    managedProps = await mojoApi_.getManagedProperties(guid);
-    assertEquals(
-        ApnState.kDisabled,
-        managedProps.result.typeProperties.cellular.customApnList[0].state);
-    assertFalse(apnListItem.$.dotsMenu.open);
-  });
-
-  [true, false].forEach(isApnRevampAndAllowApnModificationPolicyEnabled => {
-    test(
-        `Clicking APN details button triggers a show-apn-detail-dialog event
-        when isApnRevampAndAllowApnModificationPolicyEnabled is ${
-            isApnRevampAndAllowApnModificationPolicyEnabled}`,
-        async function() {
-          loadTimeData.overrideValues({
-            isApnRevampAndAllowApnModificationPolicyEnabled,
-          });
-          await init();
-          apnListItem.apn = TEST_APN_EVENT_DATA.apn;
-          apnListItem.guid = TEST_APN_EVENT_DATA.guid;
-
-          const subLabel =
-              apnListItem.shadowRoot.querySelector('#autoDetected');
-          assertTrue(!!subLabel);
-          assertFalse(subLabel.hasAttribute('hidden'));
-          assertEquals(apnListItem.i18n('apnAutoDetected'), subLabel.innerText);
-
-          let apnDetailsClickedEvent =
-              eventToPromise('show-apn-detail-dialog', window);
-          assertTrue(!!apnListItem.$.detailsButton);
-          assertEquals(
-              apnListItem.i18n('apnMenuDetails'),
-              apnListItem.$.detailsButton.innerText.trim());
-          apnListItem.$.detailsButton.click();
-          let eventData = await apnDetailsClickedEvent;
-
-          assertEquals(TEST_APN_EVENT_DATA.apn.name, eventData.detail.apn.name);
-          assertEquals(TEST_APN_EVENT_DATA.mode, eventData.detail.mode);
-          assertFalse(apnListItem.$.dotsMenu.open);
-
-          // Case: the apn list item is not auto detected.
-          apnListItem.apn = {
-            name: TEST_APN_EVENT_DATA.apn.name,
-            id: '1',
-          };
-          assertTrue(subLabel.hasAttribute('hidden'));
-          assertEquals(
-              apnListItem.i18n('apnMenuEdit'),
-              apnListItem.$.detailsButton.innerText.trim());
-
-          apnDetailsClickedEvent =
-              eventToPromise('show-apn-detail-dialog', window);
-          apnListItem.$.detailsButton.click();
-          eventData = await apnDetailsClickedEvent;
-          assertEquals(TEST_APN_EVENT_DATA.apn.name, eventData.detail.apn.name);
-          assertEquals(ApnDetailDialogMode.EDIT, eventData.detail.mode);
-          assertFalse(apnListItem.$.dotsMenu.open);
-
-          if (isApnRevampAndAllowApnModificationPolicyEnabled) {
-            // Case: APN modification is disallowed.
-            apnListItem.shouldDisallowApnModification = true;
-            await flushTasks();
-
-            assertEquals(
-                apnListItem.i18n('apnMenuDetails'),
-                apnListItem.$.detailsButton.innerText.trim());
-
-            apnDetailsClickedEvent =
-                eventToPromise('show-apn-detail-dialog', window);
-            apnListItem.$.detailsButton.click();
-            eventData = await apnDetailsClickedEvent;
-            assertEquals(
-                TEST_APN_EVENT_DATA.apn.name, eventData.detail.apn.name);
-            assertEquals(ApnDetailDialogMode.VIEW, eventData.detail.mode);
-            assertFalse(apnListItem.$.dotsMenu.open);
-          }
-        });
-  });
-
-  test('Test if disable/remove warning event is fired.', async function() {
-    await init();
-    const guid = 'cellular_guid';
-    let promptShowEvent = eventToPromise('show-error-toast', window);
-    await openThreeDotMenu();
-    const getDisableButton = () =>
-        apnListItem.$.dotsMenu.querySelector('#disableButton');
-    const getRemoveButton = () =>
-        apnListItem.$.dotsMenu.querySelector('#removeButton');
-    const createApn = () => {
-      return {
-        accessPointName: 'name1',
-        id: '1',
-        state: ApnState.kEnabled,
-      };
-    };
-
-    apnListItem.shouldDisallowDisablingRemoving = true;
-    apnListItem.apn = createApn();
-    mojoApi_.setNetworkTypeEnabledState(NetworkType.kCellular, true);
-    const props = OncMojo.getDefaultManagedProperties(
-        NetworkType.kCellular, guid, 'cellular');
-
-    props.typeProperties.cellular = {customApnList: [createApn()]};
-    mojoApi_.setManagedPropertiesForTest(props);
-    await flushTasks();
-    getDisableButton().click();
-    let eventData = await promptShowEvent;
-    let managedProps = await mojoApi_.getManagedProperties(guid);
-    assertEquals(
-        ApnState.kEnabled,
-        managedProps.result.typeProperties.cellular.customApnList[0].state);
-    assertEquals(
-        apnListItem.i18n('apnWarningPromptForDisableRemove'), eventData.detail);
-    assertFalse(apnListItem.$.dotsMenu.open);
-
-    promptShowEvent = eventToPromise('show-error-toast', window);
-    getRemoveButton().click();
-    eventData = await promptShowEvent;
-    managedProps = await mojoApi_.getManagedProperties(guid);
-    assertEquals(
-        1, managedProps.result.typeProperties.cellular.customApnList.length);
-    assertEquals(
-        apnListItem.i18n('apnWarningPromptForDisableRemove'), eventData.detail);
-    assertFalse(apnListItem.$.dotsMenu.open);
-  });
-
-  test('Test if enable warning event is fired.', async function() {
-    await init();
-    const guid = 'cellular_guid';
-    const promptShowEvent = eventToPromise('show-error-toast', window);
-    await openThreeDotMenu();
-    const getEnableButton = () =>
-        apnListItem.$.dotsMenu.querySelector('#enableButton');
-    const createApn = () => {
-      return {
-        accessPointName: 'name1',
-        id: '1',
-        state: ApnState.kDisabled,
-      };
-    };
-
-    apnListItem.shouldDisallowEnabling = true;
-    apnListItem.apn = createApn();
-    mojoApi_.setNetworkTypeEnabledState(NetworkType.kCellular, true);
-    const props = OncMojo.getDefaultManagedProperties(
-        NetworkType.kCellular, guid, 'cellular');
-
-    props.typeProperties.cellular = {customApnList: [createApn()]};
-    mojoApi_.setManagedPropertiesForTest(props);
-    await flushTasks();
-    getEnableButton().click();
-    const eventData = await promptShowEvent;
-    const managedProps = await mojoApi_.getManagedProperties(guid);
-    assertEquals(
-        ApnState.kDisabled,
-        managedProps.result.typeProperties.cellular.customApnList[0].state);
-    assertEquals(
-        apnListItem.i18n('apnWarningPromptForEnable'), eventData.detail);
-    assertFalse(apnListItem.$.dotsMenu.open);
-  });
-
-  test('Item a11y', async function() {
-    await init();
-    apnListItem.itemIndex = 0;
-    apnListItem.listSize = 1;
-
-    // Enabled custom APN, non-connected.
-    const apnName = 'apn1';
-    const apnUserFriendlyName = 'userFriendlyNameApn1';
-    const apnId = '1';
-    const defaultTypeOnly = apnListItem.i18n('apnA11yDefaultApnOnly');
-    const attachTypeOnly = apnListItem.i18n('apnA11yAttachApnOnly');
-    const defaultAndAttach = apnListItem.i18n('apnA11yDefaultAndAttachApn');
-
-    // Attach only APN.
-    apnListItem.apn = {
-      id: apnId,
-      accessPointName: apnName,
-      apnTypes: [ApnType.kAttach],
-    };
-
-    const enabledText = apnListItem.i18n('apnA11yEnabled');
-    const nameText = apnListItem.i18n(
-        'apnA11yName', /*index=*/ 1, /*count=*/ 1, /*name=*/ 'apn1');
-    assertEquals(
-        apnListItem.$.actionMenuButton.ariaLabel,
-        nameText + ' ' + enabledText + ' ' + attachTypeOnly);
-
-    // Attach and Default APN.
-    apnListItem.apn = {
-      id: apnId,
-      accessPointName: apnName,
-      apnTypes: [ApnType.kDefault, ApnType.kAttach],
-    };
-
-    assertEquals(
-        apnListItem.$.actionMenuButton.ariaLabel,
-        nameText + ' ' + enabledText + ' ' + defaultAndAttach);
-
-    // Default only APN.
-    apnListItem.apn = {
-      id: apnId,
-      accessPointName: apnName,
-      apnTypes: [ApnType.kDefault],
-    };
-
-    assertEquals(
-        apnListItem.$.actionMenuButton.ariaLabel,
-        nameText + ' ' + enabledText + ' ' + defaultTypeOnly);
-
-    // Enabled custom APN, connected.
-    apnListItem.isConnected = true;
-
-    const connectedText = apnListItem.i18n('apnA11yConnected');
-    assertEquals(
-        apnListItem.$.actionMenuButton.ariaLabel,
-        nameText + ' ' + connectedText + ' ' + defaultTypeOnly);
-
-    assertEquals(
-        apnListItem.$.actionMenuButton.ariaLabel,
-        nameText + ' ' + connectedText + ' ' + defaultTypeOnly);
-
-    assertEquals(
-        apnListItem.$.actionMenuButton.ariaLabel,
-        nameText + ' ' + connectedText + ' ' + defaultTypeOnly);
-
-    // Disabled custom APN, non-connected.
-    apnListItem.apn = {
-      id: apnId,
-      accessPointName: apnName,
-      state: ApnState.kDisabled,
-    };
-    apnListItem.isConnected = false;
-
-    const disabledText = apnListItem.i18n('apnA11yDisabled');
-    assertEquals(
-        apnListItem.$.actionMenuButton.ariaLabel,
-        nameText + ' ' + disabledText);
-
-    // Enabled database APN, non-connected.
-    apnListItem.apn = {
-      accessPointName: apnName,
-    };
-    apnListItem.isConnected = false;
-    const autoDetectedText = apnListItem.i18n('apnA11yAutoDetected');
-    assertEquals(
-        apnListItem.$.actionMenuButton.ariaLabel,
-        nameText + ' ' + autoDetectedText + ' ' + enabledText);
-
-    // Enabled database APN, connected.
-    apnListItem.apn = {
-      accessPointName: apnName,
-    };
-    apnListItem.isConnected = true;
-    assertEquals(
-        apnListItem.$.actionMenuButton.ariaLabel,
-        nameText + ' ' + autoDetectedText + ' ' + connectedText);
-
-    // Null APN, connected.
-    apnListItem.apn = {};
-    assertEquals(
-        apnListItem.$.actionMenuButton.ariaLabel,
-        apnListItem.i18n(
-            'apnA11yName', /*index=*/ 1, /*count=*/ 1,
-            apnListItem.i18n('apnNameModem')) +
-            ' ' + autoDetectedText + ' ' + connectedText);
-
-    // User friendly APN name same as APN has no text indicating as such.
-    apnListItem.apn = {
-      id: apnId,
-      accessPointName: apnName,
-      name: apnName,
-    };
-    apnListItem.isConnected = true;
-    assertEquals(
-        apnListItem.$.actionMenuButton.ariaLabel,
-        nameText + ' ' + connectedText);
-
-    // User friendly APN name different from APN has indicating text.
-    const userFriendlyNameText = apnListItem.i18n(
-        'apnA11yName', /*index=*/ 1, /*count=*/ 1,
-        /*name=*/ 'userFriendlyNameApn1');
-    apnListItem.apn = {
-      id: apnId,
-      accessPointName: apnName,
-      name: apnUserFriendlyName,
-    };
-
-    const indicateUserFriendlyNameText = apnListItem.i18n(
-        'apnA11yUserFriendlyNameIndicator', apnUserFriendlyName, apnName);
-    assertEquals(
-        apnListItem.$.actionMenuButton.ariaLabel,
-        userFriendlyNameText + ' ' + connectedText + ' ' +
-            indicateUserFriendlyNameText);
-  });
-});
diff --git a/chrome/test/data/webui/chromeos/network/apn_list_item_test.ts b/chrome/test/data/webui/chromeos/network/apn_list_item_test.ts
new file mode 100644
index 0000000..3350c66
--- /dev/null
+++ b/chrome/test/data/webui/chromeos/network/apn_list_item_test.ts
@@ -0,0 +1,603 @@
+// 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.
+
+import 'chrome://os-settings/strings.m.js';
+import 'chrome://resources/ash/common/network/apn_list_item.js';
+import 'chrome://resources/ash/common/cr_elements/cr_action_menu/cr_action_menu.js';
+
+import type {CrActionMenuElement} from 'chrome://resources/ash/common/cr_elements/cr_action_menu/cr_action_menu.js';
+import type {CrIconButtonElement} from 'chrome://resources/ash/common/cr_elements/cr_icon_button/cr_icon_button.js';
+import {ApnListItemElement} from 'chrome://resources/ash/common/network/apn_list_item.js';
+import {ApnDetailDialogMode, ApnEventData} from 'chrome://resources/ash/common/network/cellular_utils.js';
+import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js';
+import {OncMojo} from 'chrome://resources/ash/common/network/onc_mojo.js';
+import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
+import {ApnAuthenticationType, ApnIpType, ApnProperties, ApnSource, ApnState, ApnType} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js';
+import {NetworkType, PortalState} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/network_types.mojom-webui.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/polymer_test_util.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
+
+import {FakeNetworkConfig} from '../fake_network_config_mojom.js';
+
+const TEST_APN_EVENT_DATA_GUID = 'test_guid'
+
+const TEST_APN_EVENT_DATA: ApnEventData = {
+  apn: {
+    accessPointName: 'test_apn',
+    username: null,
+    password: null,
+    authentication: ApnAuthenticationType.kAutomatic,
+    ipType: ApnIpType.kAutomatic,
+    apnTypes: [],
+    state: ApnState.kEnabled,
+    id: null,
+    language: null,
+    localizedName: null,
+    name: null,
+    attach: null,
+    source: ApnSource.kUi,
+  },
+  mode: ApnDetailDialogMode.VIEW,
+};
+
+suite('ApnListItemTest', function() {
+  let apnListItem: ApnListItemElement;
+
+  let mojoApi_: FakeNetworkConfig;
+
+  setup(async () => {
+    mojoApi_ = new FakeNetworkConfig();
+    MojoInterfaceProviderImpl.getInstance().setMojoServiceRemoteForTest(
+        mojoApi_);
+  });
+
+  async function init() {
+    apnListItem = document.createElement('apn-list-item');
+    apnListItem.apn = createTestApnWithOverridenValues({
+      accessPointName: 'apn1',
+    });
+    apnListItem.guid = 'cellular_guid';
+    document.body.appendChild(apnListItem);
+    return flushTasks();
+  }
+
+  function getThreeDotsMenu(): CrActionMenuElement {
+    return apnListItem.shadowRoot!.querySelector<CrActionMenuElement>(
+        '#dotsMenu')!;
+  }
+
+  async function openThreeDotMenu() {
+    const menuButton =
+        apnListItem.shadowRoot!.querySelector<CrIconButtonElement>(
+            '#actionMenuButton');
+    assertTrue(!!menuButton);
+    assertFalse(getThreeDotsMenu().open);
+
+    menuButton.click();
+    return flushTasks();
+  }
+
+  function createTestApnWithOverridenValues(overrides: Partial<ApnProperties>):
+      ApnProperties {
+    return {...TEST_APN_EVENT_DATA.apn, ...overrides};
+  }
+
+  test('Check if APN list item exists', async () => {
+    await init();
+    assertTrue(!!apnListItem);
+    apnListItem.apn = createTestApnWithOverridenValues({
+      accessPointName: 'apn1',
+    });
+    await flushTasks();
+    assertEquals(
+        apnListItem.shadowRoot!.querySelector<HTMLDivElement>(
+                                   '#apnName')!.innerText,
+        apnListItem.apn.accessPointName);
+
+    apnListItem.apn = createTestApnWithOverridenValues({
+      accessPointName: apnListItem.apn.accessPointName,
+      name: 'name',
+    });
+    await flushTasks();
+    assertEquals(
+        apnListItem.shadowRoot!.querySelector<HTMLDivElement>(
+                                   '#apnName')!.innerText,
+        apnListItem.apn.name);
+
+    apnListItem.apn = createTestApnWithOverridenValues({
+      accessPointName: '',
+    });
+    await flushTasks();
+    assertEquals(
+        apnListItem.shadowRoot!.querySelector<HTMLDivElement>(
+                                   '#apnName')!.innerText,
+        apnListItem.i18n('apnNameModem'));
+  });
+
+  test('Check if connected sublabel is shown', async () => {
+    await init();
+    apnListItem.isApnConnected = false;
+    await flushTasks();
+
+    const subLabel =
+        apnListItem.shadowRoot!.querySelector<HTMLDivElement>('#subLabel');
+    assertTrue(!!subLabel);
+    assertTrue(subLabel.hasAttribute('hidden'), 'fails to hide sublabel');
+    apnListItem.isApnConnected = true;
+    await flushTasks();
+
+    assertFalse(subLabel.hasAttribute('hidden'), 'fails to show sublabel');
+    assertFalse(
+        subLabel.hasAttribute('warning'),
+        'fails to add warning attribute in sublabel');
+    assertEquals(
+        apnListItem.i18n('NetworkHealthStateConnected'), subLabel.innerText);
+
+    apnListItem.portalState = PortalState.kNoInternet;
+    assertFalse(subLabel.hasAttribute('hidden'), 'fails to hide show');
+    assertTrue(
+        subLabel.hasAttribute('warning'),
+        'fails to add warning a11y in sublabel');
+    assertEquals(
+        apnListItem.i18n('networkListItemConnectedNoConnectivity'),
+        subLabel.innerText);
+  });
+
+  test('Check if APN three dot menu shows', async () => {
+    await init();
+    await openThreeDotMenu();
+    assertTrue(getThreeDotsMenu().open);
+  });
+
+  test('Check disabled state.', async () => {
+    await init();
+    apnListItem.apn = createTestApnWithOverridenValues({
+      state: ApnState.kDisabled,
+      accessPointName: 'apn',
+    });
+    await flushTasks();
+    assertFalse(apnListItem.hasAttribute('is-disabled_'));
+
+    apnListItem.apn = createTestApnWithOverridenValues({
+      state: ApnState.kEnabled,
+      accessPointName: 'apn',
+      id: '1',
+    });
+    await flushTasks();
+    assertFalse(apnListItem.hasAttribute('is-disabled_'));
+
+    apnListItem.apn = createTestApnWithOverridenValues({
+      state: ApnState.kDisabled,
+      accessPointName: 'apn',
+      id: '1',
+    });
+    await flushTasks();
+    assertTrue(apnListItem.hasAttribute('is-disabled_'));
+  });
+
+  test('Check if three dot menu remove APN works', async () => {
+    await init();
+    const guid = 'cellular_guid';
+    await openThreeDotMenu();
+    const getRemoveButton = () =>
+        getThreeDotsMenu().querySelector<HTMLButtonElement>('#removeButton')!;
+    assertFalse(!!getRemoveButton());
+
+    apnListItem.apn = createTestApnWithOverridenValues({
+      accessPointName: 'name1',
+      id: '1',
+    });
+    await flushTasks();
+    assertTrue(!!getRemoveButton());
+    assertFalse(!!getRemoveButton().disabled);
+
+    apnListItem.shouldDisallowApnModification = true;
+    assertTrue(!!getRemoveButton().disabled);
+
+    apnListItem.shouldDisallowApnModification = false;
+    mojoApi_.setNetworkTypeEnabledState(NetworkType.kCellular, true);
+    const props = OncMojo.getDefaultManagedProperties(
+        NetworkType.kCellular, guid, 'cellular');
+
+    props.typeProperties.cellular = {
+      customApnList: [{
+        accessPointName: 'name1',
+        id: '1',
+      }],
+    };
+    mojoApi_.setManagedPropertiesForTest(props);
+    let managedProps = await mojoApi_.getManagedProperties(guid);
+    assertEquals(
+        1, managedProps.result.typeProperties.cellular.customApnList.length);
+    getRemoveButton().click();
+    await mojoApi_.whenCalled('removeCustomApn');
+
+    managedProps = await mojoApi_.getManagedProperties(guid);
+    assertEquals(
+        0, managedProps.result.typeProperties.cellular.customApnList.length);
+    assertFalse(getThreeDotsMenu().open);
+  });
+
+  test('Check if three dot menu disable/enable APN works', async () => {
+    await init();
+    const guid = 'cellular_guid';
+    await openThreeDotMenu();
+    const getEnableButton = () =>
+        getThreeDotsMenu().querySelector<HTMLButtonElement>('#enableButton')!;
+    const getDisableButton = () =>
+        getThreeDotsMenu().querySelector<HTMLButtonElement>('#disableButton')!;
+    assertFalse(!!getEnableButton());
+    assertFalse(!!getDisableButton());
+
+    const createApn = (disabled: boolean) => {
+      return createTestApnWithOverridenValues({
+        accessPointName: 'name1',
+        id: '1',
+        state: disabled ? ApnState.kDisabled : ApnState.kEnabled,
+      });
+    };
+
+    mojoApi_.setNetworkTypeEnabledState(NetworkType.kCellular, true);
+    const props = OncMojo.getDefaultManagedProperties(
+        NetworkType.kCellular, guid, 'cellular');
+    const disabledApn = createApn(/*disabled=*/ true);
+    props.typeProperties.cellular = {
+      customApnList: [disabledApn],
+    };
+    mojoApi_.setManagedPropertiesForTest(props);
+
+    apnListItem.apn = disabledApn;
+    await flushTasks();
+    assertTrue(!!getEnableButton());
+    assertFalse(!!getEnableButton().disabled);
+    assertFalse(!!getDisableButton());
+
+    apnListItem.shouldDisallowApnModification = true;
+    assertTrue(!!getEnableButton().disabled);
+
+    apnListItem.shouldDisallowApnModification = false;
+    getEnableButton().click();
+    await mojoApi_.whenCalled('modifyCustomApn');
+    let managedProps = await mojoApi_.getManagedProperties(guid);
+    assertEquals(
+        ApnState.kEnabled,
+        managedProps.result.typeProperties.cellular.customApnList[0].state);
+    assertFalse(getThreeDotsMenu().open);
+
+    apnListItem.apn = createApn(/*disabled=*/ false);
+    await flushTasks();
+    assertTrue(!!getDisableButton());
+    assertFalse(!!getDisableButton().disabled);
+    assertFalse(!!getEnableButton());
+
+    apnListItem.shouldDisallowApnModification = true;
+    assertTrue(!!getDisableButton().disabled);
+
+    apnListItem.shouldDisallowApnModification = false;
+    getDisableButton().click();
+    await mojoApi_.whenCalled('modifyCustomApn');
+    managedProps = await mojoApi_.getManagedProperties(guid);
+    assertEquals(
+        ApnState.kDisabled,
+        managedProps.result.typeProperties.cellular.customApnList[0].state);
+    assertFalse(getThreeDotsMenu().open);
+  });
+
+  [true, false].forEach(isApnRevampAndAllowApnModificationPolicyEnabled => {
+    test(
+        `Clicking APN details button triggers a show-apn-detail-dialog event
+        when isApnRevampAndAllowApnModificationPolicyEnabled is ${
+            isApnRevampAndAllowApnModificationPolicyEnabled}`,
+        async () => {
+          loadTimeData.overrideValues({
+            isApnRevampAndAllowApnModificationPolicyEnabled,
+          });
+          await init();
+          apnListItem.apn = TEST_APN_EVENT_DATA.apn;
+          apnListItem.guid = TEST_APN_EVENT_DATA_GUID;
+
+          const subLabel =
+              apnListItem.shadowRoot!.querySelector<HTMLDivElement>(
+                  '#autoDetected');
+          assertTrue(!!subLabel);
+          assertFalse(subLabel.hasAttribute('hidden'));
+          assertEquals(apnListItem.i18n('apnAutoDetected'), subLabel.innerText);
+
+          const getDetailsButton = () =>
+              apnListItem.shadowRoot!.querySelector<HTMLButtonElement>(
+                  '#detailsButton')!;
+
+          let apnDetailsClickedEvent =
+              eventToPromise('show-apn-detail-dialog', window);
+          assertTrue(!!getDetailsButton());
+          assertEquals(
+              apnListItem.i18n('apnMenuDetails'),
+              getDetailsButton().innerText.trim());
+          getDetailsButton().click();
+          let eventData = await apnDetailsClickedEvent;
+
+          assertEquals(TEST_APN_EVENT_DATA.apn.name, eventData.detail.apn.name);
+          assertEquals(TEST_APN_EVENT_DATA.mode, eventData.detail.mode);
+          assertFalse(getThreeDotsMenu().open);
+
+          // Case: the apn list item is not auto detected.
+          apnListItem.apn = createTestApnWithOverridenValues({
+            name: TEST_APN_EVENT_DATA.apn.name,
+            id: '1',
+          });
+          assertTrue(subLabel.hasAttribute('hidden'));
+          assertEquals(
+              apnListItem.i18n('apnMenuEdit'),
+              getDetailsButton().innerText.trim());
+
+          apnDetailsClickedEvent =
+              eventToPromise('show-apn-detail-dialog', window);
+          getDetailsButton()!.click();
+          eventData = await apnDetailsClickedEvent;
+          assertEquals(TEST_APN_EVENT_DATA.apn.name, eventData.detail.apn.name);
+          assertEquals(ApnDetailDialogMode.EDIT, eventData.detail.mode);
+          assertFalse(getThreeDotsMenu().open);
+
+          if (isApnRevampAndAllowApnModificationPolicyEnabled) {
+            // Case: APN modification is disallowed.
+            apnListItem.shouldDisallowApnModification = true;
+            await flushTasks();
+
+            assertEquals(
+                apnListItem.i18n('apnMenuDetails'),
+                getDetailsButton().innerText.trim());
+
+            apnDetailsClickedEvent =
+                eventToPromise('show-apn-detail-dialog', window);
+            getDetailsButton().click();
+            eventData = await apnDetailsClickedEvent;
+            assertEquals(
+                TEST_APN_EVENT_DATA.apn.name, eventData.detail.apn.name);
+            assertEquals(ApnDetailDialogMode.VIEW, eventData.detail.mode);
+            assertFalse(getThreeDotsMenu().open);
+          }
+        });
+  });
+
+  test('Test if disable/remove warning event is fired.', async () => {
+    await init();
+    const guid = 'cellular_guid';
+    let promptShowEvent = eventToPromise('show-error-toast', window);
+    await openThreeDotMenu();
+    const getDisableButton = () =>
+        getThreeDotsMenu().querySelector<HTMLButtonElement>('#disableButton')!;
+    const getRemoveButton = () =>
+        getThreeDotsMenu().querySelector<HTMLButtonElement>('#removeButton')!;
+    const createApn = () => createTestApnWithOverridenValues({
+      accessPointName: 'name1',
+      id: '1',
+      state: ApnState.kEnabled,
+    });
+
+    apnListItem.shouldDisallowDisablingRemoving = true;
+    apnListItem.apn = createApn();
+    mojoApi_.setNetworkTypeEnabledState(NetworkType.kCellular, true);
+    const props = OncMojo.getDefaultManagedProperties(
+        NetworkType.kCellular, guid, 'cellular');
+
+    props.typeProperties.cellular = {customApnList: [createApn()]};
+    mojoApi_.setManagedPropertiesForTest(props);
+    await flushTasks();
+    getDisableButton().click();
+    let eventData = await promptShowEvent;
+    let managedProps = await mojoApi_.getManagedProperties(guid);
+    assertEquals(
+        ApnState.kEnabled,
+        managedProps.result.typeProperties.cellular.customApnList[0].state);
+    assertEquals(
+        apnListItem.i18n('apnWarningPromptForDisableRemove'), eventData.detail);
+    assertFalse(getThreeDotsMenu().open);
+
+    promptShowEvent = eventToPromise('show-error-toast', window);
+    getRemoveButton().click();
+    eventData = await promptShowEvent;
+    managedProps = await mojoApi_.getManagedProperties(guid);
+    assertEquals(
+        1, managedProps.result.typeProperties.cellular.customApnList.length);
+    assertEquals(
+        apnListItem.i18n('apnWarningPromptForDisableRemove'), eventData.detail);
+    assertFalse(getThreeDotsMenu().open);
+  });
+
+  test('Test if enable warning event is fired.', async () => {
+    await init();
+    const guid = 'cellular_guid';
+    const promptShowEvent = eventToPromise('show-error-toast', window);
+    await openThreeDotMenu();
+    const getEnableButton = () =>
+        getThreeDotsMenu().querySelector<HTMLButtonElement>('#enableButton')!;
+    const createApn = () => createTestApnWithOverridenValues({
+      accessPointName: 'name1',
+      id: '1',
+      state: ApnState.kDisabled,
+    });
+
+    apnListItem.shouldDisallowEnabling = true;
+    apnListItem.apn = createApn();
+    mojoApi_.setNetworkTypeEnabledState(NetworkType.kCellular, true);
+    const props = OncMojo.getDefaultManagedProperties(
+        NetworkType.kCellular, guid, 'cellular');
+
+    props.typeProperties.cellular = {customApnList: [createApn()]};
+    mojoApi_.setManagedPropertiesForTest(props);
+    await flushTasks();
+    getEnableButton().click();
+    const eventData = await promptShowEvent;
+    const managedProps = await mojoApi_.getManagedProperties(guid);
+    assertEquals(
+        ApnState.kDisabled,
+        managedProps.result.typeProperties.cellular.customApnList[0].state);
+    assertEquals(
+        apnListItem.i18n('apnWarningPromptForEnable'), eventData.detail);
+    assertFalse(getThreeDotsMenu().open);
+  });
+
+  test('Item a11y', async () => {
+    await init();
+    apnListItem.itemIndex = 0;
+    apnListItem.listSize = 1;
+
+    // Enabled custom APN, non-connected.
+    const apnName = 'apn1';
+    const apnUserFriendlyName = 'userFriendlyNameApn1';
+    const apnId = '1';
+    const defaultTypeOnly = apnListItem.i18n('apnA11yDefaultApnOnly');
+    const attachTypeOnly = apnListItem.i18n('apnA11yAttachApnOnly');
+    const defaultAndAttach = apnListItem.i18n('apnA11yDefaultAndAttachApn');
+
+    // Attach only APN.
+    apnListItem.apn = createTestApnWithOverridenValues({
+      id: apnId,
+      accessPointName: apnName,
+      apnTypes: [ApnType.kAttach],
+    });
+
+    const enabledText = apnListItem.i18n('apnA11yEnabled');
+    const nameText = apnListItem.i18n(
+        'apnA11yName', /*index=*/ 1, /*count=*/ 1, /*name=*/ 'apn1');
+
+    assertEquals(
+        apnListItem.shadowRoot!
+            .querySelector<CrIconButtonElement>(
+                '#actionMenuButton')!.getAttribute('aria-label'),
+        nameText + ' ' + enabledText + ' ' + attachTypeOnly,
+        'Failed a11y for Attach only APN');
+
+    // Attach and Default APN.
+    apnListItem.apn = createTestApnWithOverridenValues({
+      id: apnId,
+      accessPointName: apnName,
+      apnTypes: [ApnType.kDefault, ApnType.kAttach],
+    });
+
+    assertEquals(
+        apnListItem.shadowRoot!
+            .querySelector<CrIconButtonElement>(
+                '#actionMenuButton')!.getAttribute('aria-label'),
+        nameText + ' ' + enabledText + ' ' + defaultAndAttach,
+        'Failed a11y for Attach+Default APN');
+
+    // Default only APN.
+    apnListItem.apn = createTestApnWithOverridenValues({
+      id: apnId,
+      accessPointName: apnName,
+      apnTypes: [ApnType.kDefault],
+    });
+
+    assertEquals(
+        apnListItem.shadowRoot!
+            .querySelector<CrIconButtonElement>(
+                '#actionMenuButton')!.getAttribute('aria-label'),
+        nameText + ' ' + enabledText + ' ' + defaultTypeOnly,
+        'Failed a11y for Default only APN');
+
+    // Enabled custom APN, connected.
+    apnListItem.isApnConnected = true;
+    await flushTasks();
+
+    const connectedText = apnListItem.i18n('apnA11yConnected');
+    assertEquals(
+        nameText + ' ' + connectedText + ' ' + defaultTypeOnly,
+        apnListItem.shadowRoot!
+            .querySelector<CrIconButtonElement>(
+                '#actionMenuButton')!.getAttribute('aria-label'),
+        'Failed a11y for connected custom APN');
+
+    // Disabled custom APN, non-connected.
+    apnListItem.apn = createTestApnWithOverridenValues({
+      id: apnId,
+      accessPointName: apnName,
+      state: ApnState.kDisabled,
+    });
+    apnListItem.isApnConnected = false;
+
+    const disabledText = apnListItem.i18n('apnA11yDisabled');
+    assertEquals(
+        apnListItem.shadowRoot!
+            .querySelector<CrIconButtonElement>(
+                '#actionMenuButton')!.getAttribute('aria-label'),
+        nameText + ' ' + disabledText,
+        'Failed a11y for disconnected disabled custom APN');
+
+    // Enabled database APN, non-connected.
+    apnListItem.apn = createTestApnWithOverridenValues({
+      accessPointName: apnName,
+    });
+    apnListItem.isApnConnected = false;
+    const autoDetectedText = apnListItem.i18n('apnA11yAutoDetected');
+    assertEquals(
+        apnListItem.shadowRoot!
+            .querySelector<CrIconButtonElement>(
+                '#actionMenuButton')!.getAttribute('aria-label'),
+        nameText + ' ' + autoDetectedText + ' ' + enabledText,
+        'Failed a11y for disconnected but enabled database APN');
+
+    // Enabled database APN, connected.
+    apnListItem.apn = createTestApnWithOverridenValues({
+      accessPointName: apnName,
+    });
+    apnListItem.isApnConnected = true;
+    assertEquals(
+        apnListItem.shadowRoot!
+            .querySelector<CrIconButtonElement>(
+                '#actionMenuButton')!.getAttribute('aria-label'),
+        nameText + ' ' + autoDetectedText + ' ' + connectedText,
+        'Failed a11y for enabled database APN, connected');
+
+    // Null APN, connected.
+    apnListItem.apn = createTestApnWithOverridenValues({
+      accessPointName: '',
+      name: null,
+    });
+    assertEquals(
+        apnListItem.shadowRoot!
+            .querySelector<CrIconButtonElement>(
+                '#actionMenuButton')!.getAttribute('aria-label'),
+        apnListItem.i18n(
+            'apnA11yName', /*index=*/ 1, /*count=*/ 1,
+            apnListItem.i18n('apnNameModem')) +
+            ' ' + autoDetectedText + ' ' + connectedText,
+        'Failed a11y for null APN');
+
+    // User friendly APN name same as APN has no text indicating as such.
+    apnListItem.apn = createTestApnWithOverridenValues({
+      id: apnId,
+      accessPointName: apnName,
+      name: apnName,
+    });
+    apnListItem.isApnConnected = true;
+    assertEquals(
+        apnListItem.shadowRoot!
+            .querySelector<CrIconButtonElement>(
+                '#actionMenuButton')!.getAttribute('aria-label'),
+        nameText + ' ' + connectedText,
+        'Failed a11y for user friendly APN name same as APN');
+
+    // User friendly APN name different from APN has indicating text.
+    const userFriendlyNameText = apnListItem.i18n(
+        'apnA11yName', /*index=*/ 1, /*count=*/ 1,
+        /*name=*/ 'userFriendlyNameApn1');
+    apnListItem.apn = createTestApnWithOverridenValues({
+      id: apnId,
+      accessPointName: apnName,
+      name: apnUserFriendlyName,
+    });
+
+    const indicateUserFriendlyNameText = apnListItem.i18n(
+        'apnA11yUserFriendlyNameIndicator', apnUserFriendlyName, apnName);
+    assertEquals(
+        apnListItem.shadowRoot!
+            .querySelector<CrIconButtonElement>(
+                '#actionMenuButton')!.getAttribute('aria-label'),
+        userFriendlyNameText + ' ' + connectedText + ' ' +
+            indicateUserFriendlyNameText,
+        'Failed a11y for user friendly APN name different from APN');
+  });
+});
diff --git a/chrome/test/data/webui/chromeos/network/apn_list_test.ts b/chrome/test/data/webui/chromeos/network/apn_list_test.ts
index f76541e7..08d0c763 100644
--- a/chrome/test/data/webui/chromeos/network/apn_list_test.ts
+++ b/chrome/test/data/webui/chromeos/network/apn_list_test.ts
@@ -288,7 +288,7 @@
     assertEquals(1, apns.length);
     assertTrue(!!apns[0]);
     assertTrue(OncMojo.apnMatch(apns[0].apn, connectedApn));
-    assertTrue(apns[0].isConnected);
+    assertTrue(apns[0].isApnConnected);
   });
 
   test('There is no Connected APN and no custom APNs', async () => {
@@ -313,7 +313,7 @@
     assertTrue(!!apns[1]);
     assertTrue(OncMojo.apnMatch(apns[0].apn, customApn1));
     assertTrue(OncMojo.apnMatch(apns[1].apn, customApn2));
-    assertFalse(apns[0].isConnected);
+    assertFalse(apns[0].isApnConnected);
     assertFalse(!!getZeroStateContent());
   });
 
@@ -329,7 +329,7 @@
         assertEquals(1, apns.length);
         assertTrue(!!apns[0]);
         assertTrue(OncMojo.apnMatch(apns[0].apn, connectedApn));
-        assertTrue(apns[0].isConnected);
+        assertTrue(apns[0].isApnConnected);
         assertFalse(!!getZeroStateContent());
       });
 
@@ -349,7 +349,7 @@
         assertTrue(OncMojo.apnMatch(apns[0].apn, connectedApn));
         assertTrue(OncMojo.apnMatch(apns[1].apn, customApn1));
         assertTrue(OncMojo.apnMatch(apns[2].apn, customApn2));
-        assertTrue(apns[0].isConnected);
+        assertTrue(apns[0].isApnConnected);
         assertFalse(!!getZeroStateContent());
       });
 
@@ -367,7 +367,7 @@
     assertTrue(OncMojo.apnMatch(apns[0].apn, customApn3));
     assertTrue(OncMojo.apnMatch(apns[1].apn, customApn1));
     assertTrue(OncMojo.apnMatch(apns[2].apn, customApn2));
-    assertTrue(apns[0].isConnected);
+    assertTrue(apns[0].isApnConnected);
     assertFalse(!!getZeroStateContent());
   });
 
@@ -380,7 +380,7 @@
     assertEquals(1, apns.length);
     assertTrue(!!apns[0]);
     assertTrue(OncMojo.apnMatch(apns[0].apn, connectedApn));
-    assertTrue(apns[0].isConnected);
+    assertTrue(apns[0].isApnConnected);
     assertFalse(!!getZeroStateContent());
 
     // Simulate the APN no longer being connected.
@@ -392,7 +392,7 @@
     assertEquals(1, apns.length);
     assertTrue(!!apns[0]);
     assertTrue(OncMojo.apnMatch(apns[0].apn, connectedApn));
-    assertFalse(apns[0].isConnected);
+    assertFalse(apns[0].isApnConnected);
     assertFalse(!!getZeroStateContent());
   });
 
diff --git a/chrome/test/data/webui/commerce/product_specifications/app_test.ts b/chrome/test/data/webui/commerce/product_specifications/app_test.ts
index b4d2b49..acdfd93 100644
--- a/chrome/test/data/webui/commerce/product_specifications/app_test.ts
+++ b/chrome/test/data/webui/commerce/product_specifications/app_test.ts
@@ -2233,6 +2233,95 @@
     assertEquals(UserFeedback.kUnspecified, feedbackArgs);
   });
 
+  test('product selection menus are closed after set updates', async () => {
+    const exampleUrl = {url: 'https://example.com/'};
+    const dimensionValues = {
+      summary: [],
+      specificationDescriptions: [
+        {
+          label: '',
+          altText: '',
+          options: [],
+        },
+      ],
+    };
+    const dimensionValuesMap = new Map<bigint, ProductSpecificationsValue>(
+        [[BigInt(2), dimensionValues]]);
+    const specsProduct = createSpecsProduct({
+      productClusterId: BigInt(123),
+      title: 'Product',
+      productDimensionValues: dimensionValuesMap,
+    });
+    const info = createProductInfo({
+      clusterId: BigInt(123),
+      title: 'Product',
+      productUrl: exampleUrl,
+      imageUrl: {url: `${exampleUrl.url}'/image.png'`},
+    });
+    const promiseValues = createAppPromiseValues({
+      idParam: testId,
+      specs: createSpecs({
+        productDimensionMap: new Map<bigint, string>([[BigInt(2), 'Title']]),
+        products: [specsProduct],
+      }),
+      productInfos: [info],
+    });
+    const specsSet = createSpecsSet({
+      name: 'table',
+      urls: [exampleUrl],
+      uuid: {value: testId},
+    });
+    shoppingServiceApi.setResultFor(
+        'getProductSpecificationsSetByUuid', Promise.resolve({set: specsSet}));
+    await createAppElementWithPromiseValues(promiseValues);
+
+    // Populate the product selection menu with options.
+    const productTabs = [{
+      title: 'title',
+      url: {url: 'https://example2.com/'},
+    }];
+    shoppingServiceApi.setResultFor(
+        'getUrlInfosForProductTabs', Promise.resolve({urlInfos: productTabs}));
+    shoppingServiceApi.setResultFor(
+        'getUrlInfosForRecentlyViewedTabs', Promise.resolve({urlInfos: []}));
+
+    // Open the new column selection menu.
+    const newColSelector = appElement.$.newColumnSelector;
+    newColSelector.$.button.click();
+    await waitAfterNextRender(appElement);
+
+    let menu = newColSelector.$.productSelectionMenu.$.menu.get();
+    assertTrue(menu.open);
+
+    focusWindowAndTriggerSetUpdate(createSpecsSet({
+      name: 'table',
+      urls: [exampleUrl, {url: 'https://example3.com'}],
+      uuid: {value: testId},
+    }));
+    await waitAfterNextRender(appElement);
+
+    assertFalse(menu.open);
+
+    // Open one of the product selection menus from a selector.
+    const productSelector = $$<ProductSelectorElement>(
+        appElement.$.summaryTable, 'product-selector');
+    assertTrue(!!productSelector);
+    productSelector.$.currentProductContainer.click();
+    await waitAfterNextRender(appElement);
+
+    menu = productSelector.$.productSelectionMenu.$.menu.get();
+    assertTrue(menu.open);
+
+    focusWindowAndTriggerSetUpdate(createSpecsSet({
+      name: 'table',
+      urls: [exampleUrl, {url: 'https://example4.com'}],
+      uuid: {value: testId},
+    }));
+    await waitAfterNextRender(appElement);
+
+    assertFalse(menu.open);
+  });
+
   suite('FeatureState', () => {
     test('feedback hidden if not allowed', async () => {
       shoppingServiceApi.setResultFor(
diff --git a/chrome/test/data/webui/commerce/product_specifications/comparison_table_list_item_test.ts b/chrome/test/data/webui/commerce/product_specifications/comparison_table_list_item_test.ts
index b1e97844..8994db70 100644
--- a/chrome/test/data/webui/commerce/product_specifications/comparison_table_list_item_test.ts
+++ b/chrome/test/data/webui/commerce/product_specifications/comparison_table_list_item_test.ts
@@ -11,8 +11,9 @@
 import type {CrInputElement} from '//resources/cr_elements/cr_input/cr_input.js';
 import {PluralStringProxyImpl} from '//resources/js/plural_string_proxy.js';
 import type {ComparisonTableListItemElement} from 'chrome://compare/comparison_table_list_item.js';
+import {ShowSetDisposition} from 'chrome://compare/product_specifications.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {assertEquals, assertFalse, assertStringContains, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {assertArrayEquals, assertEquals, assertFalse, assertStringContains, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestPluralStringProxy} from 'chrome://webui-test/test_plural_string_proxy.js';
 import {$$, eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
@@ -117,6 +118,24 @@
     });
 
     test(
+        'open in new window option opens the table in a new window',
+        async () => {
+          const openInNewWindowButton =
+              menu.querySelector<HTMLButtonElement>('#openInNewWindow');
+          assertTrue(!!openInNewWindowButton);
+          openInNewWindowButton.click();
+
+          assertEquals(
+              1,
+              productSpecsProxy.getCallCount(
+                  'showProductSpecificationsSetsForUuids'));
+          const args = productSpecsProxy.getArgs(
+              'showProductSpecificationsSetsForUuids')[0];
+          assertArrayEquals([TABLE_UUID], args[0]);
+          assertEquals(ShowSetDisposition.kInNewWindow, args[1]);
+        });
+
+    test(
         'rename option displays input, and submitting emits event with UUID ' +
             'and name',
         async () => {
diff --git a/chrome/test/data/webui/commerce/product_specifications/comparison_table_list_test.ts b/chrome/test/data/webui/commerce/product_specifications/comparison_table_list_test.ts
index d656da9..2bd7ceed 100644
--- a/chrome/test/data/webui/commerce/product_specifications/comparison_table_list_test.ts
+++ b/chrome/test/data/webui/commerce/product_specifications/comparison_table_list_test.ts
@@ -10,7 +10,8 @@
 import {PluralStringProxyImpl} from '//resources/js/plural_string_proxy.js';
 import type {ComparisonTableListElement} from 'chrome://compare/comparison_table_list.js';
 import type {ComparisonTableListItemElement} from 'chrome://compare/comparison_table_list_item.js';
-import {assertEquals, assertFalse, assertStringContains, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {ShowSetDisposition} from 'chrome://compare/product_specifications.mojom-webui.js';
+import {assertArrayEquals, assertEquals, assertFalse, assertStringContains, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
 import {TestPluralStringProxy} from 'chrome://webui-test/test_plural_string_proxy.js';
 import {$$, eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
@@ -157,19 +158,32 @@
         await openAllFinishedPromise;
 
         assertEquals(
-            2,
+            1,
             productSpecsProxy.getCallCount(
-                'showProductSpecificationsSetForUuid'));
-        const firstCallArgs =
-            productSpecsProxy.getArgs('showProductSpecificationsSetForUuid')[0];
-        const secondCallArgs =
-            productSpecsProxy.getArgs('showProductSpecificationsSetForUuid')[1];
-        assertEquals(TABLES[0]!.uuid, firstCallArgs[0]);
+                'showProductSpecificationsSetsForUuids'));
+        const args = productSpecsProxy.getArgs(
+            'showProductSpecificationsSetsForUuids')[0];
+        assertArrayEquals([TABLES[0]!.uuid, TABLES[1]!.uuid], args[0]);
+        assertEquals(ShowSetDisposition.kInNewTabs, args[1]);
+      });
+
+      test('can open multiple comparison tables in a new window', async () => {
+        const openAllInNewWindowFinishedPromise = eventToPromise(
+            'open-all-in-new-window-finished-for-testing', listElement);
+        const openAllInNewWindowButton =
+            menu.querySelector<HTMLButtonElement>('#menuOpenAllInNewWindow');
+        assertTrue(!!openAllInNewWindowButton);
+        openAllInNewWindowButton.click();
+        await openAllInNewWindowFinishedPromise;
+
         assertEquals(
-            /*inNewTab=*/ true, firstCallArgs[1]);
-        assertEquals(TABLES[1]!.uuid, secondCallArgs[0]);
-        assertEquals(
-            /*inNewTab=*/ true, secondCallArgs[1]);
+            1,
+            productSpecsProxy.getCallCount(
+                'showProductSpecificationsSetsForUuids'));
+        const args = productSpecsProxy.getArgs(
+            'showProductSpecificationsSetsForUuids')[0];
+        assertArrayEquals([TABLES[0]!.uuid, TABLES[1]!.uuid], args[0]);
+        assertEquals(ShowSetDisposition.kInNewWindow, args[1]);
       });
 
       test('can delete multiple comparison tables', async () => {
diff --git a/chrome/test/data/webui/commerce/product_specifications/new_column_selector_test.ts b/chrome/test/data/webui/commerce/product_specifications/new_column_selector_test.ts
index 59cc5f5..261eec8 100644
--- a/chrome/test/data/webui/commerce/product_specifications/new_column_selector_test.ts
+++ b/chrome/test/data/webui/commerce/product_specifications/new_column_selector_test.ts
@@ -14,6 +14,7 @@
 suite('NewColumnSelectorTest', () => {
   const shoppingServiceApi =
       TestMock.fromClass(ShoppingServiceBrowserProxyImpl);
+  const SHOWING_MENU_CLASS = 'showing-menu';
 
   async function createSelector(): Promise<NewColumnSelectorElement> {
     const selector = document.createElement('new-column-selector');
@@ -47,35 +48,33 @@
   test('menu shown on enter', async () => {
     initUrlInfos();
     const selector = await createSelector();
-    const showingMenuClass = 'showing-menu';
     const menu = selector.$.productSelectionMenu;
 
     assertEquals(menu.$.menu.getIfExists(), null);
-    assertFalse(selector.$.button.classList.contains(showingMenuClass));
+    assertFalse(selector.$.button.classList.contains(SHOWING_MENU_CLASS));
 
     selector.$.button.dispatchEvent(
         new KeyboardEvent('keydown', {key: 'Enter'}));
     await microtasksFinished();
 
     assertNotEquals(menu.$.menu.getIfExists(), null);
-    assertTrue(selector.$.button.classList.contains(showingMenuClass));
+    assertTrue(selector.$.button.classList.contains(SHOWING_MENU_CLASS));
   });
 
   test('updates showing menu class', async () => {
     initUrlInfos();
     const selector = await createSelector();
-    const showingMenuClass = 'showing-menu';
 
-    assertFalse(selector.$.button.classList.contains(showingMenuClass));
+    assertFalse(selector.$.button.classList.contains(SHOWING_MENU_CLASS));
 
     selector.$.button.click();
     await microtasksFinished();
 
-    assertTrue(selector.$.button.classList.contains(showingMenuClass));
+    assertTrue(selector.$.button.classList.contains(SHOWING_MENU_CLASS));
 
     selector.$.productSelectionMenu.close();
     await eventToPromise('close', selector.$.productSelectionMenu);
 
-    assertFalse(selector.$.button.classList.contains(showingMenuClass));
+    assertFalse(selector.$.button.classList.contains(SHOWING_MENU_CLASS));
   });
 });
diff --git a/chrome/test/data/webui/commerce/product_specifications/test_product_specifications_browser_proxy.ts b/chrome/test/data/webui/commerce/product_specifications/test_product_specifications_browser_proxy.ts
index f3adc21..191b7b3 100644
--- a/chrome/test/data/webui/commerce/product_specifications/test_product_specifications_browser_proxy.ts
+++ b/chrome/test/data/webui/commerce/product_specifications/test_product_specifications_browser_proxy.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import {PageCallbackRouter} from '//resources/cr_components/commerce/product_specifications.mojom-webui.js';
-import type {DisclosureVersion, PageRemote} from '//resources/cr_components/commerce/product_specifications.mojom-webui.js';
+import type {DisclosureVersion, PageRemote, ShowSetDisposition} from '//resources/cr_components/commerce/product_specifications.mojom-webui.js';
 import type {ProductSpecificationsBrowserProxy} from '//resources/cr_components/commerce/product_specifications_browser_proxy.js';
 import type {Uuid} from '//resources/mojo/mojo/public/mojom/base/uuid.mojom-webui.js';
 import type {Url} from '//resources/mojo/url/mojom/url.mojom-webui.js';
@@ -17,6 +17,7 @@
   constructor() {
     super([
       'showProductSpecificationsSetForUuid',
+      'showProductSpecificationsSetsForUuids',
       'showComparePage',
       'setAcceptedDisclosureVersion',
       'maybeShowDisclosure',
@@ -44,6 +45,12 @@
     this.methodCalled('showProductSpecificationsSetForUuid', uuid, inNewTab);
   }
 
+  showProductSpecificationsSetsForUuids(
+      uuids: Uuid[], disposition: ShowSetDisposition): void {
+    this.methodCalled(
+        'showProductSpecificationsSetsForUuids', uuids, disposition);
+  }
+
   showComparePage(inNewTab: boolean): void {
     this.methodCalled('showComparePage', inNewTab);
   }
diff --git a/chrome/test/data/webui/cr_components/chromeos/BUILD.gn b/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
index 4f01106..63d42d8 100644
--- a/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/chromeos/BUILD.gn
@@ -46,7 +46,7 @@
     "cellular_setup/provisioning_page_test.ts",
     "cellular_setup/psim_flow_ui_test.ts",
     "cellular_setup/setup_loading_page_test.ts",
-    "network/apn_list_item_test.js",
+    "network/apn_list_item_test.ts",
     "network/apn_list_test.ts",
     "network/apn_selection_dialog_test.js",
     "network/apn_selection_dialog_list_item_test.js",
diff --git a/chrome/test/data/webui/glic/BUILD.gn b/chrome/test/data/webui/glic/BUILD.gn
index 46c45ec2..00ea75e 100644
--- a/chrome/test/data/webui/glic/BUILD.gn
+++ b/chrome/test/data/webui/glic/BUILD.gn
@@ -16,8 +16,15 @@
 
 ts_library("build_ts") {
   out_dir = out_dir
-  in_files = [ "api_test.ts" ]
-  deps = [ "//chrome/browser/resources/glic:build_ts" ]
+  in_files = [
+    "api_test.ts",
+    "api_boot.ts",
+    "test_client/test_client.ts",
+  ]
+  deps = [
+    "//chrome/browser/resources/glic:build_ts",
+    "//ui/webui/resources/js:build_ts",
+  ]
 
   path_mappings = [ "chrome://glic/*|" + rebase_path(
                         "$root_gen_dir/chrome/browser/resources/glic/tsc/*",
@@ -25,6 +32,9 @@
 }
 
 preprocess_if_expr("move_html") {
-  in_files = [ "test.html" ]
+  in_files = [
+    "test.html",
+    "test_client/index.html",
+  ]
   out_folder = out_dir
 }
diff --git a/chrome/test/data/webui/glic/api_boot.ts b/chrome/test/data/webui/glic/api_boot.ts
new file mode 100644
index 0000000..e9732af
--- /dev/null
+++ b/chrome/test/data/webui/glic/api_boot.ts
@@ -0,0 +1,39 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import type {GlicApiBootMessage, GlicHostRegistry, WithGlicApi} from 'chrome://glic/glic_api/glic_api.js';
+
+// Note: Although the actual glic client does something similar, this code is
+// test-only.
+
+/**
+ * Creates a GlicHostRegistry by loading the glic API script from Chrome and
+ * calling the `internalAutoGlicBoot` function. This should be called on or
+ * before page load. The returned promise never resolves if a message from
+ * Chrome is not received.
+ */
+export function createGlicHostRegistryOnLoad(): Promise<GlicHostRegistry> {
+  return new Promise<GlicHostRegistry>(resolve => {
+    function messageHandler(event: MessageEvent) {
+      // Important: only accept messages from Chrome browser's glic WebUI.
+      if (event.origin !== 'chrome://glic' || event.source === null) {
+        return;
+      }
+      const bootMessage = event.data as GlicApiBootMessage;
+      if (bootMessage?.type === 'glic-bootstrap' && bootMessage.glicApiSource) {
+        window.removeEventListener('message', messageHandler);
+        const glicApiSource = bootMessage.glicApiSource;
+        const scriptElement = document.createElement('script');
+        scriptElement.textContent = glicApiSource;
+        document.head.appendChild(scriptElement);
+        const bootFunc = (window as WithGlicApi).internalAutoGlicBoot;
+        if (!bootFunc) {
+          throw new Error('Glic client import failed.');
+        }
+        resolve(bootFunc(event.source as WindowProxy));
+      }
+    }
+    window.addEventListener('message', messageHandler);
+  });
+}
diff --git a/chrome/test/data/webui/glic/api_test.ts b/chrome/test/data/webui/glic/api_test.ts
index e6b0bc3..28035ca 100644
--- a/chrome/test/data/webui/glic/api_test.ts
+++ b/chrome/test/data/webui/glic/api_test.ts
@@ -6,33 +6,9 @@
 // ash/webui/personalization_app/tools/gen_tsconfig.py --root_out_dir out/pc \
 //   --gn_target chrome/test/data/webui/glic:build_ts
 
-import type {GlicApiBootMessage, GlicBrowserHost, GlicHostRegistry, GlicWebClient, PanelState, WithGlicApi} from 'chrome://glic/glic_api/glic_api.js';
+import type {GlicBrowserHost, GlicWebClient, PanelState} from 'chrome://glic/glic_api/glic_api.js';
 
-// Glic API bootstrap code.
-function createGlicHostRegistryOnLoad(): Promise<GlicHostRegistry> {
-  return new Promise<GlicHostRegistry>(resolve => {
-    function messageHandler(event: MessageEvent) {
-      // Important: only accept messages from Chrome browser's glic WebUI.
-      if (event.origin !== 'chrome://glic' || event.source === null) {
-        return;
-      }
-      const bootMessage = event.data as GlicApiBootMessage;
-      if (bootMessage?.type === 'glic-bootstrap' && bootMessage.glicApiSource) {
-        window.removeEventListener('message', messageHandler);
-        const glicApiSource = bootMessage.glicApiSource;
-        const scriptElement = document.createElement('script');
-        scriptElement.textContent = glicApiSource;
-        document.head.appendChild(scriptElement);
-        const bootFunc = (window as WithGlicApi).internalAutoGlicBoot;
-        if (!bootFunc) {
-          throw new Error('Glic client import failed.');
-        }
-        resolve(bootFunc(event.source as WindowProxy));
-      }
-    }
-    window.addEventListener('message', messageHandler);
-  });
-}
+import {createGlicHostRegistryOnLoad} from './api_boot.js';
 
 // A dummy web client.
 class WebClient implements GlicWebClient {
diff --git a/chrome/test/data/webui/glic/test_client/README.md b/chrome/test/data/webui/glic/test_client/README.md
new file mode 100644
index 0000000..0a928e52
--- /dev/null
+++ b/chrome/test/data/webui/glic/test_client/README.md
@@ -0,0 +1,20 @@
+# Glic test client
+
+This is a test page that facilitates testing the API between the host and the web client.
+
+
+## Instructions
+
+1. Build it
+
+```bash
+   autoninja -C out/Default chrome/test/data/webui/glic:generate_test_files
+```
+
+2. Serve it
+
+```bash
+   python3 -m http.server -d out/Default/gen/chrome/test/data/webui/glic
+```
+
+3. Run Chrome with `--glic-guest-url=http://localhost:8000/glic/test_client/index.html`
diff --git a/chrome/test/data/webui/glic/test_client/index.html b/chrome/test/data/webui/glic/test_client/index.html
new file mode 100644
index 0000000..d1f4c91
--- /dev/null
+++ b/chrome/test/data/webui/glic/test_client/index.html
@@ -0,0 +1,171 @@
+<!--
+Copyright 2025 The Chromium Authors
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+-->
+<!doctype html>
+<meta charset="utf-8" />
+
+<style>
+  #pageHeader.connected {
+    background-color: green;
+  }
+  #pageHeader {
+    background-color: blue;
+    color: white;
+    display: flex;
+    justify-content: space-between;
+  }
+  .section h1 {
+    text-align: center;
+    margin: 0.2em;
+    font-size: 15pt;
+  }
+  .section {
+    background-color: #eee;
+    box-shadow: 0 0 0.5em #999;
+    border: 1px solid black;
+    padding: 3px;
+    margin-top: 0.5em;
+    margin-bottom: 0.5em;
+  }
+  body {
+    background-color: white;
+  }
+  .permission-switch {
+    display: flex;
+    align-items: center;
+    margin-bottom: 0.5em;
+  }
+  .permission-switch label {
+    margin-right: 0.5em;
+  }
+  input[type="text"] {
+    width: 400px;
+  }
+  img {
+    border: thick double black;
+    background-image: linear-gradient(cyan, fuchsia);
+  }
+</style>
+<div id="pageHeader">
+  <h2>Test Web Client</h2>
+  <button id="refreshbn">🔄</button>
+  <button id="closebn">❌</button>
+</div>
+
+<div class="section">
+  <h1>Test SetPermissionState() and NotifyPermissionChanged()</h1>
+  <div class="permission-switches">
+    <div class="permission-switch">
+      <label for="microphoneSwitch">Microphone:</label>
+      <input type="checkbox" id="microphoneSwitch" disabled />
+    </div>
+    <div class="permission-switch">
+      <label for="geolocationSwitch">Geolocation:</label>
+      <input type="checkbox" id="geolocationSwitch" disabled />
+    </div>
+    <div class="permission-switch">
+      <label for="tabContextSwitch">Tab Context:</label>
+      <input type="checkbox" id="tabContextSwitch" disabled />
+    </div>
+  </div>
+  <div>
+    <select id="permissionSelect">
+      <option value="microphone">Microphone</option>
+      <option value="geolocation">Geolocation</option>
+      <option value="tab_context">Tab Context</option>
+    </select>
+    <select id="enabledSelect">
+      <option value="true">True</option>
+      <option value="false">False</option>
+    </select>
+    <button id="testPermissionSwitch">Simulate External Permission
+      Update</button>
+  </div>
+</div>
+<div class="section">
+  <h1>Focused Tab</h1>
+  <label>Focused Tab Url: </label><input id="focusedUrl" type="text" /> <br />
+  <label>Favicon: </label><img id="focusedFavicon" />
+</div>
+<div class="section">
+  <h1>Test createTab()</h1>
+  <label>Url</label><input id="URL" type="text"
+      value="https://news.ycombinator.com" />
+  <br />
+  <button id="newtabbn">Open New Tab!</button>
+  <br />
+</div>
+<div class="section">
+  <h1>Test Panel Controls</h1>
+  <button id="attachpanelbn">Attach Panel</button>
+  <br />
+  <button id="detachpanelbn">Detach Panel</button>
+  <br />
+</div>
+<div class="section">
+  <h1>Test getContextFromFocusedTab()</h1>
+  <div>
+    <button id="getpagecontext">Get page Context</button>
+    <label> <input type="checkbox" id="innerTextCheckbox" /> Inner Text </label>
+    <label> <input type="checkbox"
+        id="viewportScreenshotCheckbox" /> Viewport Screenshot </label>
+    <img id="faviconImg" />
+  </div>
+  <div id="screenshot">
+    <img />
+  </div>
+</div>
+<div class="section">
+  <h1>Test Location Access</h1>
+  <button id="getlocation">Get Location</button> <br />
+  <div id="location"></div>
+</div>
+<div class="section">
+  <h1>Test Link Out Behavior</h1>
+  <a href="https://www.example.com" id="link" target="_blank"
+    rel="noopener noreferrer"
+    >https://www.example.com
+  </a>
+</div>
+<div class="section">
+  <h1>UserProfile</h1>
+  <div>
+    <button id="getUserProfileInfoBn">getUserProfileInfo()</button> Status:
+    <span id="getUserProfileInfoStatus"></span>
+    <img id="getUserProfileInfoImg" />
+  </div>
+  <div>
+    <button id="changeProfileBn">changeProfile()</button>
+  </div>
+</div>
+<div class="section">
+  <h1>Misc</h1>
+  <button id="syncCookiesBn">refreshSignInCookies()</button> Status:
+  <span id="syncCookieStatus"></span>
+</div>
+<div class="section">
+  <h1>Demo Test Utils</h1>
+  <button id="reloadpage">Reload page</button>
+  <div>
+    Navigate webview to <input id="navigateWebviewUrl" type="text"
+      value="https://www.google.com" />
+  </div>
+</div>
+<div class="section">
+  <h1>Audio Capture</h1>
+  <button id="audioCapStart">START</button>
+  <button id="audioCapStop">STOP</button>
+
+  After pressing STOP, this will play recorded sound.
+  <br />
+  <audio id="mic" controls></audio><br />
+  <div></div>
+  <div id="audioStatus"></div>
+</div>
+<br />
+<div id="status"></div>
+
+<script src="./test_client.js" type="module">
+</script>
diff --git a/chrome/test/data/webui/glic/test_client/test_client.ts b/chrome/test/data/webui/glic/test_client/test_client.ts
new file mode 100644
index 0000000..539db7c
--- /dev/null
+++ b/chrome/test/data/webui/glic/test_client/test_client.ts
@@ -0,0 +1,309 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import type {GlicBrowserHost, GlicWebClient, TabData} from 'chrome://glic/glic_api/glic_api.js';
+
+import {createGlicHostRegistryOnLoad} from '../api_boot.js';
+
+interface PageElementTypes {
+  status: HTMLDivElement;
+  pageHeader: HTMLDivElement;
+  focusedFavicon: HTMLImageElement;
+  focusedUrl: HTMLInputElement;
+  syncCookiesBn: HTMLButtonElement;
+  syncCookieStatus: HTMLSpanElement;
+  getUserProfileInfoBn: HTMLButtonElement;
+  getUserProfileInfoStatus: HTMLSpanElement;
+  getUserProfileInfoImg: HTMLImageElement;
+  changeProfileBn: HTMLButtonElement;
+  testPermissionSwitch: HTMLButtonElement;
+  microphoneSwitch: HTMLInputElement;
+  geolocationSwitch: HTMLInputElement;
+  tabContextSwitch: HTMLInputElement;
+  newtabbn: HTMLButtonElement;
+  reloadpage: HTMLButtonElement;
+  getpagecontext: HTMLButtonElement;
+  URL: HTMLInputElement;
+  innerTextCheckbox: HTMLInputElement;
+  viewportScreenshotCheckbox: HTMLInputElement;
+  screenshotImg: HTMLImageElement;
+  faviconImg: HTMLImageElement;
+  getlocation: HTMLButtonElement;
+  location: HTMLDivElement;
+  permissionSelect: HTMLSelectElement;
+  enabledSelect: HTMLSelectElement;
+  closebn: HTMLButtonElement;
+  attachpanelbn: HTMLButtonElement;
+  detachpanelbn: HTMLButtonElement;
+  refreshbn: HTMLButtonElement;
+  navigateWebviewUrl: HTMLInputElement;
+  audioCapStop: HTMLButtonElement;
+  audioCapStart: HTMLButtonElement;
+  audioStatus: HTMLDivElement;
+  mic: HTMLAudioElement;
+}
+
+const $: PageElementTypes = new Proxy({}, {
+  get(_target: any, prop: string) {
+    return document.getElementById(prop);
+  },
+});
+
+function logMessage(message: string) {
+  $.status.append(message.slice(0, 100000), document.createElement('br'));
+}
+
+class WebClient implements GlicWebClient {
+  browser: GlicBrowserHost|undefined;
+
+  async initialize(browser: GlicBrowserHost): Promise<void> {
+    this.browser = browser;
+    logMessage('initialize called');
+    $.pageHeader!.classList.add('connected');
+
+    const ver = await browser.getChromeVersion();
+    logMessage(`Chrome version: ${JSON.stringify(ver)}`);
+
+    const focusedTabState = await this.browser.getFocusedTabState!();
+    focusedTabChanged(focusedTabState.getValue());
+    focusedTabState.subscribe(focusedTabChanged);
+  }
+
+  async notifyPanelClosed() {
+    logMessage('notifyPanelClosed called');
+  }
+}
+
+const client = new WebClient();
+
+function getBrowser(): GlicBrowserHost|undefined {
+  return client?.browser;
+}
+
+async function focusedTabChanged(newValue: TabData|undefined) {
+  $.focusedUrl.value = '';
+  $.focusedFavicon.src = '';
+  logMessage(`Focused Tab State Changed: ${JSON.stringify(newValue)}`);
+  if (newValue?.url) {
+    $.focusedUrl.value = newValue.url;
+  }
+  if (newValue?.favicon) {
+    const fav = await newValue.favicon();
+    if (fav) {
+      $.focusedFavicon.src = URL.createObjectURL(fav);
+    }
+  }
+}
+
+createGlicHostRegistryOnLoad().then((registry) => {
+  logMessage('registring web client');
+  registry.registerWebClient(client);
+});
+
+type PermissionSwitchName = 'microphone'|'geolocation'|'tabContext';
+const permissionSwitches: Record<PermissionSwitchName, HTMLInputElement> = {
+  microphone: $.microphoneSwitch,
+  geolocation: $.geolocationSwitch,
+  tabContext: $.tabContextSwitch,
+};
+
+function updatePermissionSwitch(
+    permissionSwitchName: PermissionSwitchName, enabled: boolean,
+    sendToBackend = true) {
+  logMessage(
+      `Setting permission ${permissionSwitchName} to ${enabled} and ${
+          sendToBackend ? '' : 'not'} sending to backend.`,
+  );
+  if (!permissionSwitches[permissionSwitchName]) {
+    console.error('Permission switch not found: ' + permissionSwitchName);
+    return;
+  }
+  permissionSwitches[permissionSwitchName].checked = enabled;
+}
+
+$.syncCookiesBn.addEventListener('click', async () => {
+  $.syncCookieStatus!.innerText = 'Requesting';
+  try {
+    await getBrowser()!.refreshSignInCookies!();
+    $.syncCookieStatus!.innerText = `Done!`;
+  } catch (e) {
+    $.syncCookieStatus!.innerText = `Caught error: ${e}`;
+  }
+});
+
+$.getUserProfileInfoBn.addEventListener('click', async () => {
+  $.getUserProfileInfoStatus.innerText = 'Requesting';
+  try {
+    const profile = await getBrowser()!.getUserProfileInfo!();
+    $.getUserProfileInfoStatus.innerText = `Done: ${JSON.stringify(profile)}`;
+    const icon = await profile.avatarIcon();
+    if (icon) {
+      $.getUserProfileInfoImg.src = URL.createObjectURL(icon);
+    }
+  } catch (e) {
+    $.getUserProfileInfoStatus.innerText = `Caught error: ${e}`;
+  }
+});
+
+$.changeProfileBn.addEventListener('click', () => {
+  getBrowser()!.showProfilePicker!();
+});
+
+// Listen for switch changes by user in webclient:
+for (const key of Object.keys(permissionSwitches) as PermissionSwitchName[]) {
+  const element = permissionSwitches[key];
+  element.addEventListener('change', () => {
+    updatePermissionSwitch(key, element.checked);
+  });
+}
+
+// Add listeners to demo elements:
+$.newtabbn.addEventListener('click', async () => {
+  const url = $.URL.value;
+  await getBrowser()!.createTab!(url, {});
+  logMessage('createTab done');
+});
+
+$.reloadpage.addEventListener('click', () => {
+  location.reload();
+});
+
+$.getpagecontext.addEventListener('click', async () => {
+  logMessage('Starting Get Page Context');
+  const options: any = {};
+  if ($.innerTextCheckbox.checked) {
+    options.innerText = true;
+  }
+  if ($.viewportScreenshotCheckbox.checked) {
+    options.viewportScreenshot = {};
+  }
+  try {
+    const pageContent =
+        await client!.browser!.getContextFromFocusedTab!(options);
+    if (pageContent.viewportScreenshot) {
+      const blob =
+          new Blob([pageContent.viewportScreenshot.data], {type: 'image/jpeg'});
+      $.screenshotImg.src = URL.createObjectURL(blob);
+    }
+    if (pageContent.tabData.favicon) {
+      const favicon = await pageContent.tabData.favicon();
+      if (favicon) {
+        $.faviconImg.src = URL.createObjectURL(favicon);
+      }
+    }
+    logMessage(
+        `Finished Get Page Context. Returned data: ${
+            JSON.stringify(pageContent, null, 2)}`,
+    );
+  } catch (error) {
+    logMessage(`Error getting page context: ${error}`);
+  }
+});
+$.getlocation.addEventListener('click', async () => {
+  logMessage('Requesting geolocation...');
+
+  if (navigator.geolocation) {
+    try {
+      const position =
+          await new Promise<GeolocationPosition>((resolve, reject) => {
+            navigator.geolocation.getCurrentPosition(resolve, reject);
+          });
+
+      const latitude = position.coords.latitude;
+      const longitude = position.coords.longitude;
+      const accuracy = position.coords.accuracy;
+
+      $.location.innerHTML = `
+          Latitude: ${latitude}<br>
+          Longitude: ${longitude}<br>
+          Accuracy: ${accuracy} meters
+        `;
+      logMessage(
+          `Geolocation obtained: Latitude ${latitude}, Longitude ${longitude}`);
+    } catch (error) {
+      if (error instanceof Error) {
+        logMessage(`Error getting geolocation: ${error.message}`);
+        $.location.innerHTML = `Error: ${error.message}`;
+      }
+    }
+  } else {
+    logMessage('Geolocation is not supported by this browser.');
+    $.location.innerHTML = 'Geolocation is not supported by this browser.';
+  }
+});
+$.testPermissionSwitch.addEventListener('click', () => {
+  const selectedPermission = $.permissionSelect.value;
+  const isEnabled = $.enabledSelect.value === 'true';
+  updatePermissionSwitch(selectedPermission as any, isEnabled, false);
+});
+
+$.closebn.addEventListener('click', () => {
+  getBrowser()!.closePanel!();
+});
+$.attachpanelbn.addEventListener('click', () => {
+  getBrowser()!.attachPanel!();
+});
+$.detachpanelbn.addEventListener('click', () => {
+  getBrowser()!.detachPanel!();
+});
+$.refreshbn.addEventListener('click', () => {
+  location.reload();
+});
+$.navigateWebviewUrl.addEventListener('keyup', ({key}) => {
+  if (key === 'Enter') {
+    window.location.href = $.navigateWebviewUrl.value;
+  }
+});
+
+
+class AudioCapture {
+  recordedData: Blob[] = [];
+  recorder: MediaRecorder|undefined;
+  constructor() {}
+
+  async start() {
+    if (this.recorder) {
+      return;
+    }
+    const stream = await navigator.mediaDevices.getUserMedia({audio: true});
+
+    $.audioStatus.replaceChildren('Recording...');
+    this.recorder = new MediaRecorder(stream, {mimeType: 'audio/webm'});
+    let stopped = false;
+    window.setInterval(() => {
+      if (!stopped) {
+        this.recorder!.requestData();
+      }
+    }, 100);
+    this.recorder.addEventListener('dataavailable', (event: BlobEvent) => {
+      this.recordedData.push(event.data);
+    });
+    this.recorder.addEventListener('stop', () => {
+      stopped = true;
+      $.audioStatus.replaceChildren('Playback...');
+      const blob = new Blob(this.recordedData, {type: 'audio/webm'});
+      $.mic.src = URL.createObjectURL(blob);
+    });
+    this.recorder.start();
+  }
+
+  stop() {
+    if (!this.recorder) {
+      return;
+    }
+    $.mic.play();
+    this.recorder.stop();
+    this.recorder = undefined;
+  }
+}
+const audioCapture = new AudioCapture();
+
+window.addEventListener('load', () => {
+  $.audioCapStop.addEventListener('click', () => {
+    audioCapture.stop();
+  });
+  $.audioCapStart.addEventListener('click', () => {
+    audioCapture.start();
+  });
+});
diff --git a/chrome/updater/constants.h b/chrome/updater/constants.h
index 5381390..ae94859 100644
--- a/chrome/updater/constants.h
+++ b/chrome/updater/constants.h
@@ -270,7 +270,12 @@
     "server_keep_alive";
 inline constexpr char kDevOverrideKeyCrxVerifierFormat[] =
     "crx_verifier_format";
+inline constexpr char kDevOverrideKeyDictPolicies[] = "dict_policies";
+
+// TODO(crbug.com/389965546): remove this once the checked-in old updater builds
+// recognize "dict_policies".
 inline constexpr char kDevOverrideKeyGroupPolicies[] = "group_policies";
+
 inline constexpr char kDevOverrideKeyOverinstallTimeout[] =
     "overinstall_timeout";
 inline constexpr char kDevOverrideKeyIdleCheckPeriodSeconds[] =
@@ -571,16 +576,30 @@
 inline constexpr bool kInstallPolicyDefault = kPolicyEnabled;
 inline constexpr bool kUpdatePolicyDefault = kPolicyEnabled;
 
-// Policy manager `source()` constants.
+// Policy manager constants.
 inline constexpr char kSourceDMPolicyManager[] = "Device Management";
 inline constexpr char kSourceDefaultValuesPolicyManager[] = "Default";
 inline constexpr char kSourceDictValuesPolicyManager[] = "DictValuePolicy";
 #if BUILDFLAG(IS_WIN)
+inline constexpr bool kPlatformPolicyManagerDefined = true;
 inline constexpr char kSourcePlatformPolicyManager[] = "Group Policy";
+
+// On Windows, by default, Group Policy has a higher priority than the
+// clould policy.
+inline constexpr bool kCloudPolicyOverridesPlatformPolicyDefaultValue = false;
 #elif BUILDFLAG(IS_MAC)
+inline constexpr bool kPlatformPolicyManagerDefined = true;
 inline constexpr char kSourcePlatformPolicyManager[] = "Managed Preferences";
+
+// On macOS, cloud policy has a higher priority than the Managed Preferences.
+inline constexpr bool kCloudPolicyOverridesPlatformPolicyDefaultValue = true;
 #else
+inline constexpr bool kPlatformPolicyManagerDefined = false;
 inline constexpr char kSourcePlatformPolicyManager[] = "not-defined";
+
+// On other platforms, there's no platform policy at the moment, the value
+// doesn't actually matter.
+inline constexpr bool kCloudPolicyOverridesPlatformPolicyDefaultValue = true;
 #endif
 
 // Serializes updater installs. Defined in the .cc file so that the updater
diff --git a/chrome/updater/external_constants.h b/chrome/updater/external_constants.h
index 2846fe60d..efd6dfd 100644
--- a/chrome/updater/external_constants.h
+++ b/chrome/updater/external_constants.h
@@ -58,8 +58,8 @@
   // CRX format verification requirements.
   virtual crx_file::VerifierFormat CrxVerifierFormat() const = 0;
 
-  // Overrides for the `GroupPolicyManager`.
-  virtual base::Value::Dict GroupPolicies() const = 0;
+  // Policies for the `PolicyManager` surfaced by external constants.
+  virtual base::Value::Dict DictPolicies() const = 0;
 
   // Overrides the overinstall timeout.
   virtual base::TimeDelta OverinstallTimeout() const = 0;
diff --git a/chrome/updater/external_constants_builder.cc b/chrome/updater/external_constants_builder.cc
index 7683a44b..2f376e6 100644
--- a/chrome/updater/external_constants_builder.cc
+++ b/chrome/updater/external_constants_builder.cc
@@ -143,13 +143,15 @@
   return *this;
 }
 
-ExternalConstantsBuilder& ExternalConstantsBuilder::SetGroupPolicies(
-    const base::Value::Dict& group_policies) {
-  overrides_.Set(kDevOverrideKeyGroupPolicies, group_policies.Clone());
+ExternalConstantsBuilder& ExternalConstantsBuilder::SetDictPolicies(
+    const base::Value::Dict& dict_policies) {
+  overrides_.Set(kDevOverrideKeyDictPolicies, dict_policies.Clone());
+  overrides_.Set(kDevOverrideKeyGroupPolicies, dict_policies.Clone());
   return *this;
 }
 
-ExternalConstantsBuilder& ExternalConstantsBuilder::ClearGroupPolicies() {
+ExternalConstantsBuilder& ExternalConstantsBuilder::ClearDictPolicies() {
+  overrides_.Remove(kDevOverrideKeyDictPolicies);
   overrides_.Remove(kDevOverrideKeyGroupPolicies);
   return *this;
 }
@@ -266,8 +268,8 @@
   if (!overrides_.contains(kDevOverrideKeyCrxVerifierFormat)) {
     SetCrxVerifierFormat(verifier->CrxVerifierFormat());
   }
-  if (!overrides_.contains(kDevOverrideKeyGroupPolicies)) {
-    SetGroupPolicies(verifier->GroupPolicies());
+  if (!overrides_.contains(kDevOverrideKeyDictPolicies)) {
+    SetDictPolicies(verifier->DictPolicies());
   }
   if (!overrides_.contains(kDevOverrideKeyOverinstallTimeout)) {
     SetOverinstallTimeout(verifier->OverinstallTimeout());
diff --git a/chrome/updater/external_constants_builder.h b/chrome/updater/external_constants_builder.h
index d868278..a00657d 100644
--- a/chrome/updater/external_constants_builder.h
+++ b/chrome/updater/external_constants_builder.h
@@ -65,9 +65,9 @@
       crx_file::VerifierFormat crx_verifier_format);
   ExternalConstantsBuilder& ClearCrxVerifierFormat();
 
-  ExternalConstantsBuilder& SetGroupPolicies(
-      const base::Value::Dict& group_policies);
-  ExternalConstantsBuilder& ClearGroupPolicies();
+  ExternalConstantsBuilder& SetDictPolicies(
+      const base::Value::Dict& dict_policies);
+  ExternalConstantsBuilder& ClearDictPolicies();
 
   ExternalConstantsBuilder& SetOverinstallTimeout(
       base::TimeDelta overinstall_timeout);
diff --git a/chrome/updater/external_constants_builder_unittest.cc b/chrome/updater/external_constants_builder_unittest.cc
index fdb31e9..2e80766 100644
--- a/chrome/updater/external_constants_builder_unittest.cc
+++ b/chrome/updater/external_constants_builder_unittest.cc
@@ -58,13 +58,13 @@
 
   EXPECT_EQ(verifier->InitialDelay(), kInitialDelay);
   EXPECT_EQ(verifier->ServerKeepAliveTime(), kServerKeepAliveTime);
-  EXPECT_EQ(verifier->GroupPolicies().size(), 0U);
+  EXPECT_EQ(verifier->DictPolicies().size(), 0U);
 }
 
 TEST_F(ExternalConstantsBuilderTests, TestOverridingEverything) {
-  base::Value::Dict group_policies;
-  group_policies.Set("a", 1);
-  group_policies.Set("b", 2);
+  base::Value::Dict dict_policies;
+  dict_policies.Set("a", 1);
+  dict_policies.Set("b", 2);
 
   ExternalConstantsBuilder builder;
   builder.SetUpdateURL(std::vector<std::string>{"https://www.example.com"})
@@ -74,7 +74,7 @@
       .SetUseCUP(false)
       .SetInitialDelay(base::Seconds(123))
       .SetServerKeepAliveTime(base::Seconds(2))
-      .SetGroupPolicies(group_policies)
+      .SetDictPolicies(dict_policies)
       .SetOverinstallTimeout(base::Seconds(3))
       .SetIdleCheckPeriod(base::Seconds(4))
       .SetMachineManaged(std::make_optional(true))
@@ -97,7 +97,7 @@
   EXPECT_EQ(verifier->AppLogoURL(), GURL("https://applogo.example.com/"));
   EXPECT_EQ(verifier->InitialDelay(), base::Seconds(123));
   EXPECT_EQ(verifier->ServerKeepAliveTime(), base::Seconds(2));
-  EXPECT_EQ(verifier->GroupPolicies().size(), 2U);
+  EXPECT_EQ(verifier->DictPolicies().size(), 2U);
   EXPECT_EQ(verifier->OverinstallTimeout(), base::Seconds(3));
   EXPECT_EQ(verifier->IdleCheckPeriod(), base::Seconds(4));
   EXPECT_TRUE(verifier->IsMachineManaged().has_value());
@@ -130,7 +130,7 @@
   EXPECT_EQ(verifier->AppLogoURL(), GURL(APP_LOGO_URL));
   EXPECT_EQ(verifier->InitialDelay(), kInitialDelay);
   EXPECT_EQ(verifier->ServerKeepAliveTime(), kServerKeepAliveTime);
-  EXPECT_EQ(verifier->GroupPolicies().size(), 0U);
+  EXPECT_EQ(verifier->DictPolicies().size(), 0U);
 }
 
 TEST_F(ExternalConstantsBuilderTests, TestClearedEverything) {
@@ -151,7 +151,7 @@
                   .ClearUseCUP()
                   .ClearInitialDelay()
                   .ClearServerKeepAliveSeconds()
-                  .ClearGroupPolicies()
+                  .ClearDictPolicies()
                   .ClearOverinstallTimeout()
                   .ClearIdleCheckPeriod()
                   .ClearMachineManaged()
@@ -174,15 +174,15 @@
   EXPECT_EQ(verifier->AppLogoURL(), GURL(APP_LOGO_URL));
   EXPECT_EQ(verifier->InitialDelay(), kInitialDelay);
   EXPECT_EQ(verifier->ServerKeepAliveTime(), kServerKeepAliveTime);
-  EXPECT_EQ(verifier->GroupPolicies().size(), 0U);
+  EXPECT_EQ(verifier->DictPolicies().size(), 0U);
   EXPECT_FALSE(verifier->IsMachineManaged().has_value());
   EXPECT_FALSE(verifier->EnableDiffUpdates());
   EXPECT_EQ(verifier->CecaConnectionTimeout(), kCecaConnectionTimeout);
 }
 
 TEST_F(ExternalConstantsBuilderTests, TestOverSet) {
-  base::Value::Dict group_policies;
-  group_policies.Set("a", 1);
+  base::Value::Dict dict_policies;
+  dict_policies.Set("a", 1);
 
   EXPECT_TRUE(
       ExternalConstantsBuilder()
@@ -194,7 +194,7 @@
           .SetInitialDelay(base::Seconds(123.4))
           .SetServerKeepAliveTime(base::Seconds(2))
           .SetMachineManaged(std::make_optional(true))
-          .SetGroupPolicies(group_policies)
+          .SetDictPolicies(dict_policies)
           .SetEnableDiffUpdates(false)
           .SetUpdateURL(std::vector<std::string>{"https://www.example.com"})
           .SetCrashUploadURL("https://crash.example.com")
@@ -223,7 +223,7 @@
   EXPECT_EQ(verifier->AppLogoURL(), GURL("https://applogo.example.com/"));
   EXPECT_EQ(verifier->InitialDelay(), base::Seconds(937.6));
   EXPECT_EQ(verifier->ServerKeepAliveTime(), base::Seconds(3));
-  EXPECT_EQ(verifier->GroupPolicies().size(), 1U);
+  EXPECT_EQ(verifier->DictPolicies().size(), 1U);
   EXPECT_TRUE(verifier->IsMachineManaged().has_value());
   EXPECT_FALSE(verifier->IsMachineManaged().value());
   EXPECT_TRUE(verifier->EnableDiffUpdates());
@@ -233,9 +233,9 @@
 TEST_F(ExternalConstantsBuilderTests, TestReuseBuilder) {
   ExternalConstantsBuilder builder;
 
-  base::Value::Dict group_policies;
-  group_policies.Set("a", 1);
-  group_policies.Set("b", 2);
+  base::Value::Dict dict_policies;
+  dict_policies.Set("a", 1);
+  dict_policies.Set("b", 2);
 
   EXPECT_TRUE(
       builder.SetUpdateURL(std::vector<std::string>{"https://www.google.com"})
@@ -246,7 +246,7 @@
           .SetInitialDelay(base::Seconds(123.4))
           .SetServerKeepAliveTime(base::Seconds(3))
           .SetUpdateURL(std::vector<std::string>{"https://www.example.com"})
-          .SetGroupPolicies(group_policies)
+          .SetDictPolicies(dict_policies)
           .SetMachineManaged(std::make_optional(true))
           .SetEnableDiffUpdates(true)
           .SetCecaConnectionTimeout(base::Seconds(5))
@@ -267,14 +267,14 @@
   EXPECT_EQ(verifier->AppLogoURL(), GURL("https://applogo.google.com/"));
   EXPECT_EQ(verifier->InitialDelay(), base::Seconds(123.4));
   EXPECT_EQ(verifier->ServerKeepAliveTime(), base::Seconds(3));
-  EXPECT_EQ(verifier->GroupPolicies().size(), 2U);
+  EXPECT_EQ(verifier->DictPolicies().size(), 2U);
   EXPECT_TRUE(verifier->IsMachineManaged().has_value());
   EXPECT_TRUE(verifier->IsMachineManaged().value());
   EXPECT_TRUE(verifier->EnableDiffUpdates());
   EXPECT_EQ(verifier->CecaConnectionTimeout(), base::Seconds(5));
 
-  base::Value::Dict group_policies2;
-  group_policies2.Set("b", 2);
+  base::Value::Dict dict_policies2;
+  dict_policies2.Set("b", 2);
 
   // But now we can use the builder again:
   EXPECT_TRUE(builder.SetInitialDelay(base::Seconds(92.3))
@@ -283,7 +283,7 @@
                   .ClearCrashUploadURL()
                   .ClearDeviceManagementURL()
                   .ClearAppLogoURL()
-                  .SetGroupPolicies(group_policies2)
+                  .SetDictPolicies(dict_policies2)
                   .ClearMachineManaged()
                   .SetEnableDiffUpdates(false)
                   .ClearCecaConnectionTimeout()
@@ -307,7 +307,7 @@
   EXPECT_EQ(verifier2->InitialDelay(),
             base::Seconds(92.3));  // Updated; update should be seen.
   EXPECT_EQ(verifier2->ServerKeepAliveTime(), base::Seconds(4));
-  EXPECT_EQ(verifier2->GroupPolicies().size(), 1U);
+  EXPECT_EQ(verifier2->DictPolicies().size(), 1U);
   EXPECT_FALSE(verifier2->IsMachineManaged().has_value());
   EXPECT_FALSE(verifier2->EnableDiffUpdates());
   EXPECT_EQ(verifier2->CecaConnectionTimeout(), kCecaConnectionTimeout);
@@ -316,9 +316,9 @@
 TEST_F(ExternalConstantsBuilderTests, TestModify) {
   ExternalConstantsBuilder builder;
 
-  base::Value::Dict group_policies;
-  group_policies.Set("a", 1);
-  group_policies.Set("b", 2);
+  base::Value::Dict dict_policies;
+  dict_policies.Set("a", 1);
+  dict_policies.Set("b", 2);
 
   EXPECT_TRUE(
       builder.SetUpdateURL(std::vector<std::string>{"https://www.google.com"})
@@ -332,7 +332,7 @@
           .SetCrashUploadURL("https://crash.example.com")
           .SetDeviceManagementURL("https://dm.example.com")
           .SetAppLogoURL("https://applogo.example.com/")
-          .SetGroupPolicies(group_policies)
+          .SetDictPolicies(dict_policies)
           .SetMachineManaged(std::make_optional(false))
           .SetEnableDiffUpdates(true)
           .SetCecaConnectionTimeout(base::Seconds(55))
@@ -353,7 +353,7 @@
   EXPECT_EQ(verifier->AppLogoURL(), GURL("https://applogo.example.com/"));
   EXPECT_EQ(verifier->InitialDelay(), base::Seconds(123.4));
   EXPECT_EQ(verifier->ServerKeepAliveTime(), base::Seconds(3));
-  EXPECT_EQ(verifier->GroupPolicies().size(), 2U);
+  EXPECT_EQ(verifier->DictPolicies().size(), 2U);
   EXPECT_TRUE(verifier->IsMachineManaged().has_value());
   EXPECT_FALSE(verifier->IsMachineManaged().value());
   EXPECT_TRUE(verifier->EnableDiffUpdates());
@@ -362,10 +362,10 @@
   // Now we use a new builder to modify just the group policies.
   ExternalConstantsBuilder builder2;
 
-  base::Value::Dict group_policies2;
-  group_policies2.Set("b", 2);
+  base::Value::Dict dict_policies2;
+  dict_policies2.Set("b", 2);
 
-  EXPECT_TRUE(builder2.SetGroupPolicies(group_policies2).Modify());
+  EXPECT_TRUE(builder2.SetDictPolicies(dict_policies2).Modify());
 
   // We need a new overrider to verify because it only loads once.
   scoped_refptr<ExternalConstantsOverrider> verifier2 =
@@ -373,7 +373,7 @@
           CreateDefaultExternalConstants());
 
   // Only the group policies are different.
-  EXPECT_EQ(verifier2->GroupPolicies().size(), 1U);
+  EXPECT_EQ(verifier2->DictPolicies().size(), 1U);
 
   // All the values below are unchanged.
   EXPECT_FALSE(verifier2->UseCUP());
diff --git a/chrome/updater/external_constants_default.cc b/chrome/updater/external_constants_default.cc
index 3934b6a0..2bc49d9 100644
--- a/chrome/updater/external_constants_default.cc
+++ b/chrome/updater/external_constants_default.cc
@@ -48,7 +48,7 @@
     return crx_file::VerifierFormat::CRX3_WITH_PUBLISHER_PROOF;
   }
 
-  base::Value::Dict GroupPolicies() const override {
+  base::Value::Dict DictPolicies() const override {
     return base::Value::Dict();
   }
 
diff --git a/chrome/updater/external_constants_override.cc b/chrome/updater/external_constants_override.cc
index 73f1aca3..4411fe2 100644
--- a/chrome/updater/external_constants_override.cc
+++ b/chrome/updater/external_constants_override.cc
@@ -181,17 +181,17 @@
       crx_format_verifier_value->GetInt());
 }
 
-base::Value::Dict ExternalConstantsOverrider::GroupPolicies() const {
-  if (!override_values_.contains(kDevOverrideKeyGroupPolicies)) {
-    return next_provider_->GroupPolicies();
+base::Value::Dict ExternalConstantsOverrider::DictPolicies() const {
+  if (!override_values_.contains(kDevOverrideKeyDictPolicies)) {
+    return next_provider_->DictPolicies();
   }
 
-  const base::Value* group_policies_value =
-      override_values_.Find(kDevOverrideKeyGroupPolicies);
-  CHECK(group_policies_value->is_dict())
-      << "Unexpected type of override[" << kDevOverrideKeyGroupPolicies
-      << "]: " << base::Value::GetTypeName(group_policies_value->type());
-  return group_policies_value->GetDict().Clone();
+  const base::Value* dict_policies_value =
+      override_values_.Find(kDevOverrideKeyDictPolicies);
+  CHECK(dict_policies_value->is_dict())
+      << "Unexpected type of override[" << kDevOverrideKeyDictPolicies
+      << "]: " << base::Value::GetTypeName(dict_policies_value->type());
+  return dict_policies_value->GetDict().Clone();
 }
 
 base::TimeDelta ExternalConstantsOverrider::OverinstallTimeout() const {
diff --git a/chrome/updater/external_constants_override.h b/chrome/updater/external_constants_override.h
index 77b853c..d70c091 100644
--- a/chrome/updater/external_constants_override.h
+++ b/chrome/updater/external_constants_override.h
@@ -55,7 +55,7 @@
   base::TimeDelta InitialDelay() const override;
   base::TimeDelta ServerKeepAliveTime() const override;
   crx_file::VerifierFormat CrxVerifierFormat() const override;
-  base::Value::Dict GroupPolicies() const override;
+  base::Value::Dict DictPolicies() const override;
   base::TimeDelta OverinstallTimeout() const override;
   base::TimeDelta IdleCheckPeriod() const override;
   std::optional<bool> IsMachineManaged() const override;
diff --git a/chrome/updater/external_constants_override_unittest.cc b/chrome/updater/external_constants_override_unittest.cc
index e8da374..bc556ad 100644
--- a/chrome/updater/external_constants_override_unittest.cc
+++ b/chrome/updater/external_constants_override_unittest.cc
@@ -42,7 +42,7 @@
 
   EXPECT_EQ(overrider->InitialDelay(), kInitialDelay);
   EXPECT_EQ(overrider->ServerKeepAliveTime(), kServerKeepAliveTime);
-  EXPECT_EQ(overrider->GroupPolicies().size(), 0U);
+  EXPECT_EQ(overrider->DictPolicies().size(), 0U);
   EXPECT_FALSE(overrider->EnableDiffUpdates());
   EXPECT_EQ(overrider->CecaConnectionTimeout(), kCecaConnectionTimeout);
 }
@@ -52,9 +52,9 @@
   base::Value::List url_list;
   url_list.Append("https://www.example.com");
   url_list.Append("https://www.google.com");
-  base::Value::Dict group_policies;
-  group_policies.Set("a", 1);
-  group_policies.Set("b", 2);
+  base::Value::Dict dict_policies;
+  dict_policies.Set("a", 1);
+  dict_policies.Set("b", 2);
 
   overrides.Set(kDevOverrideKeyUseCUP, false);
   overrides.Set(kDevOverrideKeyUrl, std::move(url_list));
@@ -63,7 +63,7 @@
   overrides.Set(kDevOverrideKeyAppLogoUrl, "https://applogo.google.com/");
   overrides.Set(kDevOverrideKeyInitialDelay, 137.1);
   overrides.Set(kDevOverrideKeyServerKeepAliveSeconds, 1);
-  overrides.Set(kDevOverrideKeyGroupPolicies, std::move(group_policies));
+  overrides.Set(kDevOverrideKeyDictPolicies, std::move(dict_policies));
   overrides.Set(kDevOverrideKeyOverinstallTimeout, 3);
   overrides.Set(kDevOverrideKeyIdleCheckPeriodSeconds, 4);
   overrides.Set(kDevOverrideKeyEnableDiffUpdates, true);
@@ -89,7 +89,7 @@
 
   EXPECT_EQ(overrider->InitialDelay(), base::Seconds(137.1));
   EXPECT_EQ(overrider->ServerKeepAliveTime(), base::Seconds(1));
-  EXPECT_EQ(overrider->GroupPolicies().size(), 2U);
+  EXPECT_EQ(overrider->DictPolicies().size(), 2U);
   EXPECT_EQ(overrider->OverinstallTimeout(), base::Seconds(3));
   EXPECT_EQ(overrider->IdleCheckPeriod(), base::Seconds(4));
   EXPECT_TRUE(overrider->EnableDiffUpdates());
@@ -111,7 +111,7 @@
   EXPECT_TRUE(overrider->UseCUP());
   EXPECT_EQ(overrider->InitialDelay(), kInitialDelay);
   EXPECT_EQ(overrider->ServerKeepAliveTime(), kServerKeepAliveTime);
-  EXPECT_EQ(overrider->GroupPolicies().size(), 0U);
+  EXPECT_EQ(overrider->DictPolicies().size(), 0U);
   EXPECT_EQ(overrider->CecaConnectionTimeout(), kCecaConnectionTimeout);
 }
 
diff --git a/chrome/updater/mac/BUILD.gn b/chrome/updater/mac/BUILD.gn
index 889bb96c..490ddb6 100644
--- a/chrome/updater/mac/BUILD.gn
+++ b/chrome/updater/mac/BUILD.gn
@@ -619,6 +619,7 @@
     "$root_out_dir/CRURegistration/CRURegistration.h",
     "$root_out_dir/CRURegistration/CRURegistration-Private.h",
     "$root_out_dir/CRURegistration/CRURegistration.m",
+    "$root_out_dir/CRURegistration/CRURegistrationPaths.m",
   ]
   inputs += rebase_path(get_path_info(updater_signing_sources, "file"),
                         "",
diff --git a/chrome/updater/policy/service.cc b/chrome/updater/policy/service.cc
index 64bc51e..2bb4944 100644
--- a/chrome/updater/policy/service.cc
+++ b/chrome/updater/policy/service.cc
@@ -61,7 +61,7 @@
   dm_policy_manager_ =
       CreateDMPolicyManager(external_constants->IsMachineManaged());
   external_constants_policy_manager_ =
-      base::MakeRefCounted<PolicyManager>(external_constants->GroupPolicies());
+      base::MakeRefCounted<PolicyManager>(external_constants->DictPolicies());
   platform_policy_manager_ =
       CreatePlatformPolicyManager(external_constants->IsMachineManaged());
 }
@@ -111,19 +111,6 @@
 
 bool PolicyService::PolicyManagers::CloudPolicyOverridesPlatformPolicy(
     const std::vector<scoped_refptr<PolicyManagerInterface>>& providers) {
-#if BUILDFLAG(IS_WIN)
-  // On Windows, by default, Group Policy has a higher priority than the
-  // clould policy.
-  constexpr bool kCloudPolicyOverridesPlatformPolicyDefaultValue = false;
-#elif BUILDFLAG(IS_MAC)
-  // On macOS, cloud policy has a higher priority than the Managed Preferences.
-  constexpr bool kCloudPolicyOverridesPlatformPolicyDefaultValue = true;
-#else
-  // On other platforms, there's no platform policy at the moment, the value
-  // doesn't actually matter.
-  constexpr bool kCloudPolicyOverridesPlatformPolicyDefaultValue = true;
-#endif
-
   auto it = base::ranges::find_if(
       providers, [](scoped_refptr<PolicyManagerInterface> p) {
         return p && (p->CloudPolicyOverridesPlatformPolicy()).has_value();
diff --git a/chrome/updater/policy/service_unittest.cc b/chrome/updater/policy/service_unittest.cc
index 5cab969..ea1330da 100644
--- a/chrome/updater/policy/service_unittest.cc
+++ b/chrome/updater/policy/service_unittest.cc
@@ -20,21 +20,35 @@
 #include "base/values.h"
 #include "chrome/enterprise_companion/global_constants.h"
 #include "chrome/updater/external_constants.h"
+#include "chrome/updater/external_constants_builder.h"
+#include "chrome/updater/external_constants_override.h"
 #include "chrome/updater/policy/dm_policy_manager.h"
 #include "chrome/updater/policy/manager.h"
 #include "chrome/updater/policy/platform_policy_manager.h"
 #include "chrome/updater/protos/omaha_settings.pb.h"
+#include "chrome/updater/test/integration_tests_impl.h"
+#include "chrome/updater/test/test_scope.h"
+#include "chrome/updater/test/unit_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "base/test/test_reg_util_win.h"
-#include "base/win/registry.h"
 #include "chrome/updater/util/win_util.h"
 #include "chrome/updater/win/win_constants.h"
 #endif
 
 namespace updater {
 
+namespace {
+
+#if BUILDFLAG(IS_WIN)
+constexpr char kGlobalPolicyKey[] = "";
+#elif !BUILDFLAG(IS_MAC)
+constexpr char kGlobalPolicyKey[] = "global";
+#endif
+
+}  // namespace
+
 using PolicyManagers = std::vector<scoped_refptr<PolicyManagerInterface>>;
 
 void PolicyService::SetManagersForTesting(updater::PolicyManagers managers) {
@@ -770,62 +784,6 @@
                 ->AreUpdatesSuppressedNow(now));
 }
 
-#if BUILDFLAG(IS_WIN)
-TEST_F(PolicyServiceTest, CreateManagers) {
-  base::test::TaskEnvironment environment;
-  registry_util::RegistryOverrideManager registry_overrides;
-  ASSERT_NO_FATAL_FAILURE(
-      registry_overrides.OverrideRegistry(HKEY_LOCAL_MACHINE));
-
-  auto omaha_settings =
-      std::make_unique<::wireless_android_enterprise_devicemanagement::
-                           OmahaSettingsClientProto>();
-  auto dm_policy = base::MakeRefCounted<DMPolicyManager>(*omaha_settings, true);
-  PolicyService::PolicyManagers managers(CreateExternalConstants());
-  managers.ResetDeviceManagementManager(dm_policy);
-  EXPECT_EQ(managers.managers().size(), size_t{4});
-  EXPECT_EQ(managers.managers()[0]->source(), "Device Management");
-  EXPECT_EQ(managers.managers()[1]->source(), "Default");
-  EXPECT_EQ(managers.managers()[2]->source(), "DictValuePolicy");
-  EXPECT_EQ(managers.managers()[3]->source(), "Group Policy");
-
-  base::win::RegKey key(HKEY_LOCAL_MACHINE, UPDATER_POLICIES_KEY,
-                        Wow6432(KEY_WRITE));
-  EXPECT_EQ(ERROR_SUCCESS,
-            key.WriteValue(L"CloudPolicyOverridesPlatformPolicy", 1));
-  managers.ResetDeviceManagementManager(dm_policy);
-  EXPECT_EQ(managers.managers().size(), size_t{4});
-  EXPECT_EQ(managers.managers()[0]->source(), "Device Management");
-  EXPECT_EQ(managers.managers()[1]->source(), "Default");
-  EXPECT_EQ(managers.managers()[2]->source(), "DictValuePolicy");
-  EXPECT_EQ(managers.managers()[3]->source(), "Group Policy");
-}
-#elif BUILDFLAG(IS_MAC)
-TEST_F(PolicyServiceTest, CreateManagers) {
-  auto omaha_settings =
-      std::make_unique<::wireless_android_enterprise_devicemanagement::
-                           OmahaSettingsClientProto>();
-  auto dm_policy = base::MakeRefCounted<DMPolicyManager>(*omaha_settings, true);
-  PolicyService::PolicyManagers managers(CreateExternalConstants());
-  managers.ResetDeviceManagementManager(dm_policy);
-  EXPECT_EQ(managers.managers().size(), size_t{4});
-  EXPECT_EQ(managers.managers()[0]->source(), "Device Management");
-}
-#else
-TEST_F(PolicyServiceTest, CreateManagers) {
-  auto omaha_settings =
-      std::make_unique<::wireless_android_enterprise_devicemanagement::
-                           OmahaSettingsClientProto>();
-  auto dm_policy = base::MakeRefCounted<DMPolicyManager>(*omaha_settings, true);
-  PolicyService::PolicyManagers managers(CreateExternalConstants());
-  managers.ResetDeviceManagementManager(dm_policy);
-  EXPECT_EQ(managers.managers().size(), size_t{3});
-  EXPECT_EQ(managers.managers()[0]->source(), "Device Management");
-  EXPECT_EQ(managers.managers()[1]->source(), "Default");
-  EXPECT_EQ(managers.managers()[2]->source(), "DictValuePolicy");
-}
-#endif
-
 TEST_F(PolicyServiceTest, PolicyServiceProxyConfiguration_Get) {
   // Test proxy mode "auto_detect".
   auto manager = base::MakeRefCounted<FakePolicyManager>(true, "manager");
@@ -879,4 +837,212 @@
   ASSERT_FALSE(PolicyServiceProxyConfiguration::Get(policy_service));
 }
 
+class PolicyManagersTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+#if BUILDFLAG(IS_MAC)
+    if (IsSystemInstall(GetUpdaterScopeForTesting())) {
+      GTEST_SKIP();
+    }
+#endif
+
+    ASSERT_NO_FATAL_FAILURE(DeleteOverridesFile());
+
+#if BUILDFLAG(IS_WIN)
+    ASSERT_NO_FATAL_FAILURE(
+        registry_overrides_.OverrideRegistry(HKEY_LOCAL_MACHINE));
+#endif
+  }
+
+  void TearDown() override {
+#if BUILDFLAG(IS_MAC)
+    if (IsSystemInstall(GetUpdaterScopeForTesting())) {
+      GTEST_SKIP();
+    }
+#endif
+
+    ASSERT_NO_FATAL_FAILURE(DeleteOverridesFile());
+  }
+
+  void DeleteOverridesFile() {
+    ASSERT_TRUE(
+        test::DeleteFileAndEmptyParentDirectories(overrides_file_path_));
+  }
+
+ private:
+  const std::optional<base::FilePath> overrides_file_path_ =
+      GetOverrideFilePath(GetUpdaterScopeForTesting());
+
+#if BUILDFLAG(IS_WIN)
+  registry_util::RegistryOverrideManager registry_overrides_;
+  base::test::TaskEnvironment environment_;
+#endif
+};
+
+TEST_F(PolicyManagersTest, NullExternalConstants) {
+  PolicyService::PolicyManagers managers({});
+  ASSERT_EQ(managers.managers().size(), size_t{1});
+  EXPECT_EQ(managers.managers()[0]->source(), "Default");
+}
+
+TEST_F(PolicyManagersTest, MachineUnmanaged) {
+  ASSERT_TRUE(ExternalConstantsBuilder().SetMachineManaged(false).Overwrite());
+  PolicyService::PolicyManagers managers(CreateExternalConstants());
+
+  ASSERT_EQ(managers.managers().size(),
+            size_t{2 + kPlatformPolicyManagerDefined});
+  EXPECT_EQ(managers.managers()[0]->source(), "Default");
+  EXPECT_EQ(managers.managers()[1]->source(), "DictValuePolicy");
+  if (kPlatformPolicyManagerDefined) {
+    EXPECT_EQ(managers.managers()[2]->source(), kSourcePlatformPolicyManager);
+  }
+}
+
+TEST_F(PolicyManagersTest, ValidDeviceManagementManager) {
+  ASSERT_TRUE(ExternalConstantsBuilder().SetMachineManaged(false).Overwrite());
+  auto omaha_settings =
+      std::make_unique<::wireless_android_enterprise_devicemanagement::
+                           OmahaSettingsClientProto>();
+  auto dm_policy = base::MakeRefCounted<DMPolicyManager>(*omaha_settings, true);
+  PolicyService::PolicyManagers managers(CreateExternalConstants());
+  managers.ResetDeviceManagementManager(dm_policy);
+
+  ASSERT_EQ(managers.managers().size(),
+            size_t{3 + kPlatformPolicyManagerDefined});
+  EXPECT_EQ(managers.managers()[0]->source(), "Device Management");
+  EXPECT_EQ(managers.managers()[1]->source(), "Default");
+  EXPECT_EQ(managers.managers()[2]->source(), "DictValuePolicy");
+  if (kPlatformPolicyManagerDefined) {
+    EXPECT_EQ(managers.managers()[3]->source(), kSourcePlatformPolicyManager);
+  }
+}
+
+// TODO(crbug.com/389965546): enable these tests for mac.
+#if !BUILDFLAG(IS_MAC)
+TEST_F(PolicyManagersTest, ValidDictPlatformPolicies) {
+  base::Value::Dict dict_policies;
+  dict_policies.Set("a", 1);
+
+  ASSERT_TRUE(ExternalConstantsBuilder()
+                  .SetMachineManaged(true)
+                  .SetDictPolicies(dict_policies)
+                  .Overwrite());
+
+  base::Value::Dict policies;
+  policies.Set(kGlobalPolicyKey,
+               base::Value::Dict().Set("CloudPolicyOverridesPlatformPolicy",
+                                       kPolicyEnabled));
+  ASSERT_NO_FATAL_FAILURE(test::SetPlatformPolicies(policies));
+
+  PolicyService::PolicyManagers managers(CreateExternalConstants());
+
+  ASSERT_EQ(managers.managers().size(),
+            size_t{2 + kPlatformPolicyManagerDefined});
+  EXPECT_EQ(managers.managers()[0]->source(), "DictValuePolicy");
+  if (kPlatformPolicyManagerDefined) {
+    EXPECT_EQ(managers.managers()[1]->source(), kSourcePlatformPolicyManager);
+  }
+  EXPECT_EQ(managers.managers()[1 + kPlatformPolicyManagerDefined]->source(),
+            "Default");
+}
+
+TEST_F(PolicyManagersTest, ValidDeviceManagementPlatformPolicyNoCloudOverride) {
+  ASSERT_TRUE(ExternalConstantsBuilder().SetMachineManaged(true).Overwrite());
+
+  base::Value::Dict policies;
+  policies.Set(kGlobalPolicyKey,
+               base::Value::Dict().Set("CloudPolicyOverridesPlatformPolicy",
+                                       kPolicyDisabled));
+  ASSERT_NO_FATAL_FAILURE(test::SetPlatformPolicies(policies));
+
+  auto omaha_settings =
+      std::make_unique<::wireless_android_enterprise_devicemanagement::
+                           OmahaSettingsClientProto>();
+  auto dm_policy = base::MakeRefCounted<DMPolicyManager>(*omaha_settings, true);
+  PolicyService::PolicyManagers managers(CreateExternalConstants());
+  managers.ResetDeviceManagementManager(dm_policy);
+  ASSERT_EQ(managers.managers().size(),
+            size_t{3 + kPlatformPolicyManagerDefined});
+  if (kPlatformPolicyManagerDefined) {
+    EXPECT_EQ(managers.managers()[0]->source(),
+              kCloudPolicyOverridesPlatformPolicyDefaultValue
+                  ? "Device Management"
+                  : kSourcePlatformPolicyManager);
+    EXPECT_EQ(managers.managers()[1]->source(),
+              kCloudPolicyOverridesPlatformPolicyDefaultValue
+                  ? kSourcePlatformPolicyManager
+                  : "Device Management");
+  } else {
+    EXPECT_EQ(managers.managers()[0]->source(), "Device Management");
+  }
+
+  EXPECT_EQ(managers.managers()[1 + kPlatformPolicyManagerDefined]->source(),
+            "Default");
+  EXPECT_EQ(managers.managers()[2 + kPlatformPolicyManagerDefined]->source(),
+            "DictValuePolicy");
+}
+
+TEST_F(PolicyManagersTest, ValidDeviceManagementPlatformPolicyCloudOverride) {
+  ASSERT_TRUE(ExternalConstantsBuilder().SetMachineManaged(true).Overwrite());
+
+  base::Value::Dict policies;
+  policies.Set(kGlobalPolicyKey,
+               base::Value::Dict().Set("CloudPolicyOverridesPlatformPolicy",
+                                       kPolicyEnabled));
+  ASSERT_NO_FATAL_FAILURE(test::SetPlatformPolicies(policies));
+
+  auto omaha_settings =
+      std::make_unique<::wireless_android_enterprise_devicemanagement::
+                           OmahaSettingsClientProto>();
+  auto dm_policy = base::MakeRefCounted<DMPolicyManager>(*omaha_settings, true);
+  PolicyService::PolicyManagers managers(CreateExternalConstants());
+  managers.ResetDeviceManagementManager(dm_policy);
+
+  ASSERT_EQ(managers.managers().size(),
+            size_t{3 + kPlatformPolicyManagerDefined});
+  EXPECT_EQ(managers.managers()[0]->source(), "Device Management");
+  if (kPlatformPolicyManagerDefined) {
+    EXPECT_EQ(managers.managers()[1]->source(), kSourcePlatformPolicyManager);
+  }
+  EXPECT_EQ(managers.managers()[1 + kPlatformPolicyManagerDefined]->source(),
+            "Default");
+  EXPECT_EQ(managers.managers()[2 + kPlatformPolicyManagerDefined]->source(),
+            "DictValuePolicy");
+}
+
+TEST_F(PolicyManagersTest,
+       ValidDictDeviceManagementPlatformPolicyCloudOverride) {
+  base::Value::Dict dict_policies;
+  dict_policies.Set("a", 1);
+
+  ASSERT_TRUE(ExternalConstantsBuilder()
+                  .SetMachineManaged(true)
+                  .SetDictPolicies(dict_policies)
+                  .Overwrite());
+
+  base::Value::Dict policies;
+  policies.Set(kGlobalPolicyKey,
+               base::Value::Dict().Set("CloudPolicyOverridesPlatformPolicy",
+                                       kPolicyEnabled));
+  ASSERT_NO_FATAL_FAILURE(test::SetPlatformPolicies(policies));
+
+  auto omaha_settings =
+      std::make_unique<::wireless_android_enterprise_devicemanagement::
+                           OmahaSettingsClientProto>();
+  auto dm_policy = base::MakeRefCounted<DMPolicyManager>(*omaha_settings, true);
+  PolicyService::PolicyManagers managers(CreateExternalConstants());
+  managers.ResetDeviceManagementManager(dm_policy);
+  ASSERT_EQ(managers.managers().size(),
+            size_t{3 + kPlatformPolicyManagerDefined});
+  EXPECT_EQ(managers.managers()[0]->source(), "DictValuePolicy");
+  EXPECT_EQ(managers.managers()[1]->source(), "Device Management");
+  if (kPlatformPolicyManagerDefined) {
+    EXPECT_EQ(managers.managers()[2]->source(), kSourcePlatformPolicyManager);
+  }
+
+  EXPECT_EQ(managers.managers()[2 + kPlatformPolicyManagerDefined]->source(),
+            "Default");
+}
+#endif  // !BUILDFLAG(IS_MAC)
+
 }  // namespace updater
diff --git a/chrome/updater/test/integration_test_commands.h b/chrome/updater/test/integration_test_commands.h
index 9a5e6b17..52143c11 100644
--- a/chrome/updater/test/integration_test_commands.h
+++ b/chrome/updater/test/integration_test_commands.h
@@ -41,7 +41,7 @@
                              base::TimeDelta server_keep_alive_time,
                              base::TimeDelta ceca_connection_timeout) const = 0;
   virtual void ExitTestMode() const = 0;
-  virtual void SetGroupPolicies(const base::Value::Dict& values) const = 0;
+  virtual void SetDictPolicies(const base::Value::Dict& values) const = 0;
   virtual void SetPlatformPolicies(const base::Value::Dict& values) const = 0;
   virtual void SetMachineManaged(bool is_managed_device) const = 0;
   virtual void Clean() const = 0;
diff --git a/chrome/updater/test/integration_test_commands_system.cc b/chrome/updater/test/integration_test_commands_system.cc
index 263b1d9..e717db9 100644
--- a/chrome/updater/test/integration_test_commands_system.cc
+++ b/chrome/updater/test/integration_test_commands_system.cc
@@ -167,8 +167,8 @@
 
   void ExitTestMode() const override { RunCommand("exit_test_mode"); }
 
-  void SetGroupPolicies(const base::Value::Dict& values) const override {
-    RunCommand("set_group_policies",
+  void SetDictPolicies(const base::Value::Dict& values) const override {
+    RunCommand("set_dict_policies",
                {Param("values", StringFromValue(base::Value(values.Clone())))});
   }
 
diff --git a/chrome/updater/test/integration_test_commands_user.cc b/chrome/updater/test/integration_test_commands_user.cc
index 1eacf55..af0b4bff 100644
--- a/chrome/updater/test/integration_test_commands_user.cc
+++ b/chrome/updater/test/integration_test_commands_user.cc
@@ -103,8 +103,8 @@
     updater::test::ExpectSelfUpdateSequence(updater_scope_, test_server);
   }
 
-  void SetGroupPolicies(const base::Value::Dict& values) const override {
-    updater::test::SetGroupPolicies(values);
+  void SetDictPolicies(const base::Value::Dict& values) const override {
+    updater::test::SetDictPolicies(values);
   }
 
   void SetPlatformPolicies(const base::Value::Dict& values) const override {
diff --git a/chrome/updater/test/integration_tests.cc b/chrome/updater/test/integration_tests.cc
index c344ee0..61c1ba8c 100644
--- a/chrome/updater/test/integration_tests.cc
+++ b/chrome/updater/test/integration_tests.cc
@@ -350,8 +350,8 @@
 
   void ExitTestMode() { test_commands_->ExitTestMode(); }
 
-  void SetGroupPolicies(const base::Value::Dict& values) {
-    test_commands_->SetGroupPolicies(values);
+  void SetDictPolicies(const base::Value::Dict& values) {
+    test_commands_->SetDictPolicies(values);
   }
 
   void SetPlatformPolicies(const base::Value::Dict& values) {
@@ -1476,11 +1476,11 @@
 
 TEST_F(IntegrationTest, NoCheckWhenLastCheckedRecentlyPolicy) {
   ScopedServer test_server(test_commands_);
-  base::Value::Dict group_policies;
-  group_policies.Set("autoupdatecheckperiodminutes", 60 * 18);
+  base::Value::Dict dict_policies;
+  dict_policies.Set("autoupdatecheckperiodminutes", 60 * 18);
   ASSERT_NO_FATAL_FAILURE(SetLastChecked(base::Time::Now() - base::Hours(12)));
   ASSERT_NO_FATAL_FAILURE(Install());
-  ASSERT_NO_FATAL_FAILURE(SetGroupPolicies(group_policies));
+  ASSERT_NO_FATAL_FAILURE(SetDictPolicies(dict_policies));
   ASSERT_NO_FATAL_FAILURE(InstallApp("test"));
   ASSERT_NO_FATAL_FAILURE(RunWake(0));
   ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
@@ -1491,12 +1491,12 @@
   ScopedServer test_server(test_commands_);
   base::Time::Exploded now;
   base::Time::Now().LocalExplode(&now);
-  base::Value::Dict group_policies;
-  group_policies.Set("updatessuppressedstarthour", (now.hour - 1 + 24) % 24);
-  group_policies.Set("updatessuppressedstartmin", 0);
-  group_policies.Set("updatessuppresseddurationmin", 120);
+  base::Value::Dict dict_policies;
+  dict_policies.Set("updatessuppressedstarthour", (now.hour - 1 + 24) % 24);
+  dict_policies.Set("updatessuppressedstartmin", 0);
+  dict_policies.Set("updatessuppresseddurationmin", 120);
   ASSERT_NO_FATAL_FAILURE(Install());
-  ASSERT_NO_FATAL_FAILURE(SetGroupPolicies(group_policies));
+  ASSERT_NO_FATAL_FAILURE(SetDictPolicies(dict_policies));
   ASSERT_NO_FATAL_FAILURE(InstallApp("test"));
   ASSERT_NO_FATAL_FAILURE(RunWake(0));
   ASSERT_NO_FATAL_FAILURE(ExpectUninstallPing(&test_server));
@@ -4188,12 +4188,11 @@
   ScopedServer test_server(test_commands_);
   ASSERT_NO_FATAL_FAILURE(Install());
 
-  base::Value::Dict group_policies;
-  group_policies.Set("installtest1",
-                     IsSystemInstall(GetUpdaterScopeForTesting())
-                         ? kPolicyForceInstallMachine
-                         : kPolicyForceInstallUser);
-  ASSERT_NO_FATAL_FAILURE(SetGroupPolicies(group_policies));
+  base::Value::Dict dict_policies;
+  dict_policies.Set("installtest1", IsSystemInstall(GetUpdaterScopeForTesting())
+                                        ? kPolicyForceInstallMachine
+                                        : kPolicyForceInstallUser);
+  ASSERT_NO_FATAL_FAILURE(SetDictPolicies(dict_policies));
 
   ExpectUpdateCheckRequest(&test_server);
 
@@ -4729,9 +4728,9 @@
 
 TEST_P(IntegrationLegacyUpdate3WebTest, DisabledPolicyManual) {
   ASSERT_TRUE(WaitForUpdaterExit());
-  base::Value::Dict group_policies;
-  group_policies.Set("updatetest1", kPolicyAutomaticUpdatesOnly);
-  ASSERT_NO_FATAL_FAILURE(SetGroupPolicies(group_policies));
+  base::Value::Dict dict_policies;
+  dict_policies.Set("updatetest1", kPolicyAutomaticUpdatesOnly);
+  ASSERT_NO_FATAL_FAILURE(SetDictPolicies(dict_policies));
   ASSERT_NO_FATAL_FAILURE(ExpectLegacyUpdate3WebSucceeds(
       kAppId, AppBundleWebCreateMode::kCreateInstalledApp, STATE_ERROR,
       GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY_MANUAL));
@@ -4739,9 +4738,9 @@
 
 TEST_P(IntegrationLegacyUpdate3WebTest, DisabledPolicy) {
   ASSERT_TRUE(WaitForUpdaterExit());
-  base::Value::Dict group_policies;
-  group_policies.Set("updatetest1", kPolicyDisabled);
-  ASSERT_NO_FATAL_FAILURE(SetGroupPolicies(group_policies));
+  base::Value::Dict dict_policies;
+  dict_policies.Set("updatetest1", kPolicyDisabled);
+  ASSERT_NO_FATAL_FAILURE(SetDictPolicies(dict_policies));
   ExpectLegacyUpdate3WebSucceeds(
       kAppId, AppBundleWebCreateMode::kCreateInstalledApp, STATE_ERROR,
       GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY);
diff --git a/chrome/updater/test/integration_tests_helper.cc b/chrome/updater/test/integration_tests_helper.cc
index e1db88a..aea6973 100644
--- a/chrome/updater/test/integration_tests_helper.cc
+++ b/chrome/updater/test/integration_tests_helper.cc
@@ -301,7 +301,7 @@
                                    WithSwitch("update_url",
                                               Wrap(&EnterTestMode))))))))},
           {"exit_test_mode", WithSystemScope(Wrap(&ExitTestMode))},
-          {"set_group_policies", WithSwitch("values", Wrap(&SetGroupPolicies))},
+          {"set_dict_policies", WithSwitch("values", Wrap(&SetDictPolicies))},
           {"set_platform_policies",
            WithSwitch("values", Wrap(&SetPlatformPolicies))},
           {"set_machine_managed",
diff --git a/chrome/updater/test/integration_tests_impl.cc b/chrome/updater/test/integration_tests_impl.cc
index c659606..c246d24 100644
--- a/chrome/updater/test/integration_tests_impl.cc
+++ b/chrome/updater/test/integration_tests_impl.cc
@@ -513,8 +513,8 @@
           .Modify());
 }
 
-void SetGroupPolicies(const base::Value::Dict& values) {
-  ASSERT_TRUE(ExternalConstantsBuilder().SetGroupPolicies(values).Modify());
+void SetDictPolicies(const base::Value::Dict& values) {
+  ASSERT_TRUE(ExternalConstantsBuilder().SetDictPolicies(values).Modify());
 }
 
 void SetMachineManaged(bool is_managed_device) {
diff --git a/chrome/updater/test/integration_tests_impl.h b/chrome/updater/test/integration_tests_impl.h
index f0f1a0a..240d962 100644
--- a/chrome/updater/test/integration_tests_impl.h
+++ b/chrome/updater/test/integration_tests_impl.h
@@ -164,8 +164,8 @@
 // JSON file.
 void ExitTestMode(UpdaterScope scope);
 
-// Sets the external constants for group policies.
-void SetGroupPolicies(const base::Value::Dict& values);
+// Sets the dict policies that are surfaced via external constants.
+void SetDictPolicies(const base::Value::Dict& values);
 
 // Sets platform policies. Platform policy is group policy on Windows, and
 // Managed Preferences on macOS.
diff --git a/chrome/updater/test/service/win/run_command_as_standard_user.py b/chrome/updater/test/service/win/run_command_as_standard_user.py
index f08caa6..a2116e2 100644
--- a/chrome/updater/test/service/win/run_command_as_standard_user.py
+++ b/chrome/updater/test/service/win/run_command_as_standard_user.py
@@ -2,13 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# [VPYTHON:BEGIN]
-# python_version: "3.8"
-# wheel: <
-#   name: "infra/python/wheels/pywin32/${vpython_platform}"
-#    version: "version:300"
-# >
-# [VPYTHON:END]
 """Run the given command as the standard user.
 
 All arguments provided to this program will be used to reconstruct the command
diff --git a/chrome/updater/test/service/win/updater_test_service.py b/chrome/updater/test/service/win/updater_test_service.py
index c321d1e..ee6e301 100644
--- a/chrome/updater/test/service/win/updater_test_service.py
+++ b/chrome/updater/test/service/win/updater_test_service.py
@@ -58,6 +58,8 @@
     _svc_name_ = 'UpdaterTestService'
     _svc_display_name_ = 'Updater Test Service'
     _svc_description_ = 'Service for browser updater tests'
+    _exe_name_ = os.path.join(os.path.dirname(os.path.abspath(sys.executable)),
+                              'pythonservice.exe')
 
     def SvcStop(self):
         """Called by service framework to stop this service."""
diff --git a/chrome/updater/test/service/win/updater_test_service_control.py b/chrome/updater/test/service/win/updater_test_service_control.py
index 33b38ca1..61bed50 100644
--- a/chrome/updater/test/service/win/updater_test_service_control.py
+++ b/chrome/updater/test/service/win/updater_test_service_control.py
@@ -2,14 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# [VPYTHON:BEGIN]
-# python_version: "3.8"
-# wheel: <
-#   name: "infra/python/wheels/pywin32/${vpython_platform}"
-#    version: "version:300"
-# >
-# [VPYTHON:END]
-
 import contextlib
 import logging
 import os
@@ -17,6 +9,7 @@
 import subprocess
 import socket
 import sys
+import sysconfig
 import time
 
 import pywintypes
@@ -56,6 +49,20 @@
     return process.returncode == 0
 
 
+def _CopyDLLsInDirectory(source_dir, destination_dir):
+    """Copies all DLLs in `source_dir` to the `destination_dir`."""
+    for filename in os.listdir(source_dir):
+        source_file = os.path.join(source_dir, filename)
+        destination_file = os.path.join(destination_dir, filename)
+        if os.path.isfile(source_file) and source_file.endswith(
+                '.dll') and not os.path.exists(destination_file):
+            logging.info(f'Copying {source_file} ===> {destination_file} ...')
+            try:
+                shutil.copy(source_file, destination_file)
+            except PermissionError as err:
+                logging.exception(err)
+
+
 def _SetupEnvironmentForVPython():
     """Setup vpython environment."""
     # vpython_spec above brings the pywin32 module we need, but it may not be
@@ -63,23 +70,32 @@
     # https://pypi.org/project/pywin32/.
     # This script outputs some error messages to stderr if it has run before.
     # So skip logging to avoid this log pollution.
-    post_install_script = os.path.join(
-        os.path.dirname(os.path.abspath(sys.executable)),
-        'pywin32_postinstall.py')
-    _RunCommand(
-        [sys.executable, post_install_script, '-install', '-silent', '-quiet'],
-        log_error=False)
 
     # Make pythonservice.exe explicit for our service. This is to avoid pickup
     # an incompatible interpreter accidentally.
     python_dir = os.path.dirname(os.path.abspath(sys.executable))
-    source = os.path.join(os.path.dirname(python_dir), 'Lib', 'site-packages',
-                          'win32', 'pythonservice.exe')
+    source = os.path.join(sysconfig.get_paths()['platlib'], 'win32',
+                          'pythonservice.exe')
     python_service_path = os.path.join(python_dir, 'pythonservice.exe')
     if os.path.exists(source) and not os.path.exists(python_service_path):
         shutil.copyfile(source, python_service_path)
     os.environ['PYTHON_SERVICE_EXE'] = python_service_path
 
+    # Copy the DLLs from `pywin32_system32` folder to the folder that
+    # `pythonservice.exe` resides. The DLLs are required by `pythonservice.exe`.
+    # Normally we run the post install scripts as described by
+    # https://pypi.org/project/pywin32/ to make pywin32 ready. However
+    # the script copies the DLLs to `%WINDIR%\\system32` which leads to version
+    # conflict when multiple versions of VPython exists. Hence we
+    # setup this running environment entirely within current VPython directory.
+    # This is less robust compared with running `pywin32_postinstall.py`, but
+    # works good enough for our test. This also avoids the issue that
+    # `pywin32_postinstall.py` accidentally omits `-silent` switch since
+    # https://github.com/mhammond/pywin32/commit/c6958cdccb38e5888a5141553dd36a21fa196f53.
+    _CopyDLLsInDirectory(
+        os.path.join(sysconfig.get_paths()['platlib'], 'pywin32_system32'),
+        python_dir)
+
 
 def _IsServiceInStatus(status):
     """Returns the if test service is in the given status."""
diff --git a/chrome/updater/win/app_command_runner.cc b/chrome/updater/win/app_command_runner.cc
index 1ab6417..1a296dd 100644
--- a/chrome/updater/win/app_command_runner.cc
+++ b/chrome/updater/win/app_command_runner.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "chrome/updater/win/app_command_runner.h"
 
 #include <windows.h>
@@ -23,6 +18,7 @@
 #include "base/containers/span.h"
 #include "base/files/file_path.h"
 #include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
 #include "base/path_service.h"
 #include "base/process/launch.h"
 #include "base/strings/strcat.h"
@@ -258,7 +254,13 @@
     return E_INVALIDARG;
   }
 
-  const base::FilePath exe = base::FilePath(argv.get()[0]);
+  // SAFETY: the unsafe buffer is present due to the ::CommandLineToArgvW call.
+  // When constructing the span, `num_args` is validated and checked as a valid
+  // size_t value.
+  UNSAFE_BUFFERS(const base::span<wchar_t*> safe_args{
+      argv.get(), base::checked_cast<size_t>(num_args)});
+
+  const base::FilePath exe(safe_args[0]);
   if (!IsSecureAppCommandExePath(scope, exe)) {
     LOG(WARNING) << __func__
                  << ": !IsSecureAppCommandExePath(scope, exe): " << exe;
@@ -267,8 +269,8 @@
 
   executable = exe;
   parameters.clear();
-  for (int i = 1; i < num_args; ++i) {
-    parameters.push_back(argv.get()[i]);
+  for (size_t i = 1; i < safe_args.size(); ++i) {
+    parameters.push_back(safe_args[i]);
   }
 
   return S_OK;
diff --git a/chromecast/browser/BUILD.gn b/chromecast/browser/BUILD.gn
index bee86a9..d2c8f21 100644
--- a/chromecast/browser/BUILD.gn
+++ b/chromecast/browser/BUILD.gn
@@ -323,6 +323,7 @@
       "//chromecast/browser/android:jni_headers",
       "//components/android_autofill/browser:android",
       "//components/embedder_support/android:view",
+      "//components/variations:variations_associated_data",
     ]
   } else {
     deps += [ ":display_configurator" ]
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 8de6bab..94a05740 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-16163.0.0-1066150
\ No newline at end of file
+16164.0.0-1066176
\ No newline at end of file
diff --git a/chromeos/ash/components/network/metrics/cellular_network_metrics_logger.h b/chromeos/ash/components/network/metrics/cellular_network_metrics_logger.h
index b30c07f1..3e2a0c3 100644
--- a/chromeos/ash/components/network/metrics/cellular_network_metrics_logger.h
+++ b/chromeos/ash/components/network/metrics/cellular_network_metrics_logger.h
@@ -126,7 +126,7 @@
   };
 
   static constexpr char kCreateCustomApnResultHistogram[] =
-      "Network.Ash.Cellular.Apn.CreateCustomApn.Result";
+      "Network.Ash.Cellular.Apn.CreateCustomApn.Result2";
   static constexpr char kCreateCustomApnShillErrorHistogram[] =
       "Network.Ash.Cellular.Apn.CreateCustomApn.ShillError";
   static constexpr char kCreateCustomApnAuthenticationTypeHistogram[] =
diff --git a/chromeos/ui/frame/frame_header.cc b/chromeos/ui/frame/frame_header.cc
index 2f26708..05de497f 100644
--- a/chromeos/ui/frame/frame_header.cc
+++ b/chromeos/ui/frame/frame_header.cc
@@ -19,6 +19,7 @@
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/layer_tree_owner.h"
+#include "ui/compositor/layer_type.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/gfx/canvas.h"
 #include "ui/gfx/color_utils.h"
@@ -115,7 +116,9 @@
   old_layer->SetTransform(gfx::Transform());
   // Layer in maximized / fullscreen / snapped state is set to
   // opaque, which can prevent resterizing the new layer immediately.
-  old_layer->SetFillsBoundsOpaquely(false);
+  if (old_layer->type() != ui::LAYER_SOLID_COLOR) {
+    old_layer->SetFillsBoundsOpaquely(false);
+  }
 
   layer_owner_ = std::move(old_layer_owner);
 
diff --git a/clank b/clank
index 327a12f..7305a3c6 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 327a12f72fb1020336bddcf8c48c34ca9807eea1
+Subproject commit 7305a3c6540fa265dc3796f66a575054fafab42a
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 6c311d6..6e9755d 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -673,6 +673,7 @@
       "//components/signin/public/android:signin_java_test_support",
       "//components/spellcheck/browser/android:java",
       "//components/translate/content/android:unit_tests",
+      "//components/variations:variations_associated_data",
       "//components/variations/android:variations_java",
       "//components/webapps/browser/android:java",
       "//components/webapps/browser/android:unit_tests",
diff --git a/components/android_autofill/browser/BUILD.gn b/components/android_autofill/browser/BUILD.gn
index 16c6663..5ec70162 100644
--- a/components/android_autofill/browser/BUILD.gn
+++ b/components/android_autofill/browser/BUILD.gn
@@ -31,7 +31,6 @@
     "//third_party/androidx:androidx_annotation_annotation_java",
     "//third_party/jni_zero:jni_zero_java",
     "//ui/android:ui_no_recycler_view_java",
-    "//ui/android:ui_utils_java",
   ]
 
   sources = [
diff --git a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java
index 496c636..e64d81e 100644
--- a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java
+++ b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillProvider.java
@@ -38,8 +38,6 @@
 import org.chromium.ui.base.WindowAndroid;
 import org.chromium.ui.display.DisplayAndroid;
 
-import java.util.Objects;
-
 /**
  * This class works with Android autofill service to fill web form, it doesn't use Chrome's autofill
  * service or suggestion UI. All methods are supposed to be called in UI thread.
@@ -84,7 +82,6 @@
     // Whether onProvideAutofillVirtualStructure has been called for the current PrefillRequest.
     // Used solely for metrics.
     private boolean mStructureProvidedForPrefillRequest;
-    private boolean mVirtualViewExitedAfterFocusLoss;
 
     public AutofillProvider(
             Context context,
@@ -759,45 +756,6 @@
     }
 
     /**
-     * Notify autofill provider window focus has changed.
-     *
-     * @param hasWindowFocus whether window gained or lost focus.
-     */
-    public void onWindowFocusChanged(boolean hasWindowFocus) {
-        if (mRequest == null) return;
-        FocusField focusField = mRequest.getFocusField();
-        if (focusField == null) return;
-
-        if (!hasWindowFocus) {
-            // When physical keyboard is being used, suggestions appear as a popup window anchored
-            // next to the form. That causes window focus loss, since the popup gains the focus.
-            // So we only want to send virtualViewExit when suggestions are displayed above
-            // the soft keyboard.
-            if (mAutofillManager.isAutofillInputUiShowing() && isSoftKeyboardVisible()) {
-                notifyVirtualViewExited(mContainerView, focusField.fieldIndex);
-                mVirtualViewExitedAfterFocusLoss = true;
-            } else {
-                mVirtualViewExitedAfterFocusLoss = false;
-            }
-        }
-
-        if (hasWindowFocus && mVirtualViewExitedAfterFocusLoss) {
-            notifyVirtualViewEntered(mContainerView, focusField.fieldIndex, focusField.absBound);
-        }
-    }
-
-    /**
-     * Checks if the soft keyboard is currently visible on the screen.
-     *
-     * @return {@code true} if the soft keyboard is visible, {@code false} otherwise.
-     */
-    private boolean isSoftKeyboardVisible() {
-        return Objects.requireNonNull(mWebContents.getTopLevelNativeWindow())
-                .getKeyboardDelegate()
-                .isKeyboardShowing(mContext, mContainerView);
-    }
-
-    /**
      * Inform native provider to autofill.
      *
      * @param nativeAutofillProvider the native autofill provider.
diff --git a/components/autofill/core/browser/data_model/credit_card.cc b/components/autofill/core/browser/data_model/credit_card.cc
index 0d7c812..74ebfdf 100644
--- a/components/autofill/core/browser/data_model/credit_card.cc
+++ b/components/autofill/core/browser/data_model/credit_card.cc
@@ -318,12 +318,12 @@
     case Suggestion::Icon::kOfferTag:
     case Suggestion::Icon::kPenSpark:
     case Suggestion::Icon::kPlusAddress:
+    case Suggestion::Icon::kSaveAndFill:
     case Suggestion::Icon::kScanCreditCard:
     case Suggestion::Icon::kSettings:
     case Suggestion::Icon::kSettingsAndroid:
     case Suggestion::Icon::kUndo:
     case Suggestion::Icon::kBnpl:
-
       NOTREACHED();
   }
   NOTREACHED();
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc
index f87c0e3..0bfcfc0 100644
--- a/components/autofill/core/browser/form_structure.cc
+++ b/components/autofill/core/browser/form_structure.cc
@@ -359,20 +359,33 @@
   return ShouldBeParsed();
 }
 
-bool FormStructure::IsCompleteCreditCardForm() const {
-  bool found_cc_number = false;
-  bool found_cc_expiration = false;
-  for (const auto& field : fields_) {
-    FieldType type = field->Type().GetStorableType();
-    if (!found_cc_expiration && data_util::IsCreditCardExpirationType(type)) {
-      found_cc_expiration = true;
-    } else if (!found_cc_number && type == CREDIT_CARD_NUMBER) {
-      found_cc_number = true;
+bool FormStructure::IsCompleteCreditCardForm(
+    CreditCardFormCompleteness credit_card_form_completeness) const {
+  bool found_cc_expiration =
+      std::ranges::any_of(fields_, [](const auto& field) {
+        return data_util::IsCreditCardExpirationType(
+            field->Type().GetStorableType());
+      });
+  auto has_type = [&](FieldType type) {
+    return std::ranges::any_of(fields_, [&](const auto& field) {
+      return field->Type().GetStorableType() == type;
+    });
+  };
+  bool found_cc_number = has_type(CREDIT_CARD_NUMBER);
+
+  switch (credit_card_form_completeness) {
+    case CreditCardFormCompleteness::kCompleteCreditCardForm:
+      return found_cc_expiration && found_cc_number;
+    case CreditCardFormCompleteness::
+        kCompleteCreditCardFormIncludingCvcAndName: {
+      bool found_cc_cvc = has_type(CREDIT_CARD_VERIFICATION_CODE);
+      bool found_cc_name =
+          has_type(CREDIT_CARD_NAME_FULL) ||
+          (has_type(CREDIT_CARD_NAME_FIRST) && has_type(CREDIT_CARD_NAME_LAST));
+      return found_cc_expiration && found_cc_number && found_cc_cvc &&
+             found_cc_name;
     }
-    if (found_cc_expiration && found_cc_number)
-      return true;
   }
-  return false;
 }
 
 void FormStructure::UpdateAutofillCount() {
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h
index c0cc28e..a67d414 100644
--- a/components/autofill/core/browser/form_structure.h
+++ b/components/autofill/core/browser/form_structure.h
@@ -93,10 +93,24 @@
   // auto-fillable, like google/yahoo/msn search, etc.
   bool IsAutofillable() const;
 
-  // Returns whether |this| form represents a complete Credit Card form, which
-  // consists in having at least a credit card number field and an expiration
-  // field.
-  bool IsCompleteCreditCardForm() const;
+  // This enum defines two different states of completeness for a credit card
+  // form, each used for a distinct purpose to check if the required credit card
+  // fields exist.
+  enum class CreditCardFormCompleteness {
+    // This represents a minimal complete credit card form which has at least a
+    // credit card number field and an expiration date field.
+    kCompleteCreditCardForm,
+    // This represents a credit card form which has a CVC field and a cardholder
+    // name field in addition to the credit card number field and the expiration
+    // date field. For example, this level is required for offering `Save and
+    // Fill`.
+    kCompleteCreditCardFormIncludingCvcAndName,
+  };
+
+  // Returns whether |this| form represents a complete Credit Card form, as
+  // defined by the given CreditCardFormCompleteness level.
+  bool IsCompleteCreditCardForm(
+      CreditCardFormCompleteness credit_card_form_completeness) const;
 
   // Resets |autofill_count_| and counts the number of auto-fillable fields.
   // This is used when we receive server data for form fields.  At that time,
diff --git a/components/autofill/core/browser/form_structure_fuzzer.cc b/components/autofill/core/browser/form_structure_fuzzer.cc
index 1838574..c641af1 100644
--- a/components/autofill/core/browser/form_structure_fuzzer.cc
+++ b/components/autofill/core/browser/form_structure_fuzzer.cc
@@ -74,7 +74,8 @@
       GenerateGeoIpCountryCode(data_provider),
       /*log_manager=*/nullptr);
   std::ignore = form_structure.IsAutofillable();
-  std::ignore = form_structure.IsCompleteCreditCardForm();
+  std::ignore = form_structure.IsCompleteCreditCardForm(
+      FormStructure::CreditCardFormCompleteness::kCompleteCreditCardForm);
   std::ignore = form_structure.ShouldBeParsed();
   std::ignore = form_structure.ShouldRunHeuristics();
   std::ignore = form_structure.ShouldRunHeuristicsForSingleFields();
diff --git a/components/autofill/core/browser/form_structure_unittest.cc b/components/autofill/core/browser/form_structure_unittest.cc
index 70e744d..9ce16a8 100644
--- a/components/autofill/core/browser/form_structure_unittest.cc
+++ b/components/autofill/core/browser/form_structure_unittest.cc
@@ -636,56 +636,126 @@
 TEST_F(FormStructureTestImpl, IsCompleteCreditCardForm_Minimal) {
   CheckFormStructureTestData(
       {{{.description_for_logging = "IsCompleteCreditCardForm_Minimal",
-         .fields = {{.role = FieldType::CREDIT_CARD_NUMBER},
-                    {.label = u"Expiration", .name = u"cc_exp"},
-                    {.role = FieldType::ADDRESS_HOME_ZIP}}},
+         .fields = {{.role = CREDIT_CARD_NUMBER},
+                    {.label = u"Expiration"},
+                    {.role = ADDRESS_HOME_ZIP}}},
         {.determine_heuristic_type = true,
-         .is_complete_credit_card_form = true},
+         .is_complete_credit_card_form = std::make_pair(
+             FormStructure::CreditCardFormCompleteness::kCompleteCreditCardForm,
+             true)},
         {}}});
 }
 
 TEST_F(FormStructureTestImpl, IsCompleteCreditCardForm_Full) {
   CheckFormStructureTestData(
       {{{.description_for_logging = "IsCompleteCreditCardForm_Full",
-         .fields = {{.label = u"Name on Card", .name = u"name_on_card"},
-                    {.role = FieldType::CREDIT_CARD_NUMBER},
-                    {.label = u"Exp Month", .name = u"ccmonth"},
-                    {.label = u"Exp Year", .name = u"ccyear"},
-                    {.label = u"Verification", .name = u"verification"},
-                    {.label = u"Submit",
-                     .name = u"submit",
-                     .form_control_type = FormControlType::kInputText}}},
+         .fields = {{.label = u"Name on Card"},
+                    {.role = CREDIT_CARD_NUMBER},
+                    {.label = u"Exp Month"},
+                    {.label = u"Exp Year"},
+                    {.label = u"Verification"}}},
         {.determine_heuristic_type = true,
-         .is_complete_credit_card_form = true},
-        {}}});
+         .is_complete_credit_card_form = std::make_pair(
+             FormStructure::CreditCardFormCompleteness::kCompleteCreditCardForm,
+             true)}}});
 }
 
 // A form with only the credit card number is not considered sufficient.
 TEST_F(FormStructureTestImpl, IsCompleteCreditCardForm_OnlyCCNumber) {
   CheckFormStructureTestData(
       {{{.description_for_logging = "IsCompleteCreditCardForm_OnlyCCNumber",
-         .fields = {{.role = FieldType::CREDIT_CARD_NUMBER}}},
+         .fields = {{.role = CREDIT_CARD_NUMBER}}},
         {.determine_heuristic_type = true,
-         .is_complete_credit_card_form = false},
+         .is_complete_credit_card_form = std::make_pair(
+             FormStructure::CreditCardFormCompleteness::kCompleteCreditCardForm,
+             false)},
         {}}});
 }
 
-// A form with only the credit card number is not considered sufficient.
 TEST_F(FormStructureTestImpl, IsCompleteCreditCardForm_AddressForm) {
   CheckFormStructureTestData(
       {{{.description_for_logging = "IsCompleteCreditCardForm_AddressForm",
-         .fields = {{.role = FieldType::NAME_FIRST, .name = u""},
-                    {.role = FieldType::NAME_LAST, .name = u""},
-                    {.role = FieldType::EMAIL_ADDRESS, .name = u""},
-                    {.role = FieldType::PHONE_HOME_NUMBER, .name = u""},
-                    {.label = u"Address", .name = u""},
-                    {.label = u"Address", .name = u""},
-                    {.role = FieldType::ADDRESS_HOME_ZIP, .name = u""}}},
+         .fields = {{.role = FieldType::NAME_FIRST},
+                    {.role = FieldType::NAME_LAST},
+                    {.role = FieldType::EMAIL_ADDRESS},
+                    {.role = FieldType::PHONE_HOME_NUMBER},
+                    {.label = u"Address"},
+                    {.label = u"Address"},
+                    {.role = FieldType::ADDRESS_HOME_ZIP}}},
         {.determine_heuristic_type = true,
-         .is_complete_credit_card_form = false},
+         .is_complete_credit_card_form = std::make_pair(
+             FormStructure::CreditCardFormCompleteness::kCompleteCreditCardForm,
+             false)},
         {}}});
 }
 
+TEST_F(FormStructureTestImpl,
+       IsCompleteCreditCardFormIncludingCvcAndName_CvcAndNameExist) {
+  CheckFormStructureTestData({{
+      {.description_for_logging =
+           "IsCompleteCreditCardFormIncludingCvcAndName_CvcAndNameExist",
+       .fields = {{.role = CREDIT_CARD_NUMBER},
+                  {.label = u"Expiration"},
+                  {.label = u"Verification"},
+                  {.label = u"Name on Card"}}},
+      {.determine_heuristic_type = true,
+       .is_complete_credit_card_form =
+           std::make_pair(FormStructure::CreditCardFormCompleteness::
+                              kCompleteCreditCardFormIncludingCvcAndName,
+                          true)},
+  }});
+}
+
+TEST_F(FormStructureTestImpl,
+       IsCompleteCreditCardFormIncludingCvcAndName_MissingCvc) {
+  CheckFormStructureTestData({{
+      {.description_for_logging =
+           "IsCompleteCreditCardFormIncludingCvcAndName_MissingCvc",
+       .fields = {{.role = CREDIT_CARD_NUMBER},
+                  {.label = u"Expiration"},
+                  {.label = u"Name on Card"}}},
+      {.determine_heuristic_type = true,
+       .is_complete_credit_card_form =
+           std::make_pair(FormStructure::CreditCardFormCompleteness::
+                              kCompleteCreditCardFormIncludingCvcAndName,
+                          false)},
+  }});
+}
+
+TEST_F(FormStructureTestImpl,
+       IsCompleteCreditCardFormIncludingCvcAndName_MissingName) {
+  CheckFormStructureTestData({{
+      {.description_for_logging =
+           "IsCompleteCreditCardFormIncludingCvcAndName_MissingName",
+       .fields = {{.role = CREDIT_CARD_NUMBER},
+                  {.label = u"Expiration"},
+                  {.label = u"Verification"}}},
+      {.determine_heuristic_type = true,
+       .is_complete_credit_card_form =
+           std::make_pair(FormStructure::CreditCardFormCompleteness::
+                              kCompleteCreditCardFormIncludingCvcAndName,
+                          false)},
+  }});
+}
+
+TEST_F(FormStructureTestImpl,
+       IsCompleteCreditCardFormIncludingCvcAndName_FirstAndLastNames) {
+  CheckFormStructureTestData({{
+      {.description_for_logging =
+           "IsCompleteCreditCardFormIncludingCvcAndName_FirstAndLastNames",
+       .fields = {{.role = CREDIT_CARD_NUMBER},
+                  {.label = u"Expiration"},
+                  {.label = u"Verification"},
+                  {.label = u"first name"},
+                  {.label = u"last name"}}},
+      {.determine_heuristic_type = true,
+       .is_complete_credit_card_form =
+           std::make_pair(FormStructure::CreditCardFormCompleteness::
+                              kCompleteCreditCardFormIncludingCvcAndName,
+                          true)},
+  }});
+}
+
 // Verify that we can correctly process the 'autocomplete' attribute for phone
 // number types (especially phone prefixes and suffixes).
 TEST_F(FormStructureTestImpl, HeuristicsAutocompleteAttributePhoneTypes) {
diff --git a/components/autofill/core/browser/foundations/autofill_client.cc b/components/autofill/core/browser/foundations/autofill_client.cc
index 403d535c7..7b00f7363d 100644
--- a/components/autofill/core/browser/foundations/autofill_client.cc
+++ b/components/autofill/core/browser/foundations/autofill_client.cc
@@ -210,4 +210,9 @@
 void AutofillClient::TriggerPlusAddressUserPerceptionSurvey(
     plus_addresses::hats::SurveyType survey_type) {}
 
+const syncer::SyncService* AutofillClient::GetSyncService() const {
+  return const_cast<const syncer::SyncService*>(
+      const_cast<AutofillClient*>(this)->GetSyncService());
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/foundations/autofill_client.h b/components/autofill/core/browser/foundations/autofill_client.h
index 4ed72c0..6258e50 100644
--- a/components/autofill/core/browser/foundations/autofill_client.h
+++ b/components/autofill/core/browser/foundations/autofill_client.h
@@ -327,6 +327,7 @@
 
   // Gets the sync service associated with the client.
   virtual syncer::SyncService* GetSyncService() = 0;
+  const syncer::SyncService* GetSyncService() const;
 
   // Gets the IdentityManager associated with the client.
   virtual signin::IdentityManager* GetIdentityManager() = 0;
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager.cc b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
index c63d5d5..20ebd71 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
@@ -179,7 +179,8 @@
   if (form_structure.developer_engagement_metrics()) {
     AutofillMetrics::LogDeveloperEngagementUkm(
         ukm_recorder, source_id, form_structure.main_frame_origin().GetURL(),
-        form_structure.IsCompleteCreditCardForm(),
+        form_structure.IsCompleteCreditCardForm(
+            FormStructure::CreditCardFormCompleteness::kCompleteCreditCardForm),
         autofill_metrics::GetFormTypesForLogging(form_structure),
         form_structure.developer_engagement_metrics(),
         form_structure.form_signature());
diff --git a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc
index e07f86a..c6a8164 100644
--- a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc
@@ -907,6 +907,9 @@
   return bnpl_suggestion;
 }
 
+// Used to manually enable credit card upload in tests.
+std::optional<bool> credit_card_upload_enabled_test_;
+
 }  // namespace
 
 std::vector<Suggestion> GetSuggestionsForCreditCards(
@@ -918,6 +921,22 @@
     bool should_show_cards_from_account,
     const std::vector<std::string>& four_digit_combinations_in_dom,
     const std::u16string& autofilled_last_four_digits_in_form_for_filtering) {
+  std::vector<Suggestion> suggestions;
+  if (client.GetPersonalDataManager()
+          .payments_data_manager()
+          .GetCreditCards()
+          .empty() &&
+      base::FeatureList::IsEnabled(features::kAutofillEnableSaveAndFill)) {
+    bool display_gpay_logo = false;
+    suggestions.push_back(
+        CreateSaveAndFillSuggestion(client, display_gpay_logo));
+    base::ranges::move(
+        GetCreditCardFooterSuggestions(
+            should_show_scan_credit_card, should_show_cards_from_account,
+            trigger_field.is_autofilled(), display_gpay_logo),
+        std::back_inserter(suggestions));
+    return suggestions;
+  }
   // Only trigger GetVirtualCreditCardsForStandaloneCvcField if it's standalone
   // CVC field.
   base::flat_map<std::string, VirtualCardUsageData::VirtualCardLastFour>
@@ -931,7 +950,6 @@
   // Non-empty virtual_card_guid_to_last_four_map indicates this is standalone
   // CVC form AND there is matched VCN (based on the VCN usages and last four
   // from the DOM).
-  std::vector<Suggestion> suggestions;
   if (!virtual_card_guid_to_last_four_map.empty()) {
     suggestions = GetVirtualCardStandaloneCvcFieldSuggestions(
         client, trigger_field, summary.metadata_logging_context,
@@ -1257,6 +1275,23 @@
                                          /*with_gpay_logo=*/false);
 }
 
+Suggestion CreateSaveAndFillSuggestion(const AutofillClient& client,
+                                       bool& display_gpay_logo) {
+  Suggestion save_and_fill(
+      l10n_util::GetStringUTF16(IDS_AUTOFILL_SAVE_AND_FILL_SUGGESTION_TITLE),
+      SuggestionType::kSaveAndFillCreditCardEntry);
+  if (IsCreditCardUploadEnabled(client)) {
+    save_and_fill.labels = {{Suggestion::Text(l10n_util::GetStringUTF16(
+        IDS_AUTOFILL_SERVER_SAVE_AND_FILL_SUGGESTION_DESCRIPTION))}};
+    display_gpay_logo = true;
+  } else {
+    save_and_fill.labels = {{Suggestion::Text(l10n_util::GetStringUTF16(
+        IDS_AUTOFILL_LOCAL_SAVE_AND_FILL_SUGGESTION_DESCRIPTION))}};
+  }
+  save_and_fill.icon = Suggestion::Icon::kSaveAndFill;
+  return save_and_fill;
+}
+
 std::vector<Suggestion> GetSuggestionsForIbans(const std::vector<Iban>& ibans) {
   if (ibans.empty()) {
     return {};
@@ -1360,6 +1395,21 @@
   return suggestions;
 }
 
+bool IsCreditCardUploadEnabled(const AutofillClient& client) {
+  if (credit_card_upload_enabled_test_.has_value()) {
+    return credit_card_upload_enabled_test_.value();
+  }
+  return ::autofill::IsCreditCardUploadEnabled(
+      client.GetSyncService(), *client.GetPrefs(),
+      client.GetPersonalDataManager()
+          .payments_data_manager()
+          .GetCountryCodeForExperimentGroup(),
+      client.GetPersonalDataManager()
+          .payments_data_manager()
+          .GetPaymentsSigninStateForMetrics(),
+      const_cast<AutofillClient*>(&client)->GetCurrentLogManager());
+}
+
 bool IsCardSuggestionAcceptable(const CreditCard& card,
                                 const AutofillClient& client) {
   if (card.record_type() == CreditCard::RecordType::kVirtualCard) {
@@ -1482,6 +1532,10 @@
                                         is_autofilled, with_gpay_logo);
 }
 
+void SetCreditCardUploadEnabledForTest(bool credit_card_upload_enabled) {
+  credit_card_upload_enabled_test_ = credit_card_upload_enabled;
+}
+
 bool ShouldShowVirtualCardOptionForTest(const CreditCard* candidate_card,
                                         const AutofillClient& client) {
   return ShouldShowVirtualCardOption(candidate_card, client);
diff --git a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h
index 3babbe3..b6c1ad21 100644
--- a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h
+++ b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h
@@ -133,6 +133,14 @@
 // `SuggestionType`. This distinction is needed for metrics recording.
 Suggestion CreateManageIbansSuggestion();
 
+// Generates a "Save and Fill" suggestion for users who don't have any cards
+// saved in Autofill. This suggestion is shown above the footer.
+// `display_gpay_logo` is an  output parameter that is set to true if credit
+// card upload is enabled, indicating that the GPay logo should be displayed
+// with the suggestion.
+Suggestion CreateSaveAndFillSuggestion(const AutofillClient& client,
+                                       bool& display_gpay_logo);
+
 // Generates suggestions for all available IBANs.
 std::vector<Suggestion> GetSuggestionsForIbans(const std::vector<Iban>& ibans);
 
@@ -141,6 +149,10 @@
 std::vector<Suggestion> GetPromoCodeSuggestionsFromPromoCodeOffers(
     const std::vector<const AutofillOfferData*>& promo_code_offers);
 
+//  Returns true if all the conditions for enabling the upload of credit card
+//  are satisfied.
+bool IsCreditCardUploadEnabled(const AutofillClient& client);
+
 // Returns true if the suggestion created from the card can be accepted by the
 // user. Returns false when merchant does not accept the given card for example
 // when merchants opt-out of VCNs.
@@ -181,6 +193,8 @@
     bool is_autofilled,
     bool with_gpay_logo);
 
+void SetCreditCardUploadEnabledForTest(bool credit_card_upload_enabled);
+
 // Exposes `ShouldShowVirtualCardOption` in tests.
 bool ShouldShowVirtualCardOptionForTest(const CreditCard* candidate_card,
                                         const AutofillClient& client);
diff --git a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator_unittest.cc
index 06732d5..9b7508c 100644
--- a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator_unittest.cc
@@ -1722,6 +1722,101 @@
             SuggestionType::kMerchantPromoCodeEntry);
 }
 
+TEST_F(PaymentsSuggestionGeneratorTest,
+       GenerateLocalSaveAndFillSuggestion_CreditCardUploadDisabled) {
+  base::test::ScopedFeatureList scoped_feature_list(
+      features::kAutofillEnableSaveAndFill);
+  SetCreditCardUploadEnabledForTest(/*credit_card_upload_enabled=*/false);
+
+  CreditCardSuggestionSummary summary;
+  std::vector<Suggestion> suggestions = GetSuggestionsForCreditCards(
+      *autofill_client(), FormFieldData(), CREDIT_CARD_NUMBER, summary,
+      /*should_show_scan_credit_card=*/false,
+      /*should_show_cards_from_account=*/false,
+      /*four_digit_combinations_in_dom=*/{},
+      /*autofilled_last_four_digits_in_form_for_filtering=*/u"");
+
+  // `suggestions` should contain 3 suggestions which are save and fill
+  // suggestion, separator, and manage cards footer.
+  ASSERT_GE(suggestions.size(), 3ul);
+  EXPECT_THAT(
+      suggestions[0],
+      EqualsSuggestion(
+          SuggestionType::kSaveAndFillCreditCardEntry,
+          l10n_util::GetStringUTF16(
+              IDS_AUTOFILL_SAVE_AND_FILL_SUGGESTION_TITLE),
+          Suggestion::Icon::kSaveAndFill,
+          {{Suggestion::Text(l10n_util::GetStringUTF16(
+              IDS_AUTOFILL_LOCAL_SAVE_AND_FILL_SUGGESTION_DESCRIPTION))}}));
+  EXPECT_THAT(suggestions,
+              ContainsCreditCardFooterSuggestions(/*with_gpay_logo=*/false));
+}
+
+TEST_F(PaymentsSuggestionGeneratorTest,
+       GenerateServerSaveAndFillSuggestion_CreditCardUploadEnabled) {
+  base::test::ScopedFeatureList scoped_feature_list(
+      features::kAutofillEnableSaveAndFill);
+  SetCreditCardUploadEnabledForTest(/*credit_card_upload_enabled=*/true);
+
+  CreditCardSuggestionSummary summary;
+  std::vector<Suggestion> suggestions = GetSuggestionsForCreditCards(
+      *autofill_client(), FormFieldData(), CREDIT_CARD_NUMBER, summary,
+      /*should_show_scan_credit_card=*/false,
+      /*should_show_cards_from_account=*/false,
+      /*four_digit_combinations_in_dom=*/{},
+      /*autofilled_last_four_digits_in_form_for_filtering=*/u"");
+
+  // `suggestions` should contain 3 suggestions which are save and fill
+  // suggestion, separator, and manage cards footer.
+  ASSERT_GE(suggestions.size(), 3ul);
+  EXPECT_THAT(
+      suggestions[0],
+      EqualsSuggestion(
+          SuggestionType::kSaveAndFillCreditCardEntry,
+          l10n_util::GetStringUTF16(
+              IDS_AUTOFILL_SAVE_AND_FILL_SUGGESTION_TITLE),
+          Suggestion::Icon::kSaveAndFill,
+          {{Suggestion::Text(l10n_util::GetStringUTF16(
+              IDS_AUTOFILL_SERVER_SAVE_AND_FILL_SUGGESTION_DESCRIPTION))}}));
+  EXPECT_THAT(suggestions,
+              ContainsCreditCardFooterSuggestions(/*with_gpay_logo=*/true));
+}
+
+TEST_F(PaymentsSuggestionGeneratorTest,
+       GenerateLocalSaveAndFillSuggestion_FlagDisabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(
+      features::kAutofillEnableSaveAndFill);
+
+  CreditCardSuggestionSummary summary;
+  std::vector<Suggestion> suggestions = GetSuggestionsForCreditCards(
+      *autofill_client(), FormFieldData(), CREDIT_CARD_NUMBER, summary,
+      /*should_show_scan_credit_card=*/false,
+      /*should_show_cards_from_account=*/false,
+      /*four_digit_combinations_in_dom=*/{},
+      /*autofilled_last_four_digits_in_form_for_filtering=*/u"");
+
+  ASSERT_GE(suggestions.size(), 0ul);
+}
+
+TEST_F(PaymentsSuggestionGeneratorTest,
+       SaveAndFillSuggestion_NotOfferedWhenCreditCardIsSavedInProfile) {
+  base::test::ScopedFeatureList scoped_feature_list(
+      features::kAutofillEnableSaveAndFill);
+
+  payments_data().AddCreditCard(test::GetCreditCard());
+  CreditCardSuggestionSummary summary;
+  std::vector<Suggestion> suggestions = GetSuggestionsForCreditCards(
+      *autofill_client(), FormFieldData(), CREDIT_CARD_NUMBER, summary,
+      /*should_show_scan_credit_card=*/false,
+      /*should_show_cards_from_account=*/false,
+      /*four_digit_combinations_in_dom=*/{},
+      /*autofilled_last_four_digits_in_form_for_filtering=*/u"");
+
+  EXPECT_EQ(suggestions.size(), 3ul);
+  EXPECT_THAT(suggestions[0],
+              EqualsSuggestion(SuggestionType::kCreditCardEntry));
+}
 // This class helps test the credit card contents that are displayed in
 // Autofill suggestions. It covers suggestions on Desktop/Android dropdown,
 // and on Android keyboard accessory.
diff --git a/components/autofill/core/browser/suggestions/suggestion.cc b/components/autofill/core/browser/suggestions/suggestion.cc
index 932e891..27f90a6d 100644
--- a/components/autofill/core/browser/suggestions/suggestion.cc
+++ b/components/autofill/core/browser/suggestions/suggestion.cc
@@ -116,6 +116,8 @@
       return "kNoIcon";
     case Suggestion::Icon::kBnpl:
       return "kBnpl";
+    case Suggestion::Icon::kSaveAndFill:
+      return "kSaveAndFill";
   }
   NOTREACHED();
 }
diff --git a/components/autofill/core/browser/suggestions/suggestion.h b/components/autofill/core/browser/suggestions/suggestion.h
index 73ace513..d0566088 100644
--- a/components/autofill/core/browser/suggestions/suggestion.h
+++ b/components/autofill/core/browser/suggestions/suggestion.h
@@ -275,6 +275,7 @@
     kIban,
     kBnpl,
     kAutofillAi,
+    kSaveAndFill,
   };
 
   // This enum is used to control filtration of suggestions (see it's used in
diff --git a/components/autofill/core/browser/test_utils/autofill_form_test_utils.cc b/components/autofill/core/browser/test_utils/autofill_form_test_utils.cc
index 31d106f..46c3300 100644
--- a/components/autofill/core/browser/test_utils/autofill_form_test_utils.cc
+++ b/components/autofill/core/browser/test_utils/autofill_form_test_utils.cc
@@ -217,8 +217,10 @@
     }
 
     if (test_case.form_flags.is_complete_credit_card_form.has_value()) {
-      EXPECT_EQ(form_structure->IsCompleteCreditCardForm(),
-                *test_case.form_flags.is_complete_credit_card_form);
+      auto [completeness, expected_result] =
+          *test_case.form_flags.is_complete_credit_card_form;
+      EXPECT_EQ(form_structure->IsCompleteCreditCardForm(completeness),
+                expected_result);
     }
     if (test_case.form_flags.field_count) {
       ASSERT_EQ(*test_case.form_flags.field_count,
diff --git a/components/autofill/core/browser/test_utils/autofill_form_test_utils.h b/components/autofill/core/browser/test_utils/autofill_form_test_utils.h
index ee15e80..efb8681 100644
--- a/components/autofill/core/browser/test_utils/autofill_form_test_utils.h
+++ b/components/autofill/core/browser/test_utils/autofill_form_test_utils.h
@@ -6,10 +6,12 @@
 #define COMPONENTS_AUTOFILL_CORE_BROWSER_TEST_UTILS_AUTOFILL_FORM_TEST_UTILS_H_
 
 #include <optional>
+#include <utility>
 #include <vector>
 
 #include "components/autofill/core/browser/autofill_field.h"
 #include "components/autofill/core/browser/field_types.h"
+#include "components/autofill/core/browser/form_structure.h"
 #include "components/autofill/core/common/autocomplete_parsing_util.h"
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill/core/common/form_field_data.h"
@@ -92,8 +94,12 @@
   bool should_be_queried = false;
   bool should_be_uploaded = false;
   bool has_author_specified_types = false;
-  // The implicit default value `std::nullopt` means no checking.
-  std::optional<bool> is_complete_credit_card_form;
+  // The implicit default value `std::nullopt` means no checking. The first
+  // value is the argument for `IsCompleteCreditCardForm()` specifying the
+  // required completeness level of the credit card form. The second value is
+  // the expected result of `IsCompleteCreditCardForm()`.
+  std::optional<std::pair<FormStructure::CreditCardFormCompleteness, bool>>
+      is_complete_credit_card_form;
   std::optional<int> field_count;
   std::optional<int> autofill_count;
   std::optional<int> section_count;
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp
index 68d639f..343e406 100644
--- a/components/autofill_strings.grdp
+++ b/components/autofill_strings.grdp
@@ -223,6 +223,18 @@
     Show cards from your Google Account
   </message>
 
+  <message name="IDS_AUTOFILL_SAVE_AND_FILL_SUGGESTION_TITLE" desc="Title for the `Save and Fill` suggestion shown in the Autofill dropdown for users who don't have any cards saved in Autofill. When the suggestion is clicked, users are offered to save and fill a credit card with a single click.">
+    Save card for faster checkout
+  </message>
+
+  <message name="IDS_AUTOFILL_LOCAL_SAVE_AND_FILL_SUGGESTION_DESCRIPTION" desc="Description for the local `Save and Fill` suggestion shown in the Autofill dropdown for users who don't have any cards saved in Autofill. When the suggestion is clicked, users are offered to save and fill a credit card with a single click">
+    Autofill next time. Card is saved on your device.
+  </message>
+
+  <message name="IDS_AUTOFILL_SERVER_SAVE_AND_FILL_SUGGESTION_DESCRIPTION" desc="Description for the server `Save and Fill` suggestion shown in the Autofill dropdown for users who don't have any cards saved in Autofill. When the suggestion is clicked, users are offered to save and fill a credit card with a single click">
+    Autofill next time. Card is encrypted when saved.
+  </message>
+
   <if expr="not is_android">
     <message name="IDS_AUTOFILL_POPUP_ACCESSIBLE_NODE_DATA" desc="The accessibility text to speak when we display an autofill popup.">
       Autofill
diff --git a/components/autofill_strings_grdp/IDS_AUTOFILL_LOCAL_SAVE_AND_FILL_SUGGESTION_DESCRIPTION.png.sha1 b/components/autofill_strings_grdp/IDS_AUTOFILL_LOCAL_SAVE_AND_FILL_SUGGESTION_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..8fe81c0
--- /dev/null
+++ b/components/autofill_strings_grdp/IDS_AUTOFILL_LOCAL_SAVE_AND_FILL_SUGGESTION_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+097afa3b0efd3962a6363120266ee65ec1e6b845
\ No newline at end of file
diff --git a/components/autofill_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_SUGGESTION_TITLE.png.sha1 b/components/autofill_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_SUGGESTION_TITLE.png.sha1
new file mode 100644
index 0000000..29f1e386
--- /dev/null
+++ b/components/autofill_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_SUGGESTION_TITLE.png.sha1
@@ -0,0 +1 @@
+5b9ede374f6c0ecd633d0f6d62b5fed894bf8137
\ No newline at end of file
diff --git a/components/autofill_strings_grdp/IDS_AUTOFILL_SERVER_SAVE_AND_FILL_SUGGESTION_DESCRIPTION.png.sha1 b/components/autofill_strings_grdp/IDS_AUTOFILL_SERVER_SAVE_AND_FILL_SUGGESTION_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..8ca44ec
--- /dev/null
+++ b/components/autofill_strings_grdp/IDS_AUTOFILL_SERVER_SAVE_AND_FILL_SUGGESTION_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+e5c91ec05258e56ea4bdbaaf8a71fa6cf5101360
\ No newline at end of file
diff --git a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomCoordinator.java b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomCoordinator.java
index b32ced3e..5be5d3d 100644
--- a/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomCoordinator.java
+++ b/components/browser_ui/accessibility/android/java/src/org/chromium/components/browser_ui/accessibility/PageZoomCoordinator.java
@@ -162,7 +162,7 @@
     /** Clean-up views and children during destruction. */
     public void destroy() {
         if (mWebContentsObserver != null) {
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
         }
 
         if (mView != null) {
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java
index ed8e0bf..26c6a410 100644
--- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java
+++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetObserverTest.java
@@ -9,7 +9,6 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.graphics.Color;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -130,8 +129,7 @@
                                     new ScrimCoordinator(
                                             sTestRule.getActivity(),
                                             /* systemUiScrimDelegate= */ null,
-                                            rootView,
-                                            Color.WHITE);
+                                            rootView);
                             Supplier<ScrimCoordinator> scrimSupplier = () -> mScrimCoordinator;
                             Callback<View> initializedCallback = (v) -> {};
                             return new BottomSheetControllerImpl(
diff --git a/components/browser_ui/display_cutout/android/java/src/org/chromium/components/browser_ui/display_cutout/DisplayCutoutController.java b/components/browser_ui/display_cutout/android/java/src/org/chromium/components/browser_ui/display_cutout/DisplayCutoutController.java
index 33ea65fb..09521f8 100644
--- a/components/browser_ui/display_cutout/android/java/src/org/chromium/components/browser_ui/display_cutout/DisplayCutoutController.java
+++ b/components/browser_ui/display_cutout/android/java/src/org/chromium/components/browser_ui/display_cutout/DisplayCutoutController.java
@@ -223,7 +223,7 @@
         updateInsetObserver(null);
         updateBrowserCutoutObserver(null);
         if (mWebContentsObserver != null) {
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
             mWebContentsObserver = null;
         }
         mWindow = null;
@@ -271,7 +271,7 @@
         }
 
         if (mWebContentsObserver != null) {
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
             mWebContentsObserver = null;
         }
 
diff --git a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java
index 4512c08..6b1211b 100644
--- a/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java
+++ b/components/browser_ui/media/android/java/src/org/chromium/components/browser_ui/media/MediaSessionHelper.java
@@ -289,7 +289,7 @@
 
         mWebContents = webContents;
 
-        if (mWebContentsObserver != null) mWebContentsObserver.destroy();
+        if (mWebContentsObserver != null) mWebContentsObserver.observe(null);
 
         mMediaImageManager.setWebContents(mWebContents);
 
@@ -428,7 +428,7 @@
     public void destroy() {
         cleanupMediaSessionObserver();
         hideNotificationImmediately();
-        if (mWebContentsObserver != null) mWebContentsObserver.destroy();
+        if (mWebContentsObserver != null) mWebContentsObserver.observe(null);
         mWebContentsObserver = null;
         if (mLargeIconBridge != null) mLargeIconBridge.destroy();
         mLargeIconBridge = null;
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimCoordinator.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimCoordinator.java
index 6e68395..6bd009f 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimCoordinator.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimCoordinator.java
@@ -10,9 +10,11 @@
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
 
 import org.chromium.base.ObserverList;
 import org.chromium.base.supplier.Supplier;
+import org.chromium.components.browser_ui.widget.R;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.modelutil.PropertyModel;
 import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
@@ -99,13 +101,13 @@
      * @param context An Android {@link Context} for creating the view.
      * @param systemUiScrimDelegate A means of changing the scrim over the system UI.
      * @param parent The {@link ViewGroup} the scrim should exist in.
-     * @param defaultColor The default color of the scrim.
      */
     public ScrimCoordinator(
             Context context,
             @Nullable SystemUiScrimDelegate systemUiScrimDelegate,
-            ViewGroup parent,
-            @ColorInt int defaultColor) {
+            ViewGroup parent) {
+        @ColorInt
+        int defaultScrimColor = ContextCompat.getColor(context, R.color.default_scrim_color);
         mMediator =
                 new ScrimMediator(
                         () -> {
@@ -114,14 +116,20 @@
                             mView = null;
                             mChangeProcessor = null;
                         },
-                        systemUiScrimDelegate);
+                        systemUiScrimDelegate,
+                        defaultScrimColor);
         mScrimViewBuilder =
                 () -> {
-                    ScrimView view = new ScrimView(context, parent, defaultColor);
+                    ScrimView view = new ScrimView(context, parent);
                     return view;
                 };
     }
 
+    /** Returns the default scrim color, not the currently shown color. */
+    public @ColorInt int getDefaultScrimColor() {
+        return mMediator.getDefaultScrimColor();
+    }
+
     /**
      * Show the scrim.
      *
@@ -198,12 +206,18 @@
 
     /**
      * Manually set the alpha for the scrim.
+     *
      * @param alpha The alpha in range [0, 1].
      */
     public void setAlpha(float alpha) {
+        // TODO(skym): This method should take a PropertyModel as well.
         mMediator.setAlpha(alpha);
     }
 
+    public void setScrimColor(@ColorInt int scrimColor, PropertyModel propertyModel) {
+        mMediator.setScrimColor(scrimColor, propertyModel);
+    }
+
     /** Clean up this coordinator. */
     public void destroy() {
         mMediator.destroy();
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimMediator.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimMediator.java
index ba8514f..7bdb34fb 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimMediator.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimMediator.java
@@ -18,10 +18,9 @@
 import org.chromium.base.MathUtils;
 import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener;
 import org.chromium.ui.interpolators.Interpolators;
-import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
-import org.chromium.ui.modelutil.PropertyObservable;
-import org.chromium.ui.modelutil.PropertyObservable.PropertyObserver;
+
+import java.util.Objects;
 
 /** This class holds the animation and related business logic for the scrim. */
 class ScrimMediator implements ScrimCoordinator.TouchEventDelegate {
@@ -31,7 +30,7 @@
     /** A means of changing the system UI color. */
     private final @Nullable ScrimCoordinator.SystemUiScrimDelegate mSystemUiScrimDelegate;
 
-    private final PropertyObserver<PropertyKey> mOnModelChange = this::onModelChange;
+    private final @ColorInt int mDefaultScrimColor;
 
     /** The animator for fading the view in. */
     private ValueAnimator mOverlayFadeInAnimator;
@@ -59,12 +58,20 @@
     /**
      * @param scrimHiddenRunnable A mechanism for hiding the scrim.
      * @param systemUiScrimDelegate A means of changing the scrim over the system UI.
+     * @param defaultScrimColor The color of the scrim when not explicitly set.
      */
     ScrimMediator(
             @NonNull Runnable scrimHiddenRunnable,
-            @Nullable ScrimCoordinator.SystemUiScrimDelegate systemUiScrimDelegate) {
+            @Nullable ScrimCoordinator.SystemUiScrimDelegate systemUiScrimDelegate,
+            @ColorInt int defaultScrimColor) {
         mScrimHiddenRunnable = scrimHiddenRunnable;
         mSystemUiScrimDelegate = systemUiScrimDelegate;
+        mDefaultScrimColor = defaultScrimColor;
+    }
+
+    /* package */ @ColorInt
+    int getDefaultScrimColor() {
+        return mDefaultScrimColor;
     }
 
     /** Triggers a fade in of the scrim creating a new animation if necessary. */
@@ -79,21 +86,28 @@
         assert model.get(ScrimProperties.ANCHOR_VIEW) != null
                 : "The anchor for the scrim cannot be null.";
 
-        if (mModel != null && mSystemUiScrimDelegate != null) {
-            mModel.removeObserver(mOnModelChange);
-        }
         mModel = model;
-        if (mModel != null) {
-            mModel.set(ScrimProperties.TOUCH_EVENT_DELEGATE, this);
-        }
+        mModel.set(ScrimProperties.TOUCH_EVENT_DELEGATE, this);
         mIsHidingOrHidden = false;
 
-        // Pass the current scrim color to the SystemUiScrimDelegate.
-        if (mSystemUiScrimDelegate != null
-                && model.getAllSetProperties().contains(ScrimProperties.BACKGROUND_COLOR)) {
-            @ColorInt int color = model.get(ScrimProperties.BACKGROUND_COLOR);
-            mSystemUiScrimDelegate.setScrimColor(color);
-            mModel.addObserver(mOnModelChange);
+        // TODO(crbug.com/371034867): This flag and the usage of it should be deleted. This is
+        // currently needed because SigninAccountPickerCoordinator incorrectly consumes the color in
+        // its #setScrimColor() observer method. Once the scrim logic publishes more easily consumed
+        // color + alpha values things can be cleaned up.
+        boolean colorIsDefault = false;
+
+        // When clients do not specify a background color, use the default.
+        if (mModel.get(ScrimProperties.BACKGROUND_COLOR) == ScrimProperties.INVALID_COLOR) {
+            mModel.set(ScrimProperties.BACKGROUND_COLOR, mDefaultScrimColor);
+            colorIsDefault = true;
+        }
+
+        if (mSystemUiScrimDelegate != null) {
+            if (!colorIsDefault) {
+                // Pass the current scrim color to the SystemUiScrimDelegate.
+                @ColorInt int currentScrimColor = model.get(ScrimProperties.BACKGROUND_COLOR);
+                mSystemUiScrimDelegate.setScrimColor(currentScrimColor);
+            }
         }
 
         // Make sure alpha is reset to 0 since the model may be reused.
@@ -128,15 +142,6 @@
         runFadeAnimation(mOverlayFadeInAnimator);
     }
 
-    private void onModelChange(
-            PropertyObservable<PropertyKey> source, @Nullable PropertyKey propertyKey) {
-        assert mSystemUiScrimDelegate != null;
-        if (propertyKey == ScrimProperties.BACKGROUND_COLOR) {
-            @ColorInt int color = mModel.get(ScrimProperties.BACKGROUND_COLOR);
-            mSystemUiScrimDelegate.setScrimColor(color);
-        }
-    }
-
     private int getAnimationDuration(int animDurationMs) {
         return mDisableAnimationForTesting ? 0 : animDurationMs;
     }
@@ -229,16 +234,20 @@
         mCurrentVisibility = isVisible;
 
         if (mIsHidingOrHidden && !isVisible && mModel != null) {
-            if (mSystemUiScrimDelegate != null) {
-                mModel.removeObserver(mOnModelChange);
-            }
             mModel = null;
             mScrimHiddenRunnable.run();
         }
     }
 
+    /*package */ void setScrimColor(@ColorInt int scrimColor, PropertyModel propertyModel) {
+        if (!Objects.equals(mModel, propertyModel)) return;
+        mModel.set(ScrimProperties.BACKGROUND_COLOR, scrimColor);
+        mSystemUiScrimDelegate.setScrimColor(scrimColor);
+    }
+
     /**
      * Runs an animation for this view. If one is running, the existing one will be canceled.
+     *
      * @param fadeAnimation The animation to run.
      */
     private void runFadeAnimation(Animator fadeAnimation) {
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimProperties.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimProperties.java
index 1b97c1b..ebf44fc 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimProperties.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimProperties.java
@@ -60,7 +60,7 @@
     /* package */ static final WritableFloatPropertyKey ALPHA = new WritableFloatPropertyKey();
 
     /**
-     * The background color for the scrim. If null, a default color will be set as the background.
+     * The background color for the scrim. If not set a default color will be set as the background.
      */
     public static final WritableIntPropertyKey BACKGROUND_COLOR = new WritableIntPropertyKey();
 
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimTest.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimTest.java
index 45a90a7..52d305eeb 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimTest.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimTest.java
@@ -128,8 +128,7 @@
                     mAnchorView = new View(sActivity);
                     sParent.addView(mAnchorView);
 
-                    mScrimCoordinator =
-                            new ScrimCoordinator(sActivity, mScrimDelegate, sParent, Color.RED);
+                    mScrimCoordinator = new ScrimCoordinator(sActivity, mScrimDelegate, sParent);
 
                     mDelegatedEventHelper = new CallbackHelper();
                     mCustomGestureDetector =
@@ -233,7 +232,7 @@
         assertScrimColor(Color.GREEN);
         assertEquals(Color.GREEN, mScrimColorCallbackHelper.getOnlyPayloadBlocking().intValue());
 
-        ThreadUtils.runOnUiThreadBlocking(() -> model.set(BACKGROUND_COLOR, Color.RED));
+        ThreadUtils.runOnUiThreadBlocking(() -> mScrimCoordinator.setScrimColor(Color.RED, model));
         assertScrimColor(Color.RED);
         assertEquals(Color.RED, mScrimColorCallbackHelper.getPayloadByIndexBlocking(1).intValue());
     }
diff --git a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimView.java b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimView.java
index 50544b4..8a49470 100644
--- a/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimView.java
+++ b/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/scrim/ScrimView.java
@@ -9,7 +9,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 
-import androidx.annotation.ColorInt;
 import androidx.annotation.VisibleForTesting;
 
 import org.chromium.ui.UiUtils;
@@ -23,9 +22,6 @@
     /** The view that the scrim should exist in. */
     private final ViewGroup mParent;
 
-    /** The default background color. */
-    private final int mDefaultBackgroundColor;
-
     /** A means of passing all touch events to an external handler. */
     private ScrimCoordinator.TouchEventDelegate mEventDelegate;
 
@@ -33,16 +29,14 @@
      * @param context An Android {@link Context} for creating the view.
      * @param parent The {@link ViewGroup} the scrim should exist in.
      */
-    public ScrimView(Context context, ViewGroup parent, @ColorInt int defaultColor) {
+    public ScrimView(Context context, ViewGroup parent) {
         super(context);
         mParent = parent;
         setFocusable(false);
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
-        mDefaultBackgroundColor = defaultColor;
 
         setAlpha(0.0f);
         setVisibility(View.GONE);
-        setBackgroundColor(mDefaultBackgroundColor);
         setLayoutParams(
                 new ViewGroup.MarginLayoutParams(
                         ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
@@ -79,12 +73,6 @@
     }
 
     @Override
-    public void setBackgroundColor(@ColorInt int color) {
-        super.setBackgroundColor(
-                color == ScrimProperties.INVALID_COLOR ? mDefaultBackgroundColor : color);
-    }
-
-    @Override
     public boolean onTouchEvent(MotionEvent e) {
         if (mEventDelegate != null && mEventDelegate.onTouchEvent(e)) return true;
         return super.onTouchEvent(e);
diff --git a/components/collaboration_strings.grdp b/components/collaboration_strings.grdp
index 86bfd9d0..3bb01e7 100644
--- a/components/collaboration_strings.grdp
+++ b/components/collaboration_strings.grdp
@@ -33,6 +33,15 @@
     <message name ="IDS_COLLABORATION_SOMETHING_WENT_WRONG_BODY" desc="The body of of the error dialog something went wrong." formatter_data="android_java">
       There was an error. Try again.
     </message>
+    <message name ="IDS_COLLABORATION_SHARE_SHEET_TITLE" desc="The title of the share sheet when sharing a tab group." formatter_data="android_java">
+      Collaborate on tab group
+    </message>
+    <message name ="IDS_COLLABORATION_SHARE_SHEET_MESSAGE" desc="The contents of the message when sharing a tab group." formatter_data="android_java">
+      <ph name="TAB_GROUP_DISPLAY_NAME">%1$s<ex>Vacation plan</ex></ph> link expires in 48 hours
+    </message>
+    <message name ="IDS_COLLABORATION_SHARE_SHEET_TAB_GROUP_FALLBACK_NAME" desc="Fallback name if the tab group was not named by the user." formatter_data="android_java">
+      Shared tab group
+    </message>
   </if>
   <!-- Error Messages -->
   <!-- /Data Sharing -->
diff --git a/components/collaboration_strings_grdp/IDS_COLLABORATION_SHARE_SHEET_MESSAGE.png.sha1 b/components/collaboration_strings_grdp/IDS_COLLABORATION_SHARE_SHEET_MESSAGE.png.sha1
new file mode 100644
index 0000000..17b451e
--- /dev/null
+++ b/components/collaboration_strings_grdp/IDS_COLLABORATION_SHARE_SHEET_MESSAGE.png.sha1
@@ -0,0 +1 @@
+1d88990072450331fdf41d795b37e1144804c5d1
\ No newline at end of file
diff --git a/components/collaboration_strings_grdp/IDS_COLLABORATION_SHARE_SHEET_TAB_GROUP_FALLBACK_NAME.png.sha1 b/components/collaboration_strings_grdp/IDS_COLLABORATION_SHARE_SHEET_TAB_GROUP_FALLBACK_NAME.png.sha1
new file mode 100644
index 0000000..a1b0c239
--- /dev/null
+++ b/components/collaboration_strings_grdp/IDS_COLLABORATION_SHARE_SHEET_TAB_GROUP_FALLBACK_NAME.png.sha1
@@ -0,0 +1 @@
+68a75e77993440ebe9f98e1a7afbc94c0c2eac8e
\ No newline at end of file
diff --git a/components/collaboration_strings_grdp/IDS_COLLABORATION_SHARE_SHEET_TITLE.png.sha1 b/components/collaboration_strings_grdp/IDS_COLLABORATION_SHARE_SHEET_TITLE.png.sha1
new file mode 100644
index 0000000..17b451e
--- /dev/null
+++ b/components/collaboration_strings_grdp/IDS_COLLABORATION_SHARE_SHEET_TITLE.png.sha1
@@ -0,0 +1 @@
+1d88990072450331fdf41d795b37e1144804c5d1
\ No newline at end of file
diff --git a/components/commerce/core/mojom/product_specifications.mojom b/components/commerce/core/mojom/product_specifications.mojom
index aac8d54d..e4643c1 100644
--- a/components/commerce/core/mojom/product_specifications.mojom
+++ b/components/commerce/core/mojom/product_specifications.mojom
@@ -13,6 +13,11 @@
   kV1 = 1,
 };
 
+enum ShowSetDisposition {
+  kInNewTabs = 0,
+  kInNewWindow = 1,
+};
+
 // Browser-side handler for Compare-related requests from a WebUI page.
 interface ProductSpecificationsHandler {
   // Updates the user's accepted disclosure version for product specifications.
@@ -39,10 +44,16 @@
 
   // Show the product specifications set based on the uuid, `in_new_tab`
   // indicates whether the browser should open the set in a new tab or
-  // current tab.
+  // current tab. If in a new tab, the new tab will be focused.
   ShowProductSpecificationsSetForUuid(mojo_base.mojom.Uuid uuid,
       bool in_new_tab);
 
+  // Show the product specifications sets for the given uuids in either new tabs
+  // or a new browser window. If in new tabs, the tabs will be opened in the
+  // background. If in a new window, the window will be focused.
+  ShowProductSpecificationsSetsForUuids(
+      array<mojo_base.mojom.Uuid> uuids, ShowSetDisposition disposition);
+
   // Show the chrome://compare page either in the current tab or a new tab.
   ShowComparePage(bool in_new_tab);
 
diff --git a/components/commerce/core/webui/product_specifications_handler.cc b/components/commerce/core/webui/product_specifications_handler.cc
index b8b4006..0360287 100644
--- a/components/commerce/core/webui/product_specifications_handler.cc
+++ b/components/commerce/core/webui/product_specifications_handler.cc
@@ -64,6 +64,16 @@
   delegate_->ShowProductSpecificationsSetForUuid(uuid, in_new_tab);
 }
 
+void ProductSpecificationsHandler::ShowProductSpecificationsSetsForUuids(
+    const std::vector<base::Uuid>& uuids,
+    const product_specifications::mojom::ShowSetDisposition disposition) {
+  if (!delegate_) {
+    return;
+  }
+
+  delegate_->ShowProductSpecificationsSetsForUuids(uuids, disposition);
+}
+
 void ProductSpecificationsHandler::ShowComparePage(bool in_new_tab) {
   if (!delegate_) {
     return;
diff --git a/components/commerce/core/webui/product_specifications_handler.h b/components/commerce/core/webui/product_specifications_handler.h
index 8543ebb..2158d626 100644
--- a/components/commerce/core/webui/product_specifications_handler.h
+++ b/components/commerce/core/webui/product_specifications_handler.h
@@ -41,6 +41,14 @@
     virtual void ShowProductSpecificationsSetForUuid(const base::Uuid& uuid,
                                                      bool in_new_tab) = 0;
 
+    // Show the product specifications sets for the given UUIDs. The disposition
+    // indicates how the sets should be opened (i.e. in new tabs or in a new
+    // window).
+    virtual void ShowProductSpecificationsSetsForUuids(
+        const std::vector<base::Uuid>& uuids,
+        const product_specifications::mojom::ShowSetDisposition
+            disposition) = 0;
+
     // Show the sync setup flow for Compare.
     virtual void ShowSyncSetupFlow() = 0;
 
@@ -72,6 +80,10 @@
   void ShowSyncSetupFlow() override;
   void ShowProductSpecificationsSetForUuid(const base::Uuid& uuid,
                                            bool in_new_tab) override;
+  void ShowProductSpecificationsSetsForUuids(
+      const std::vector<base::Uuid>& uuids,
+      const product_specifications::mojom::ShowSetDisposition disposition)
+      override;
   void ShowComparePage(bool in_new_tab) override;
   void GetPageTitleFromHistory(
       const GURL& url,
diff --git a/components/commerce/core/webui/product_specifications_handler_unittest.cc b/components/commerce/core/webui/product_specifications_handler_unittest.cc
index f9fd1f1..c7fca15 100644
--- a/components/commerce/core/webui/product_specifications_handler_unittest.cc
+++ b/components/commerce/core/webui/product_specifications_handler_unittest.cc
@@ -79,6 +79,12 @@
               ShowProductSpecificationsSetForUuid,
               (const base::Uuid& uuid, bool in_new_tab),
               (override));
+  MOCK_METHOD(
+      void,
+      ShowProductSpecificationsSetsForUuids,
+      (const std::vector<base::Uuid>& uuids,
+       const product_specifications::mojom::ShowSetDisposition disposition),
+      (override));
   MOCK_METHOD(void, ShowSyncSetupFlow, (), (override));
   MOCK_METHOD(void, ShowComparePage, (bool in_new_tab), (override));
 };
@@ -290,4 +296,19 @@
   handler_->ShowComparePage(true);
 }
 
+TEST_F(ProductSpecificationsHandlerTest,
+       TestShowProductSpecificationsSetsForUuids) {
+  base::Uuid uuid_one = base::Uuid::GenerateRandomV4();
+  base::Uuid uuid_two = base::Uuid::GenerateRandomV4();
+  const auto disposition =
+      product_specifications::mojom::ShowSetDisposition::kInNewTabs;
+
+  EXPECT_CALL(*delegate_,
+              ShowProductSpecificationsSetsForUuids(
+                  testing::ElementsAre(uuid_one, uuid_two), disposition))
+      .Times(1);
+  handler_->ShowProductSpecificationsSetsForUuids({uuid_one, uuid_two},
+                                                  disposition);
+}
+
 }  // namespace commerce
diff --git a/components/commerce_strings.grdp b/components/commerce_strings.grdp
index b85a46f..c4806d9 100644
--- a/components/commerce_strings.grdp
+++ b/components/commerce_strings.grdp
@@ -688,9 +688,15 @@
   <message name="IDS_COMPARE_CONTEXT_MENU_OPEN_IN_NEW_TAB" desc="The label for the comparison table list dropdown menu item for opening a comparison table in a new tab.">
     Open in new tab
   </message>
+  <message name="IDS_COMPARE_CONTEXT_MENU_OPEN_IN_NEW_WINDOW" desc="The label for the comparison table list dropdown menu item for opening a comparison table in a new window.">
+    Open in new window
+  </message>
   <message name="IDS_COMPARE_CONTEXT_MENU_OPEN_ALL_WITH_COUNT" desc="The label for the comparison table list dropdown menu item for opening comparison table(s) in new tab(s).">
     Open all (<ph name="ITEMS">$1<ex>3</ex></ph>)
   </message>
+  <message name="IDS_COMPARE_CONTEXT_MENU_OPEN_ALL_IN_NEW_WINDOW_WITH_COUNT" desc="The label for the comparison table list dropdown menu item for opening comparison table(s) in a new window.">
+    Open all (<ph name="ITEMS">$1<ex>3</ex></ph>) in new window
+  </message>
   <message name="IDS_COMPARE_CONTEXT_MENU_RENAME" desc="The label for the comparison table list dropdown menu item for renaming a comparison table.">
     Rename
   </message>
diff --git a/components/commerce_strings_grdp/IDS_COMPARE_CONTEXT_MENU_OPEN_ALL_IN_NEW_WINDOW_WITH_COUNT.png.sha1 b/components/commerce_strings_grdp/IDS_COMPARE_CONTEXT_MENU_OPEN_ALL_IN_NEW_WINDOW_WITH_COUNT.png.sha1
new file mode 100644
index 0000000..c5d0559
--- /dev/null
+++ b/components/commerce_strings_grdp/IDS_COMPARE_CONTEXT_MENU_OPEN_ALL_IN_NEW_WINDOW_WITH_COUNT.png.sha1
@@ -0,0 +1 @@
+4ff8f1b65813a85488a4c7e08f971ccd6ce2f508
\ No newline at end of file
diff --git a/components/commerce_strings_grdp/IDS_COMPARE_CONTEXT_MENU_OPEN_IN_NEW_WINDOW.png.sha1 b/components/commerce_strings_grdp/IDS_COMPARE_CONTEXT_MENU_OPEN_IN_NEW_WINDOW.png.sha1
new file mode 100644
index 0000000..9f19681
--- /dev/null
+++ b/components/commerce_strings_grdp/IDS_COMPARE_CONTEXT_MENU_OPEN_IN_NEW_WINDOW.png.sha1
@@ -0,0 +1 @@
+898d972c5351959d96987d4d467dba68ce6f1a04
\ No newline at end of file
diff --git a/components/content_settings/browser/page_specific_content_settings.cc b/components/content_settings/browser/page_specific_content_settings.cc
index f233b27..74c4992 100644
--- a/components/content_settings/browser/page_specific_content_settings.cc
+++ b/components/content_settings/browser/page_specific_content_settings.cc
@@ -1290,9 +1290,6 @@
     MaybeUpdateLocationBar();
   }
 
-  // The PiP window does not support blocked indicators, hence there is no need
-  // to start a timer to display it.
-  if (!delegate_->IsPiPWindow(GetWebContents())) {
     // Camera and/or Mic is blocked, start a blocked indicator's dismiss timer.
     if (microphone_camera_state_.Has(kMicrophoneBlocked)) {
       StartBlockedIndicatorTimer(ContentSettingsType::MEDIASTREAM_MIC);
@@ -1300,7 +1297,6 @@
     if (microphone_camera_state_.Has(kCameraBlocked)) {
       StartBlockedIndicatorTimer(ContentSettingsType::MEDIASTREAM_CAMERA);
     }
-  }
 }
 
 void PageSpecificContentSettings::AddPermissionUsageObserver(
diff --git a/components/content_settings/browser/page_specific_content_settings.h b/components/content_settings/browser/page_specific_content_settings.h
index c2eed88d..cbe284c1 100644
--- a/components/content_settings/browser/page_specific_content_settings.h
+++ b/components/content_settings/browser/page_specific_content_settings.h
@@ -168,10 +168,6 @@
     virtual content::WebContents* MaybeGetSyncedWebContentsForPictureInPicture(
         content::WebContents* web_contents) = 0;
 
-    // Returns `true` if `web_contents` represents a PiP window. Returns `false`
-    // otherwise.
-    virtual bool IsPiPWindow(content::WebContents* web_contents) = 0;
-
     // Notifies the delegate a particular content settings type was allowed for
     // the first time on this page.
     virtual void OnContentAllowed(ContentSettingsType type) = 0;
diff --git a/components/content_settings/browser/test_page_specific_content_settings_delegate.cc b/components/content_settings/browser/test_page_specific_content_settings_delegate.cc
index 2af19c5..22d4a20 100644
--- a/components/content_settings/browser/test_page_specific_content_settings_delegate.cc
+++ b/components/content_settings/browser/test_page_specific_content_settings_delegate.cc
@@ -63,9 +63,4 @@
   return false;
 }
 
-bool TestPageSpecificContentSettingsDelegate::IsPiPWindow(
-    content::WebContents* web_contents) {
-  return false;
-}
-
 }  // namespace content_settings
diff --git a/components/content_settings/browser/test_page_specific_content_settings_delegate.h b/components/content_settings/browser/test_page_specific_content_settings_delegate.h
index 0b3d6c8c..6a01631 100644
--- a/components/content_settings/browser/test_page_specific_content_settings_delegate.h
+++ b/components/content_settings/browser/test_page_specific_content_settings_delegate.h
@@ -36,7 +36,6 @@
   bool IsBlockedOnSystemLevel(ContentSettingsType type) override;
   bool IsFrameAllowlistedForJavaScript(
       content::RenderFrameHost* render_frame_host) override;
-  bool IsPiPWindow(content::WebContents* web_contents) override;
 
  private:
   raw_ptr<PrefService> prefs_;
diff --git a/components/cronet/gn2bp/gen_android_bp.py b/components/cronet/gn2bp/gen_android_bp.py
index 6d62750..6e06316 100755
--- a/components/cronet/gn2bp/gen_android_bp.py
+++ b/components/cronet/gn2bp/gen_android_bp.py
@@ -2042,7 +2042,14 @@
     module.handle_static_inline = True
 
   module.bindgen_flags = get_bindgen_flags(target.args)
-  module.header_libs = ["fake_header_libs"]
+  # This ensures that any CC file that is being processed through the
+  # rust_bindgen module is able to #include files relative to the root of the
+  # repository.
+  #
+  # Note: this module is not part of the generated build rules; it is expected
+  # to already be present in AOSP (currently, in Android.extras.bp). See
+  # https://r.android.com/3413202.
+  module.header_libs = ["cronet_repository_root_include_dirs_anchor"]
   module.min_sdk_version = 31
   module.apex_available = [tethering_apex]
   blueprint.add_module(module)
diff --git a/components/cronet/tools/utils.py b/components/cronet/tools/utils.py
index 48112591..a09dd7c9 100755
--- a/components/cronet/tools/utils.py
+++ b/components/cronet/tools/utils.py
@@ -22,7 +22,7 @@
 GN_PATH = os.path.join(REPOSITORY_ROOT, 'buildtools/linux64/gn')
 NINJA_PATH = os.path.join(REPOSITORY_ROOT, 'third_party/ninja/ninja')
 ARCHS = ['x86', 'x64', 'arm', 'arm64', 'riscv64']
-AOSP_EXTRA_ARGS = ('is_cronet_for_aosp_build=true', 'use_nss_certs=false')
+AOSP_EXTRA_ARGS = ('is_cronet_for_aosp_build=true', 'use_nss_certs=false', 'use_allocator_shim=false')
 _GN_ARG_MATCHER = re.compile("^.*=.*$")
 
 
diff --git a/components/dbus/thread_linux/dbus_thread_linux.cc b/components/dbus/thread_linux/dbus_thread_linux.cc
index 7bdd83c..c53eeca 100644
--- a/components/dbus/thread_linux/dbus_thread_linux.cc
+++ b/components/dbus/thread_linux/dbus_thread_linux.cc
@@ -4,8 +4,10 @@
 
 #include "components/dbus/thread_linux/dbus_thread_linux.h"
 
+#include "base/no_destructor.h"
 #include "base/task/lazy_thread_pool_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
+#include "dbus/bus.h"
 
 namespace dbus_thread_linux {
 
@@ -21,10 +23,38 @@
         base::TaskTraits(base::MayBlock(), base::TaskPriority::USER_BLOCKING),
         base::SingleThreadTaskRunnerThreadMode::SHARED);
 
+scoped_refptr<dbus::Bus> CreateSharedSessionBus() {
+  dbus::Bus::Options options;
+  options.bus_type = dbus::Bus::SESSION;
+  options.connection_type = dbus::Bus::PRIVATE;
+  options.dbus_task_runner = GetTaskRunner();
+  return base::MakeRefCounted<dbus::Bus>(options);
+}
+
+scoped_refptr<dbus::Bus> CreateSharedSystemBus() {
+  dbus::Bus::Options options;
+  options.bus_type = dbus::Bus::SYSTEM;
+  options.connection_type = dbus::Bus::PRIVATE;
+  options.dbus_task_runner = GetTaskRunner();
+  return base::MakeRefCounted<dbus::Bus>(options);
+}
+
 }  // namespace
 
 scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() {
   return g_dbus_thread_task_runner.Get();
 }
 
+scoped_refptr<dbus::Bus> GetSharedSessionBus() {
+  static base::NoDestructor<scoped_refptr<dbus::Bus>> bus(
+      CreateSharedSessionBus());
+  return *bus;
+}
+
+scoped_refptr<dbus::Bus> GetSharedSystemBus() {
+  static base::NoDestructor<scoped_refptr<dbus::Bus>> bus(
+      CreateSharedSystemBus());
+  return *bus;
+}
+
 }  // namespace dbus_thread_linux
diff --git a/components/dbus/thread_linux/dbus_thread_linux.h b/components/dbus/thread_linux/dbus_thread_linux.h
index b5f849b..ed6d513 100644
--- a/components/dbus/thread_linux/dbus_thread_linux.h
+++ b/components/dbus/thread_linux/dbus_thread_linux.h
@@ -19,12 +19,27 @@
 #error On ChromeOS, use DBusThreadManager instead.
 #endif
 
+namespace dbus {
+class Bus;
+}
+
 namespace dbus_thread_linux {
 
 // Obtains a task runner to handle DBus IO for usage on desktop Linux.
 COMPONENT_EXPORT(COMPONENTS_DBUS)
 scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner();
 
+// Obtains a shared session bus for usage on desktop Linux. The task runner is
+// the same as the one obtained from GetTaskRunner(). This should be used for
+// all session bus operations except for those that require a named connection
+// (currently MPRIS is the only one). Must be called on the UI thread.
+COMPONENT_EXPORT(COMPONENTS_DBUS)
+scoped_refptr<dbus::Bus> GetSharedSessionBus();
+
+// The same as GetSharedSessionBus(), but for the system bus.
+COMPONENT_EXPORT(COMPONENTS_DBUS)
+scoped_refptr<dbus::Bus> GetSharedSystemBus();
+
 }  // namespace dbus_thread_linux
 
 #endif  // COMPONENTS_DBUS_THREAD_LINUX_DBUS_THREAD_LINUX_H_
diff --git a/components/device_signals/core/BUILD.gn b/components/device_signals/core/BUILD.gn
index 9b0b4db..ec43529 100644
--- a/components/device_signals/core/BUILD.gn
+++ b/components/device_signals/core/BUILD.gn
@@ -6,7 +6,6 @@
   testonly = true
   deps = [
     "//components/device_signals/core/browser:unit_tests",
-    "//components/device_signals/core/common:unit_tests",
     "//components/device_signals/core/system_signals:unit_tests",
   ]
 }
diff --git a/components/device_signals/core/common/BUILD.gn b/components/device_signals/core/common/BUILD.gn
index a92d72b..6a4e099f 100644
--- a/components/device_signals/core/common/BUILD.gn
+++ b/components/device_signals/core/common/BUILD.gn
@@ -39,20 +39,3 @@
 
   public_deps = [ "//base" ]
 }
-
-source_set("unit_tests") {
-  testonly = true
-  sources = [ "signals_features_unittest.cc" ]
-
-  deps = [
-    ":common",
-    ":features",
-    "//base",
-    "//base/test:test_support",
-    "//testing/gmock",
-    "//testing/gtest",
-  ]
-  if (is_win) {
-    deps += [ "//components/device_signals/core/common/win:unit_tests" ]
-  }
-}
diff --git a/components/device_signals/core/common/signals_features.cc b/components/device_signals/core/common/signals_features.cc
index f40e632..7a18aec 100644
--- a/components/device_signals/core/common/signals_features.cc
+++ b/components/device_signals/core/common/signals_features.cc
@@ -10,45 +10,6 @@
              "AllowClientCertificateReportingForUsers",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kNewEvSignalsEnabled,
-             "NewEvSignalsEnabled",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-const base::FeatureParam<bool> kDisableFileSystemInfo{
-    &kNewEvSignalsEnabled, "DisableFileSystemInfo", false};
-const base::FeatureParam<bool> kDisableSettings{&kNewEvSignalsEnabled,
-                                                "DisableSettings", false};
-const base::FeatureParam<bool> kDisableAntiVirus{&kNewEvSignalsEnabled,
-                                                 "DisableAntiVirus", false};
-const base::FeatureParam<bool> kDisableHotfix{&kNewEvSignalsEnabled,
-                                              "DisableHotfix", false};
-
-bool IsNewFunctionEnabled(NewEvFunction new_ev_function) {
-  // AntiVirus and Hotfix are considered "Launched". So only rely on the value
-  // of the kill-switch to control the feature's behavior.
-  bool disable_function = false;
-  switch (new_ev_function) {
-    case NewEvFunction::kFileSystemInfo:
-      disable_function = kDisableFileSystemInfo.Get();
-      break;
-    case NewEvFunction::kSettings:
-      disable_function = kDisableSettings.Get();
-      break;
-    case NewEvFunction::kAntiVirus:
-      disable_function = kDisableAntiVirus.Get();
-      break;
-    case NewEvFunction::kHotfix:
-      disable_function = kDisableHotfix.Get();
-      break;
-  }
-
-  if (!base::FeatureList::IsEnabled(kNewEvSignalsEnabled)) {
-    return false;
-  }
-
-  return !disable_function;
-}
-
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || \
     BUILDFLAG(IS_CHROMEOS)
 // Enables the triggering of device signals consent dialog when conditions met
diff --git a/components/device_signals/core/common/signals_features.h b/components/device_signals/core/common/signals_features.h
index 53eaa28..1f87333 100644
--- a/components/device_signals/core/common/signals_features.h
+++ b/components/device_signals/core/common/signals_features.h
@@ -13,23 +13,6 @@
 // Allows the reporting of client certificates for managed users.
 BASE_DECLARE_FEATURE(kAllowClientCertificateReportingForUsers);
 
-// Feature flag for new private SecureConnect functions exposing additional
-// device signals.
-BASE_DECLARE_FEATURE(kNewEvSignalsEnabled);
-
-// Feature parameters that can be used to turn off individual functions.
-extern const base::FeatureParam<bool> kDisableFileSystemInfo;
-extern const base::FeatureParam<bool> kDisableSettings;
-extern const base::FeatureParam<bool> kDisableAntiVirus;
-extern const base::FeatureParam<bool> kDisableHotfix;
-
-// Enum used to map a given function to its kill switch.
-enum class NewEvFunction { kFileSystemInfo, kSettings, kAntiVirus, kHotfix };
-
-// Returns true if the function pointed at by `new_ev_function` is considered
-// to be enabled based on the feature flag and its parameters.
-bool IsNewFunctionEnabled(NewEvFunction new_ev_function);
-
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || \
     BUILDFLAG(IS_CHROMEOS)
 BASE_DECLARE_FEATURE(kDeviceSignalsConsentDialog);
diff --git a/components/device_signals/core/common/signals_features_unittest.cc b/components/device_signals/core/common/signals_features_unittest.cc
deleted file mode 100644
index 82b26b4..0000000
--- a/components/device_signals/core/common/signals_features_unittest.cc
+++ /dev/null
@@ -1,89 +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 "components/device_signals/core/common/signals_features.h"
-
-#include "base/test/scoped_feature_list.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace enterprise_signals::features {
-
-class SignalsFeaturesTest : public testing::Test {
- protected:
-  const int min_enum_value_ = static_cast<int>(NewEvFunction::kFileSystemInfo);
-  const int max_enum_value_ = static_cast<int>(NewEvFunction::kHotfix);
-  base::test::ScopedFeatureList scoped_features_;
-};
-
-// Tests that IsNewFunctionEnabled will return false when the feature flag is
-// disabled.
-TEST_F(SignalsFeaturesTest, DisabledFeature) {
-  scoped_features_.InitAndDisableFeature(kNewEvSignalsEnabled);
-  for (int i = min_enum_value_; i <= max_enum_value_; i++) {
-    EXPECT_FALSE(IsNewFunctionEnabled(static_cast<NewEvFunction>(i)));
-  }
-}
-
-// Tests that IsNewFunctionEnabled will return true when the feature flag is
-// enabled, and no specific function is disabled.
-TEST_F(SignalsFeaturesTest, EnabledFeature) {
-  scoped_features_.InitAndEnableFeature(kNewEvSignalsEnabled);
-  for (int i = min_enum_value_; i <= max_enum_value_; i++) {
-    EXPECT_TRUE(IsNewFunctionEnabled(static_cast<NewEvFunction>(i)));
-  }
-}
-
-// Tests how IsNewFunctionEnabled behaves when the feature flag is enabled, but
-// the FileSystemInfo function is disabled.
-TEST_F(SignalsFeaturesTest, Enabled_DisableFileSystemInfo) {
-  scoped_features_.InitAndEnableFeatureWithParameters(
-      kNewEvSignalsEnabled, {{"DisableFileSystemInfo", "true"}});
-
-  for (int i = min_enum_value_; i <= max_enum_value_; i++) {
-    NewEvFunction current_function = static_cast<NewEvFunction>(i);
-    EXPECT_EQ(IsNewFunctionEnabled(current_function),
-              current_function != NewEvFunction::kFileSystemInfo);
-  }
-}
-
-// Tests how IsNewFunctionEnabled behaves when the feature flag is enabled, but
-// the Settings function is disabled.
-TEST_F(SignalsFeaturesTest, Enabled_DisableSetting) {
-  scoped_features_.InitAndEnableFeatureWithParameters(
-      kNewEvSignalsEnabled, {{"DisableSettings", "true"}});
-
-  for (int i = min_enum_value_; i <= max_enum_value_; i++) {
-    NewEvFunction current_function = static_cast<NewEvFunction>(i);
-    EXPECT_EQ(IsNewFunctionEnabled(current_function),
-              current_function != NewEvFunction::kSettings);
-  }
-}
-
-// Tests how IsNewFunctionEnabled behaves when the feature flag is enabled, but
-// the Antivirus function is disabled.
-TEST_F(SignalsFeaturesTest, Enabled_DisableAntiVirus) {
-  scoped_features_.InitAndEnableFeatureWithParameters(
-      kNewEvSignalsEnabled, {{"DisableAntiVirus", "true"}});
-
-  for (int i = min_enum_value_; i <= max_enum_value_; i++) {
-    NewEvFunction current_function = static_cast<NewEvFunction>(i);
-    EXPECT_EQ(IsNewFunctionEnabled(current_function),
-              current_function != NewEvFunction::kAntiVirus);
-  }
-}
-
-// Tests how IsNewFunctionEnabled behaves when the feature flag is enabled, but
-// the Hotfix function is disabled.
-TEST_F(SignalsFeaturesTest, Enabled_DisableHotfix) {
-  scoped_features_.InitAndEnableFeatureWithParameters(
-      kNewEvSignalsEnabled, {{"DisableHotfix", "true"}});
-
-  for (int i = min_enum_value_; i <= max_enum_value_; i++) {
-    NewEvFunction current_function = static_cast<NewEvFunction>(i);
-    EXPECT_EQ(IsNewFunctionEnabled(current_function),
-              current_function != NewEvFunction::kHotfix);
-  }
-}
-
-}  // namespace enterprise_signals::features
diff --git a/components/enterprise/watermarking/BUILD.gn b/components/enterprise/watermarking/BUILD.gn
index 659a9eb..3283fdd8 100644
--- a/components/enterprise/watermarking/BUILD.gn
+++ b/components/enterprise/watermarking/BUILD.gn
@@ -32,3 +32,19 @@
     "//testing/gtest",
   ]
 }
+
+source_set("watermark_test_utils") {
+  testonly = true
+
+  sources = [
+    "watermark_test_utils.cc",
+    "watermark_test_utils.h",
+  ]
+
+  deps = [ "//components/enterprise/watermarking" ]
+
+  public_deps = [
+    "//components/enterprise/watermarking/mojom",
+    "//skia",
+  ]
+}
diff --git a/components/enterprise/watermarking/DEPS b/components/enterprise/watermarking/DEPS
index 5e835e2..3eac807 100644
--- a/components/enterprise/watermarking/DEPS
+++ b/components/enterprise/watermarking/DEPS
@@ -1,6 +1,7 @@
 include_rules = [
   "+cc/paint",
   "+cc/test",
+  "+skia/ext",
   "+third_party/skia",
   "+ui/gfx"
 ]
diff --git a/components/enterprise/watermarking/watermark_test_utils.cc b/components/enterprise/watermarking/watermark_test_utils.cc
new file mode 100644
index 0000000..9959aaea
--- /dev/null
+++ b/components/enterprise/watermarking/watermark_test_utils.cc
@@ -0,0 +1,61 @@
+// Copyright 2025 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/enterprise/watermarking/watermark_test_utils.h"
+
+#include <utility>
+
+#include "base/memory/shared_memory_mapping.h"
+#include "components/enterprise/watermarking/mojom/watermark.mojom.h"
+#include "components/enterprise/watermarking/watermark.h"
+#include "skia/ext/font_utils.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkPictureRecorder.h"
+#include "third_party/skia/include/core/SkSerialProcs.h"
+#include "third_party/skia/include/core/SkStream.h"
+#include "third_party/skia/include/core/SkTextBlob.h"
+
+namespace enterprise_watermark {
+
+watermark::mojom::WatermarkBlockPtr MakeTestWatermarkBlock(
+    const std::string& watermark_text,
+    const SkSize watermark_size) {
+  // Initialize text blob
+  static constexpr SkScalar kTextSize = 30.0f;
+  SkFont font(skia::DefaultTypeface(), kTextSize, 1.0f, 0.0f);
+  sk_sp<SkTextBlob> blob =
+      SkTextBlob::MakeFromString(watermark_text.c_str(), font);
+
+  // Draw onto SkPicture-backed SkCanvas
+  SkPictureRecorder recorder;
+  SkCanvas* canvas = recorder.beginRecording(
+      SkRect{SkRect::MakeWH(watermark_size.fWidth, watermark_size.fHeight)});
+  SkPaint paint;
+  paint.setColor(SK_ColorWHITE);
+  canvas->drawTextBlob(blob.get(), 0.0f, 0.0f, paint);
+  sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
+
+  // Serialize SkPicture
+  SkDynamicMemoryWStream stream;
+  SkSerialProcs procs;
+  picture->serialize(&stream, &procs);
+  base::MappedReadOnlyRegion region_mapping =
+      base::ReadOnlySharedMemoryRegion::Create(stream.bytesWritten());
+  if (!region_mapping.IsValid()) {
+    return nullptr;
+  }
+  stream.copyTo(region_mapping.mapping.memory());
+
+  // Measure string dimensions
+  SkScalar text_width = font.measureText(
+      watermark_text.c_str(), watermark_text.size(), SkTextEncoding::kUTF8);
+
+  // Construct test data
+  return watermark::mojom::WatermarkBlockPtr(
+      std::in_place, std::move(region_mapping.region), text_width, kTextSize);
+}
+
+}  // namespace enterprise_watermark
diff --git a/components/enterprise/watermarking/watermark_test_utils.h b/components/enterprise/watermarking/watermark_test_utils.h
new file mode 100644
index 0000000..384905d9
--- /dev/null
+++ b/components/enterprise/watermarking/watermark_test_utils.h
@@ -0,0 +1,19 @@
+// Copyright 2025 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_ENTERPRISE_WATERMARKING_WATERMARK_TEST_UTILS_H_
+#define COMPONENTS_ENTERPRISE_WATERMARKING_WATERMARK_TEST_UTILS_H_
+
+#include "components/enterprise/watermarking/mojom/watermark.mojom-forward.h"
+#include "third_party/skia/include/core/SkSize.h"
+
+namespace enterprise_watermark {
+
+watermark::mojom::WatermarkBlockPtr MakeTestWatermarkBlock(
+    const std::string& watermark_text,
+    const SkSize watermark_size);
+
+}  // namespace enterprise_watermark
+
+#endif  // COMPONENTS_ENTERPRISE_WATERMARKING_WATERMARK_TEST_UTILS_H_
diff --git a/components/exo/surface_tree_host.cc b/components/exo/surface_tree_host.cc
index e47ead8..53d4059 100644
--- a/components/exo/surface_tree_host.cc
+++ b/components/exo/surface_tree_host.cc
@@ -380,9 +380,6 @@
           : std::make_optional(GetScaleFactor()),
       &frame);
 
-  // Update after resource is updated.
-  UpdateHostLayerOpacity();
-
   std::vector<GLbyte*> sync_tokens;
   // We track previously verified tokens and set them to be verified to avoid
   // the considerable overhead of flush verification in
@@ -493,17 +490,8 @@
     root_surface_->window()->SetBounds(updated_bounds);
   }
 
-  UpdateHostWindowOpaqueRegion();
-}
-
-void SurfaceTreeHost::UpdateHostLayerOpacity() {
-  ui::Layer* commit_target_layer = GetCommitTargetLayer();
-
   if (commit_target_layer == host_window_->layer()) {
     UpdateHostWindowOpaqueRegion();
-  } else if (commit_target_layer) {
-    commit_target_layer->SetFillsBoundsOpaquely(
-        ContentsFillsHostWindowOpaquely());
   }
 }
 
diff --git a/components/exo/surface_tree_host.h b/components/exo/surface_tree_host.h
index c7935fc..24004cc 100644
--- a/components/exo/surface_tree_host.h
+++ b/components/exo/surface_tree_host.h
@@ -210,10 +210,6 @@
   // It also updates `root_surface_origin_` accordingly to the origin.
   void UpdateSurfaceLayerSizeAndRootSurfaceOrigin();
 
-  // Updates the host layer's opacity. This has to be called after root
-  // surface's resource is updated.
-  void UpdateHostLayerOpacity();
-
   void UpdateHostWindowOpaqueRegion();
 
   bool client_submits_surfaces_in_pixel_coordinates() const {
diff --git a/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java b/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
index 9932818..c734b5c7 100644
--- a/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
+++ b/components/external_intents/android/java/src/org/chromium/components/external_intents/InterceptNavigationDelegateImpl.java
@@ -522,13 +522,23 @@
                         .getLastCommittedEntryIndexBeforeStartingNavigation();
         if (getLastCommittedEntryIndex() <= lastCommittedEntryIndexBeforeNavigation) return;
 
-        // http://crbug/426679 : we want to go back to the last committed entry index which
-        // was saved before this navigation, and remove the empty entries from the
-        // navigation history.
-        mClearAllForwardHistoryRequired = true;
-        mClient.getWebContents()
-                .getNavigationController()
-                .goToNavigationIndex(lastCommittedEntryIndexBeforeNavigation);
+        // Like clobbering below, changing navigation index could cancel the current navigation and
+        // delete the NavigationThrottle calling this code, leading to UAFs. Do the navigation
+        // asynchronously to avoid that.
+        PostTask.postTask(
+                TaskTraits.UI_DEFAULT,
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        // http://crbug.com/426679 : we want to go back to the last committed entry
+                        // index which was saved before this navigation, and remove the empty
+                        // entries from the navigation history.
+                        mClearAllForwardHistoryRequired = true;
+                        mClient.getWebContents()
+                                .getNavigationController()
+                                .goToNavigationIndex(lastCommittedEntryIndexBeforeNavigation);
+                    }
+                });
     }
 
     private void clobberMainFrame(GURL targetUrl, ExternalNavigationParams params) {
diff --git a/components/facilitated_payments/android/BUILD.gn b/components/facilitated_payments/android/BUILD.gn
index 6d6a9aac..90f89b6 100644
--- a/components/facilitated_payments/android/BUILD.gn
+++ b/components/facilitated_payments/android/BUILD.gn
@@ -8,6 +8,8 @@
   sources = [
     "facilitated_payments_api_client_android.cc",
     "facilitated_payments_api_client_android.h",
+    "secure_payload_android.cc",
+    "secure_payload_android.h",
   ]
 
   deps = [
@@ -22,7 +24,10 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "facilitated_payments_api_client_android_unittest.cc" ]
+  sources = [
+    "facilitated_payments_api_client_android_unittest.cc",
+    "secure_payload_android_unittest.cc",
+  ]
   deps = [
     ":android",
     "//components/facilitated_payments/core/browser",
diff --git a/components/facilitated_payments/android/java/BUILD.gn b/components/facilitated_payments/android/java/BUILD.gn
index b513444..d94cec0 100644
--- a/components/facilitated_payments/android/java/BUILD.gn
+++ b/components/facilitated_payments/android/java/BUILD.gn
@@ -10,6 +10,8 @@
   sources = [
     "src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClient.java",
     "src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientBridge.java",
+    "src/org/chromium/components/facilitated_payments/SecureData.java",
+    "src/org/chromium/components/facilitated_payments/SecurePayload.java",
   ]
   deps = [
     "//base:base_java",
@@ -24,5 +26,9 @@
 }
 
 generate_jni("jni_headers") {
-  sources = [ "src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientBridge.java" ]
+  sources = [
+    "src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientBridge.java",
+    "src/org/chromium/components/facilitated_payments/SecureData.java",
+    "src/org/chromium/components/facilitated_payments/SecurePayload.java",
+  ]
 }
diff --git a/components/facilitated_payments/android/java/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClient.java b/components/facilitated_payments/android/java/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClient.java
index ea268f92..16d351d0 100644
--- a/components/facilitated_payments/android/java/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClient.java
+++ b/components/facilitated_payments/android/java/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClient.java
@@ -9,12 +9,13 @@
 
 /**
  * Client for facilitated payment APIs, such as PIX. The default implementation cannot invoke
- * payments. An implementing subclass must provide a factory that builds its instances.
- * Example usage:
+ * payments. An implementing subclass must provide a factory that builds its instances. Example
+ * usage:
  *
- *  FacilitatedPaymentsApiClient apiClient =
- *      FacilitatedPaymentsApiClient.create(renderFrameHost, delegate);
- *  apiClient.isAvailable();
+ * <pre>
+ * FacilitatedPaymentsApiClient apiClient =
+ *      FacilitatedPaymentsApiClient.create(renderFrameHost,delegate); apiClient.isAvailable();
+ * </pre>
  */
 public class FacilitatedPaymentsApiClient {
     private static Factory sFactory;
@@ -152,7 +153,19 @@
      * Initiates the payment flow UI. Will invoke a delegate callback with the result.
      *
      * @param primaryAccount User's signed in account.
+     * @param securePayload The secure payload received from Payments backend that is required for
+     *     invoking the purchase action in Google Play Services.
+     */
+    public void invokePurchaseAction(CoreAccountInfo primaryAccount, SecurePayload securePayload) {
+        mDelegate.onPurchaseActionResultEnum(PurchaseActionResult.COULD_NOT_INVOKE);
+    }
+
+    /**
+     * Initiates the payment flow UI. Will invoke a delegate callback with the result.
+     *
+     * @param primaryAccount User's signed in account.
      * @param actionToken An opaque token used for invoking the purchase action.
+     * @deprecated TODO(https://crbug.com/329108444): Remove this method.
      */
     public void invokePurchaseAction(CoreAccountInfo primaryAccount, byte[] actionToken) {
         mDelegate.onPurchaseActionResultEnum(PurchaseActionResult.COULD_NOT_INVOKE);
diff --git a/components/facilitated_payments/android/java/src/org/chromium/components/facilitated_payments/SecureData.java b/components/facilitated_payments/android/java/src/org/chromium/components/facilitated_payments/SecureData.java
new file mode 100644
index 0000000..b7840910
--- /dev/null
+++ b/components/facilitated_payments/android/java/src/org/chromium/components/facilitated_payments/SecureData.java
@@ -0,0 +1,31 @@
+// Copyright 2025 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.facilitated_payments;
+
+import org.jni_zero.CalledByNative;
+import org.jni_zero.JNINamespace;
+
+/** Class containing the key value pairs for the secure data returned from Payments backend. */
+@JNINamespace("payments::facilitated")
+public class SecureData {
+    private final int mKey;
+    private final String mValue;
+
+    @CalledByNative
+    public SecureData(int key, String value) {
+        this.mKey = key;
+        this.mValue = value;
+    }
+
+    /** Returns the key for the SecureData. */
+    public int getKey() {
+        return mKey;
+    }
+
+    /** Returns the value for the SecureData. */
+    public String getValue() {
+        return mValue;
+    }
+}
diff --git a/components/facilitated_payments/android/java/src/org/chromium/components/facilitated_payments/SecurePayload.java b/components/facilitated_payments/android/java/src/org/chromium/components/facilitated_payments/SecurePayload.java
new file mode 100644
index 0000000..439800d
--- /dev/null
+++ b/components/facilitated_payments/android/java/src/org/chromium/components/facilitated_payments/SecurePayload.java
@@ -0,0 +1,41 @@
+// Copyright 2025 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.facilitated_payments;
+
+import org.jni_zero.CalledByNative;
+import org.jni_zero.JNINamespace;
+import org.jni_zero.JniType;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Class containing the action token and the decrypted secure data returned from payments backend.
+ * Both are used to trigger a UI flow within Google Play Services.
+ */
+@JNINamespace("payments::facilitated")
+class SecurePayload {
+    private final byte[] mActionToken;
+    private final List<SecureData> mSecureData;
+
+    @CalledByNative
+    public SecurePayload(byte[] actionToken, @JniType("std::vector") Object[] secureData) {
+        this.mActionToken = actionToken;
+        this.mSecureData = (List<SecureData>) (List<?>) Arrays.asList(secureData);
+    }
+
+    /** Returns an action token that can be used to trigger a UI flow in Google Play Services. */
+    public byte[] getActionToken() {
+        return mActionToken;
+    }
+
+    /**
+     * Returns a list of {@link SecureData} that can be passed in conjunction to the action token
+     * while triggering a UI flow in Google Play Services.
+     */
+    public List<SecureData> getSecureData() {
+        return mSecureData;
+    }
+}
diff --git a/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientUnitTest.java b/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientUnitTest.java
index b26791e2..e71f268 100644
--- a/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientUnitTest.java
+++ b/components/facilitated_payments/android/junit/src/org/chromium/components/facilitated_payments/FacilitatedPaymentsApiClientUnitTest.java
@@ -89,7 +89,8 @@
                 FacilitatedPaymentsApiClient.create(/* renderFrameHost= */ null, delegate);
 
         apiClient.invokePurchaseAction(
-                /* primaryAccount= */ null, new byte[] {'A', 'c', 't', 'i', 'o', 'n'});
+                /* primaryAccount= */ null,
+                new SecurePayload(new byte[] {'A', 'c', 't', 'i', 'o', 'n'}, new SecureData[0]));
 
         Assert.assertTrue(delegate.mIsPurchaseActionInvoked);
         Assert.assertEquals(PurchaseActionResult.COULD_NOT_INVOKE, delegate.mPurchaseActionResult);
@@ -113,7 +114,8 @@
         }
 
         @Override
-        public void invokePurchaseAction(CoreAccountInfo primaryAccount, byte[] actionToken) {
+        public void invokePurchaseAction(
+                CoreAccountInfo primaryAccount, SecurePayload securePayload) {
             mDelegate.onPurchaseActionResultEnum(PurchaseActionResult.RESULT_OK);
         }
     }
@@ -161,7 +163,8 @@
                 FacilitatedPaymentsApiClient.create(/* renderFrameHost= */ null, delegate);
 
         apiClient.invokePurchaseAction(
-                /* primaryAccount= */ null, new byte[] {'A', 'c', 't', 'i', 'o', 'n'});
+                /* primaryAccount= */ null,
+                new SecurePayload(new byte[] {'A', 'c', 't', 'i', 'o', 'n'}, new SecureData[0]));
 
         Assert.assertTrue(delegate.mIsPurchaseActionInvoked);
         Assert.assertEquals(PurchaseActionResult.RESULT_OK, delegate.mPurchaseActionResult);
diff --git a/components/facilitated_payments/android/secure_payload_android.cc b/components/facilitated_payments/android/secure_payload_android.cc
new file mode 100644
index 0000000..1adcf7a
--- /dev/null
+++ b/components/facilitated_payments/android/secure_payload_android.cc
@@ -0,0 +1,39 @@
+// Copyright 2025 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/facilitated_payments/android/secure_payload_android.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+
+// Must come after all headers that specialize FromJniType() / ToJniType().
+#include "components/facilitated_payments/android/java/jni_headers/SecureData_jni.h"
+#include "components/facilitated_payments/android/java/jni_headers/SecurePayload_jni.h"
+
+namespace payments::facilitated {
+
+namespace {
+
+using base::android::AttachCurrentThread;
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ScopedJavaLocalRef;
+
+}  // namespace
+
+ScopedJavaLocalRef<jobject> ConvertSecurePayloadToJavaObject(
+    const SecurePayload& secure_payload) {
+  JNIEnv* env = AttachCurrentThread();
+  ScopedJavaLocalRef<jbyteArray> action_token =
+      base::android::ToJavaByteArray(env, secure_payload.action_token);
+  std::vector<base::android::ScopedJavaLocalRef<jobject>> secure_data_array;
+  for (const SecureData& secure_data : secure_payload.secure_data) {
+    secure_data_array.push_back(Java_SecureData_Constructor(
+        env, secure_data.key, ConvertUTF8ToJavaString(env, secure_data.value)));
+  }
+  return Java_SecurePayload_Constructor(env, action_token,
+                                        std::move(secure_data_array));
+}
+
+}  // namespace payments::facilitated
diff --git a/components/facilitated_payments/android/secure_payload_android.h b/components/facilitated_payments/android/secure_payload_android.h
new file mode 100644
index 0000000..63d6348
--- /dev/null
+++ b/components/facilitated_payments/android/secure_payload_android.h
@@ -0,0 +1,20 @@
+// Copyright 2025 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_FACILITATED_PAYMENTS_ANDROID_SECURE_PAYLOAD_ANDROID_H_
+#define COMPONENTS_FACILITATED_PAYMENTS_ANDROID_SECURE_PAYLOAD_ANDROID_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "components/facilitated_payments/core/browser/model/secure_payload.h"
+
+namespace payments::facilitated {
+
+base::android::ScopedJavaLocalRef<jobject> ConvertSecurePayloadToJavaObject(
+    const SecurePayload& secure_payload);
+
+}  // namespace payments::facilitated
+
+#endif  // COMPONENTS_FACILITATED_PAYMENTS_ANDROID_SECURE_PAYLOAD_ANDROID_H_
diff --git a/components/facilitated_payments/android/secure_payload_android_unittest.cc b/components/facilitated_payments/android/secure_payload_android_unittest.cc
new file mode 100644
index 0000000..2381a6f
--- /dev/null
+++ b/components/facilitated_payments/android/secure_payload_android_unittest.cc
@@ -0,0 +1,29 @@
+// Copyright 2025 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/facilitated_payments/android/secure_payload_android.h"
+
+#include <utility>
+
+#include "base/android/jni_android.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace payments::facilitated {
+
+using SecurePayloadAndroidJniTest = testing::Test;
+
+TEST_F(SecurePayloadAndroidJniTest, SecurePayloadJavaObjectCreated) {
+  SecurePayload secure_payload;
+  secure_payload.action_token = {1, 2, 3};
+  secure_payload.secure_data.emplace_back(1, "value_1");
+  secure_payload.secure_data.emplace_back(2, "value_2");
+
+  base::android::ScopedJavaLocalRef<jobject> java_secure_payload =
+      ConvertSecurePayloadToJavaObject(std::move(secure_payload));
+
+  // Verify that the java object is created.
+  EXPECT_TRUE(java_secure_payload.obj());
+}
+
+}  // namespace payments::facilitated
diff --git a/components/facilitated_payments/core/browser/ewallet_manager.cc b/components/facilitated_payments/core/browser/ewallet_manager.cc
index 90311b3..9dbaa6c6 100644
--- a/components/facilitated_payments/core/browser/ewallet_manager.cc
+++ b/components/facilitated_payments/core/browser/ewallet_manager.cc
@@ -9,6 +9,7 @@
 #include "base/check_deref.h"
 #include "base/containers/span.h"
 #include "base/functional/callback_helpers.h"
+#include "base/time/time.h"
 #include "components/autofill/core/browser/data_manager/payments/payments_data_manager.h"
 #include "components/autofill/core/browser/data_model/ewallet.h"
 #include "components/autofill/core/browser/payments/payments_autofill_client.h"
@@ -32,6 +33,8 @@
 static constexpr FacilitatedPaymentsType kPaymentsType =
     FacilitatedPaymentsType::kEwallet;
 
+static constexpr base::TimeDelta kProgressScreenDismissDelay = base::Seconds(1);
+
 }  // namespace
 
 EwalletManager::EwalletManager(
@@ -283,6 +286,11 @@
       account_info.value(), response_details->secure_payload_.action_token,
       base::BindOnce(&EwalletManager::OnTransactionResult,
                      weak_ptr_factory_.GetWeakPtr(), base::TimeTicks::Now()));
+
+  // Close the progress screen just after the platform screen appears.
+  ui_timer_.Start(FROM_HERE, kProgressScreenDismissDelay,
+                  base::BindOnce(&EwalletManager::DismissProgressScreen,
+                                 weak_ptr_factory_.GetWeakPtr()));
 }
 
 void EwalletManager::OnTransactionResult(base::TimeTicks start_time,
@@ -371,4 +379,10 @@
   return AvailableEwalletsConfiguration::kMultipleEwallets;
 }
 
+void EwalletManager::DismissProgressScreen() {
+  if (ui_state_ == UiState::kProgressScreen) {
+    DismissPrompt();
+  }
+}
+
 }  // namespace payments::facilitated
diff --git a/components/facilitated_payments/core/browser/ewallet_manager.h b/components/facilitated_payments/core/browser/ewallet_manager.h
index 2196cd4e..7ced5d4f 100644
--- a/components/facilitated_payments/core/browser/ewallet_manager.h
+++ b/components/facilitated_payments/core/browser/ewallet_manager.h
@@ -13,6 +13,7 @@
 #include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
+#include "base/timer/timer.h"
 #include "components/autofill/core/browser/payments/payments_autofill_client.h"
 #include "components/facilitated_payments/core/browser/facilitated_payments_api_client.h"
 #include "components/facilitated_payments/core/browser/network_api/facilitated_payments_initiate_payment_request_details.h"
@@ -134,6 +135,10 @@
   // Returns the `AvailableEwalletsConfiguration` for this user profile.
   AvailableEwalletsConfiguration GetAvailableEwalletsConfiguration();
 
+  // Dismisses the FacilitatedPayments bottom sheet if the progress screen is
+  // being shown.
+  void DismissProgressScreen();
+
   // A list of eWallets that support the payment link provided in
   // TriggerEwalletPushPayment().
   //
@@ -187,6 +192,9 @@
   // device. This field is used for logging purposes.
   bool is_device_bound_for_logging_ = false;
 
+  // A timer to make UI changes.
+  base::OneShotTimer ui_timer_;
+
   base::WeakPtrFactory<EwalletManager> weak_ptr_factory_{this};
 };
 
diff --git a/components/facilitated_payments/core/browser/ewallet_manager_unittest.cc b/components/facilitated_payments/core/browser/ewallet_manager_unittest.cc
index b0ed9a1..e18fbf8a 100644
--- a/components/facilitated_payments/core/browser/ewallet_manager_unittest.cc
+++ b/components/facilitated_payments/core/browser/ewallet_manager_unittest.cc
@@ -1349,4 +1349,61 @@
   EXPECT_EQ(ukm_entries[0].metrics.at("Scheme"), 2);
 }
 
+TEST_F(EwalletManagerTest,
+       ProgressScreenAutoDismissedAfterInvokingPurchaseAction) {
+  // When purchase action is invoked, the progress screen would be showing.
+  test_api(*ewallet_manager_).ShowProgressScreen();
+
+  EXPECT_CALL(GetApiClient(), InvokePurchaseAction);
+
+  auto response_details =
+      std::make_unique<FacilitatedPaymentsInitiatePaymentResponseDetails>();
+  response_details->secure_payload_.action_token =
+      std::vector<uint8_t>{'t', 'o', 'k', 'e', 'n'};
+  test_api(*ewallet_manager_)
+      .OnInitiatePaymentResponseReceived(
+          base::TimeTicks::Now() - base::Seconds(2),
+          autofill::payments::PaymentsAutofillClient::PaymentsRpcResult::
+              kSuccess,
+          std::move(response_details));
+
+  // The progress screen is persisted for a short duration after invoking the
+  // purchase action for a smooth transition to the platform screen.
+  EXPECT_EQ(test_api(*ewallet_manager_).ui_state(), UiState::kProgressScreen);
+
+  FastForwardBy(base::Seconds(1));
+
+  // The progress screen should be dismissed after a short delay.
+  EXPECT_EQ(test_api(*ewallet_manager_).ui_state(), UiState::kHidden);
+}
+
+TEST_F(EwalletManagerTest,
+       ErrorScreenNotAutoDismissedAfterInvokingPurchaseAction) {
+  // When purchase action is invoked, the progress screen would be showing.
+  test_api(*ewallet_manager_).ShowProgressScreen();
+
+  EXPECT_CALL(GetApiClient(), InvokePurchaseAction);
+
+  auto response_details =
+      std::make_unique<FacilitatedPaymentsInitiatePaymentResponseDetails>();
+  response_details->secure_payload_.action_token =
+      std::vector<uint8_t>{'t', 'o', 'k', 'e', 'n'};
+  test_api(*ewallet_manager_)
+      .OnInitiatePaymentResponseReceived(
+          base::TimeTicks::Now() - base::Seconds(2),
+          autofill::payments::PaymentsAutofillClient::PaymentsRpcResult::
+              kSuccess,
+          std::move(response_details));
+
+  // If the purchase action could not be invoked, the `PurchaseActionResult` is
+  // returned immediately. The error screen is shown.
+  test_api(*ewallet_manager_)
+      .OnTransactionResult(base::TimeTicks::Now() - base::Seconds(2),
+                           PurchaseActionResult::kCouldNotInvoke);
+  FastForwardBy(base::Seconds(1));
+
+  // The error screen shouldn't be auto-dismissed.
+  EXPECT_EQ(test_api(*ewallet_manager_).ui_state(), UiState::kErrorScreen);
+}
+
 }  // namespace payments::facilitated
diff --git a/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc b/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc
index 66067b8..4d71fd43 100644
--- a/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc
+++ b/components/feed/core/v2/api_test/feed_api_reliability_logging_unittest.cc
@@ -320,6 +320,78 @@
       surface.reliability_logging_bridge.GetEventsString());
 }
 
+TEST_F(FeedApiReliabilityLoggingTest, LoadStreamComplete_InternetDisconnected) {
+  network_.error = net::ERR_INTERNET_DISCONNECTED;
+  TestForYouSurface surface(stream_.get());
+  WaitForIdleTaskQueue();
+
+  EXPECT_EQ(
+      "LogFeedLaunchOtherStart\n"
+      "LogLoadingIndicatorShown\n"
+
+      "LogCacheReadStart\n"
+      "LogCacheReadEnd result=EMPTY_SESSION\n"
+
+      "LogFeedRequestStart id=1\n"
+      "LogRequestSent id=1\n"
+      // Should not call LogResponseReceived.
+      "LogRequestFinished result=-106 id=1\n"
+
+      "LogLaunchFinishedAfterStreamUpdate "
+      "result=NO_CARDS_REQUEST_ERROR_NO_INTERNET\n"
+
+      "LogAboveTheFoldRender result=FULL_FEED_ERROR\n",
+      surface.reliability_logging_bridge.GetEventsString());
+}
+
+TEST_F(FeedApiReliabilityLoggingTest, LoadStreamComplete_NameNotResolved) {
+  network_.error = net::ERR_NAME_NOT_RESOLVED;
+  TestForYouSurface surface(stream_.get());
+  WaitForIdleTaskQueue();
+
+  EXPECT_EQ(
+      "LogFeedLaunchOtherStart\n"
+      "LogLoadingIndicatorShown\n"
+
+      "LogCacheReadStart\n"
+      "LogCacheReadEnd result=EMPTY_SESSION\n"
+
+      "LogFeedRequestStart id=1\n"
+      "LogRequestSent id=1\n"
+      // Should not call LogResponseReceived.
+      "LogRequestFinished result=-105 id=1\n"
+
+      "LogLaunchFinishedAfterStreamUpdate "
+      "result=NO_CARDS_REQUEST_ERROR_NO_INTERNET\n"
+
+      "LogAboveTheFoldRender result=FULL_FEED_ERROR\n",
+      surface.reliability_logging_bridge.GetEventsString());
+}
+
+TEST_F(FeedApiReliabilityLoggingTest, LoadStreamComplete_AddressUnreachable) {
+  network_.error = net::ERR_ADDRESS_UNREACHABLE;
+  TestForYouSurface surface(stream_.get());
+  WaitForIdleTaskQueue();
+
+  EXPECT_EQ(
+      "LogFeedLaunchOtherStart\n"
+      "LogLoadingIndicatorShown\n"
+
+      "LogCacheReadStart\n"
+      "LogCacheReadEnd result=EMPTY_SESSION\n"
+
+      "LogFeedRequestStart id=1\n"
+      "LogRequestSent id=1\n"
+      // Should not call LogResponseReceived.
+      "LogRequestFinished result=-109 id=1\n"
+
+      "LogLaunchFinishedAfterStreamUpdate "
+      "result=NO_CARDS_REQUEST_ERROR_NO_INTERNET\n"
+
+      "LogAboveTheFoldRender result=FULL_FEED_ERROR\n",
+      surface.reliability_logging_bridge.GetEventsString());
+}
+
 TEST_F(FeedApiReliabilityLoggingTest,
        LoadStreamComplete_ResponseReceivedWithHttpError) {
   network_.http_status_code = net::HttpStatusCode::HTTP_FORBIDDEN;
diff --git a/components/feed/core/v2/tasks/load_stream_task.cc b/components/feed/core/v2/tasks/load_stream_task.cc
index 6d0e569f..05729a8 100644
--- a/components/feed/core/v2/tasks/load_stream_task.cc
+++ b/components/feed/core/v2/tasks/load_stream_task.cc
@@ -73,7 +73,8 @@
 LaunchResult LoadStreamTask::LaunchResultFromNetworkInfo(
     const NetworkResponseInfo& response_info,
     bool has_parsed_body) {
-  if (response_info.status_code == 200) {
+  int status_code = response_info.status_code;
+  if (status_code == 200) {
     if (has_parsed_body) {
       // Success.
       return {LoadStreamStatus::kNoStatus,
@@ -106,6 +107,13 @@
           LoadStreamStatus::kAccountTokenFetchTimedOut,
           feedwire::DiscoverLaunchResult::NO_CARDS_FAILED_TO_GET_AUTH_TOKEN};
   }
+  if (status_code == net::ERR_INTERNET_DISCONNECTED ||
+      status_code == net::ERR_NAME_NOT_RESOLVED ||
+      status_code == net::ERR_ADDRESS_UNREACHABLE ||
+      status_code == net::ERR_PROXY_CONNECTION_FAILED) {
+    return {LoadStreamStatus::kCannotLoadFromNetworkOffline,
+            feedwire::DiscoverLaunchResult::NO_CARDS_REQUEST_ERROR_NO_INTERNET};
+  }
   return {LoadStreamStatus::kNetworkFetchFailed,
           feedwire::DiscoverLaunchResult::NO_CARDS_RESPONSE_ERROR_NON_200};
 }
diff --git a/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeController.java b/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeController.java
index 1df37ba..93fed62b 100644
--- a/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeController.java
+++ b/components/messages/android/internal/java/src/org/chromium/components/messages/ScopeChangeController.java
@@ -84,6 +84,7 @@
         // TODO(crbug.com/40230391): Replace GURL with Origin.
         private GURL mLastVisitedUrl;
         private boolean mIsActive;
+        private boolean mIsDestroyed;
 
         public NavigationWebContentsScopeObserver(Delegate delegate, ScopeKey scopeKey) {
             super(scopeKey.webContents);
@@ -134,10 +135,19 @@
         }
 
         @Override
-        public void onDestroy() {
+        public void webContentsDestroyed() {
+            destroy();
+        }
+
+        @Override
+        public void destroy() {
+            if (mIsDestroyed) return;
+            mIsDestroyed = true;
+
             mDelegate.onScopeChange(
                     new MessageScopeChange(mScopeKey.scopeType, mScopeKey, ChangeType.DESTROY));
             mIsActive = false;
+            observe(null);
         }
 
         @Override
diff --git a/components/navigation_interception/intercept_navigation_throttle.cc b/components/navigation_interception/intercept_navigation_throttle.cc
index 111ab866..9dd5458 100644
--- a/components/navigation_interception/intercept_navigation_throttle.cc
+++ b/components/navigation_interception/intercept_navigation_throttle.cc
@@ -70,12 +70,14 @@
                                   weak_factory_.GetWeakPtr()));
     return content::NavigationThrottle::PROCEED;
   }
+  auto weak_this = weak_factory_.GetWeakPtr();
   // No need to set |should_ignore_| since if it is true, we'll cancel the
   // navigation immediately.
   return should_ignore_callback_.Run(navigation_handle())
              ? content::NavigationThrottle::CANCEL_AND_IGNORE
              : content::NavigationThrottle::PROCEED;
-  // Careful, |this| can be deleted at this point.
+  // Clients should not synchronously cause the navigation to be deleted.
+  CHECK(weak_this);
 }
 
 void InterceptNavigationThrottle::RunCheckAsync() {
@@ -85,8 +87,8 @@
   bool final_deferred_check = deferring_ && pending_checks_ == 0;
   auto weak_this = weak_factory_.GetWeakPtr();
   bool should_ignore = should_ignore_callback_.Run(navigation_handle());
-  if (!weak_this)
-    return;
+  // Clients should not synchronously cause the navigation to be deleted.
+  CHECK(weak_this);
 
   should_ignore_ |= should_ignore;
   if (!final_deferred_check)
diff --git a/components/navigation_interception/intercept_navigation_throttle_unittest.cc b/components/navigation_interception/intercept_navigation_throttle_unittest.cc
index c1f462d..1c418f1 100644
--- a/components/navigation_interception/intercept_navigation_throttle_unittest.cc
+++ b/components/navigation_interception/intercept_navigation_throttle_unittest.cc
@@ -199,34 +199,6 @@
   EXPECT_EQ(NavigationThrottle::CANCEL_AND_IGNORE, result);
 }
 
-// Regression test for https://crbug.com/856737. There is some java code that
-// runs in the CheckCallback that can synchronously tear down the navigation
-// while the throttle is running.
-// TODO(csharrison): We should probably make that code async to avoid these
-// sorts of situations. However, it might not be possible if we implement
-// WebViewClient#shouldOverrideUrlLoading with this class which can end up
-// calling loadUrl() within the callback. See https://crbug.com/794020 for more
-// details.
-TEST_P(InterceptNavigationThrottleTest, IgnoreCallbackDeletesNavigation) {
-  NavigateAndCommit(GURL("about:blank"));
-
-  auto ignore_callback = [](content::NavigationHandle* handle) {
-    handle->GetWebContents()->GetController().GoToIndex(0);
-    return true;
-  };
-  auto inserter = std::make_unique<content::TestNavigationThrottleInserter>(
-      web_contents(),
-      base::BindRepeating(&InterceptNavigationThrottleTest::CreateThrottle,
-                          base::BindRepeating(ignore_callback)));
-
-  // Intercepting a navigation and forcing a synchronous re-navigation should
-  // not crash.
-  auto navigation = content::NavigationSimulator::CreateBrowserInitiated(
-      GURL("https://intercept.test/"), web_contents());
-  navigation->Start();
-  base::RunLoop().RunUntilIdle();
-}
-
 INSTANTIATE_TEST_SUITE_P(All,
                          InterceptNavigationThrottleTest,
                          testing::Values(true, false));
diff --git a/components/omnibox/browser/enterprise_search_aggregator_suggestions_service.cc b/components/omnibox/browser/enterprise_search_aggregator_suggestions_service.cc
index db779e2..35cac99 100644
--- a/components/omnibox/browser/enterprise_search_aggregator_suggestions_service.cc
+++ b/components/omnibox/browser/enterprise_search_aggregator_suggestions_service.cc
@@ -9,6 +9,10 @@
 #include <utility>
 
 #include "base/functional/bind.h"
+#include "base/strings/stringprintf.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/signin/public/identity_manager/primary_account_access_token_fetcher.h"
+#include "components/signin/public/identity_manager/scope_set.h"
 #include "components/variations/net/variations_http_headers.h"
 #include "net/base/load_flags.h"
 #include "net/cookies/site_for_cookies.h"
@@ -20,10 +24,11 @@
 
 EnterpriseSearchAggregatorSuggestionsService::
     EnterpriseSearchAggregatorSuggestionsService(
+        signin::IdentityManager* identity_manager,
         scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
-    : url_loader_factory_(url_loader_factory) {
-  DCHECK(url_loader_factory);
-}
+    : url_loader_factory_(url_loader_factory),
+      identity_manager_(identity_manager),
+      token_fetcher_(nullptr) {}
 
 EnterpriseSearchAggregatorSuggestionsService::
     ~EnterpriseSearchAggregatorSuggestionsService() = default;
@@ -85,6 +90,40 @@
         }
       })");
 
+  // Create and fetch an OAuth2 token.
+  signin::ScopeSet scopes;
+  // TODO(crbug.com/380631529): Add scope once access is provided.
+  token_fetcher_ = std::make_unique<signin::PrimaryAccountAccessTokenFetcher>(
+      "enterprise_search_aggregator_suggestions_service", identity_manager_,
+      scopes,
+      base::BindOnce(
+          &EnterpriseSearchAggregatorSuggestionsService::AccessTokenAvailable,
+          base::Unretained(this), std::move(request), std::move(request_body),
+          traffic_annotation, std::move(start_callback),
+          std::move(completion_callback)),
+      signin::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable,
+      signin::ConsentLevel::kSignin);
+}
+
+void EnterpriseSearchAggregatorSuggestionsService::AccessTokenAvailable(
+    std::unique_ptr<network::ResourceRequest> request,
+    std::string request_body,
+    net::NetworkTrafficAnnotationTag traffic_annotation,
+    StartCallback start_callback,
+    CompletionCallback completion_callback,
+    GoogleServiceAuthError error,
+    signin::AccessTokenInfo access_token_info) {
+  DCHECK(token_fetcher_);
+  token_fetcher_.reset();
+  // If there were no errors obtaining the access token, append it to the
+  // request as a header.
+  if (error.state() == GoogleServiceAuthError::NONE) {
+    DCHECK(!access_token_info.token.empty());
+    request->headers.SetHeader(
+        "Authorization",
+        base::StringPrintf("Bearer %s", access_token_info.token.c_str()));
+  }
+
   StartDownloadAndTransferLoader(std::move(request), std::move(request_body),
                                  traffic_annotation, std::move(start_callback),
                                  std::move(completion_callback));
diff --git a/components/omnibox/browser/enterprise_search_aggregator_suggestions_service.h b/components/omnibox/browser/enterprise_search_aggregator_suggestions_service.h
index 6cae54b..f4377fc 100644
--- a/components/omnibox/browser/enterprise_search_aggregator_suggestions_service.h
+++ b/components/omnibox/browser/enterprise_search_aggregator_suggestions_service.h
@@ -11,11 +11,20 @@
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/scoped_observation.h"
 #include "components/keyed_service/core/keyed_service.h"
+#include "components/signin/public/identity_manager/access_token_info.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "url/gurl.h"
 
+namespace signin {
+class PrimaryAccountAccessTokenFetcher;
+}  // namespace signin
+
+class GoogleServiceAuthError;
+
 namespace network {
 struct ResourceRequest;
 class SharedURLLoaderFactory;
@@ -25,7 +34,8 @@
 // A service to fetch suggestions from the search aggregator endpoint URL.
 class EnterpriseSearchAggregatorSuggestionsService : public KeyedService {
  public:
-  explicit EnterpriseSearchAggregatorSuggestionsService(
+  EnterpriseSearchAggregatorSuggestionsService(
+      signin::IdentityManager* identity_manager,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
 
   ~EnterpriseSearchAggregatorSuggestionsService() override;
@@ -54,6 +64,15 @@
       CompletionCallback completion_callback);
 
  private:
+  // Called when an access token request completes (successfully or not).
+  void AccessTokenAvailable(std::unique_ptr<network::ResourceRequest> request,
+                            std::string request_body,
+                            net::NetworkTrafficAnnotationTag traffic_annotation,
+                            StartCallback start_callback,
+                            CompletionCallback completion_callback,
+                            GoogleServiceAuthError error,
+                            signin::AccessTokenInfo access_token_info);
+
   // TODO(crbug.com/385756623): Factor out this method so it can be used across
   //   document_suggestions_service and
   //   enterprise_search_aggregator_suggestions_service.
@@ -65,6 +84,13 @@
       CompletionCallback completion_callback);
 
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
+
+  // This will outlive this instance because of the factory dependencies.
+  raw_ptr<signin::IdentityManager> identity_manager_;
+
+  // Helper for fetching OAuth2 access tokens. Non-null when we have a token
+  // available, or while a token fetch is in progress.
+  std::unique_ptr<signin::PrimaryAccountAccessTokenFetcher> token_fetcher_;
 };
 
 #endif  // COMPONENTS_OMNIBOX_BROWSER_ENTERPRISE_SEARCH_AGGREGATOR_SUGGESTIONS_SERVICE_H_
diff --git a/components/omnibox/browser/enterprise_search_aggregator_suggestions_service_unittest.cc b/components/omnibox/browser/enterprise_search_aggregator_suggestions_service_unittest.cc
index 18bcca7..0407fe1 100644
--- a/components/omnibox/browser/enterprise_search_aggregator_suggestions_service_unittest.cc
+++ b/components/omnibox/browser/enterprise_search_aggregator_suggestions_service_unittest.cc
@@ -8,13 +8,13 @@
 #include "base/json/json_parser.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
-#include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
+#include "base/test/test_future.h"
 #include "components/signin/public/identity_manager/identity_test_environment.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "components/variations/net/variations_http_headers.h"
@@ -59,16 +59,6 @@
        "dataStore" : ""
    } ] })"});
 
-void OnEnterpriseSearchAggregatorSuggestionsRequestAvailable(
-    network::ResourceRequest* request) {}
-
-void OnEnterpriseSearchAggregatorSuggestionsLoaderAvailable(
-    std::unique_ptr<network::SimpleURLLoader> loader,
-    const std::string& request_body) {}
-
-void OnURLLoadComplete(const network::SimpleURLLoader* source,
-                       std::unique_ptr<std::string> response_body) {}
-
 class EnterpriseSearchAggregatorSuggestionsServiceTest : public testing::Test {
  public:
   EnterpriseSearchAggregatorSuggestionsServiceTest()
@@ -76,8 +66,10 @@
         shared_url_loader_factory_(
             base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
                 &test_url_loader_factory_)),
+        identity_test_env_(&test_url_loader_factory_, &prefs_),
         enterprise_search_aggregator_suggestions_service_(
             new EnterpriseSearchAggregatorSuggestionsService(
+                identity_test_env_.identity_manager(),
                 shared_url_loader_factory_)) {
     // Set up a variation.
     variations::AssociateGoogleVariationID(
@@ -126,16 +118,25 @@
   base::JSONWriter::Write(root, &test_request_body);
   const GURL test_endpoint = GURL("https://fake_url.com");
 
+  base::test::TestFuture<network::ResourceRequest*> request_future;
+  base::test::TestFuture<std::unique_ptr<network::SimpleURLLoader>,
+                         const std::string&>
+      loader_future;
+  base::test::TestFuture<const network::SimpleURLLoader*,
+                         std::unique_ptr<std::string>>
+      complete_future;
+
   enterprise_search_aggregator_suggestions_service_
       ->CreateEnterpriseSearchAggregatorSuggestionsRequest(
-          test_endpoint, test_request_body,
-          base::BindOnce(
-              OnEnterpriseSearchAggregatorSuggestionsRequestAvailable),
-          base::BindOnce(
-              OnEnterpriseSearchAggregatorSuggestionsLoaderAvailable),
-          base::BindOnce(OnURLLoadComplete));
+          test_endpoint, test_request_body, request_future.GetCallback(),
+          loader_future.GetCallback(), complete_future.GetCallback());
 
-  base::RunLoop().RunUntilIdle();
+  ASSERT_TRUE(request_future.Wait());
+  ASSERT_TRUE(loader_future.Wait());
+
+  complete_future.SetValue(nullptr,
+                           std::make_unique<std::string>(mock_response));
+  ASSERT_TRUE(complete_future.Wait());
 
   EXPECT_TRUE(resource_request.site_for_cookies.IsEquivalent(
       net::SiteForCookies::FromUrl(GURL(test_endpoint))))
diff --git a/components/omnibox/browser/unscoped_extension_provider.cc b/components/omnibox/browser/unscoped_extension_provider.cc
index e6aadfe..bd8ed5e 100644
--- a/components/omnibox/browser/unscoped_extension_provider.cc
+++ b/components/omnibox/browser/unscoped_extension_provider.cc
@@ -7,24 +7,12 @@
 #include <string>
 
 #include "base/check_is_test.h"
-#include "base/containers/fixed_flat_map.h"
 #include "base/memory/raw_ptr.h"
-#include "build/build_config.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
 #include "components/omnibox/browser/unscoped_extension_provider_delegate.h"
 #include "components/search_engines/template_url_service.h"
-#include "components/strings/grit/components_strings.h"
-#include "ui/base/l10n/l10n_util.h"
-#include "ui/base/page_transition_types.h"
-
-namespace {
-constexpr auto kReservedSectionMap =
-    base::MakeFixedFlatMap<int, omnibox::GroupSection>(
-        {{0, omnibox::SECTION_UNSCOPED_EXTENSION_1},
-         {1, omnibox::SECTION_UNSCOPED_EXTENSION_2}});
-}  // namespace
 
 UnscopedExtensionProvider::UnscopedExtensionProvider(
     AutocompleteProviderClient* client,
@@ -40,42 +28,51 @@
 
 void UnscopedExtensionProvider::Start(const AutocompleteInput& input,
                                       bool minimal_changes) {
+  // If the changes to the input are not minimal, clear the current list of
+  // matches and suggestion group information and increment the current request
+  // ID to discard any suggestions that may be incoming later with a stale
+  // request ID.
+  Stop(/*clear_cached_results=*/!minimal_changes,
+       /*due_to_user_inactivity=*/false);
+
+  // Extension suggestions are not allowed in keyword mode.
+  if (input.InKeywordMode()) {
+    return;
+  }
+
+  // Extension suggestions are not allowed for zero-suggest or empty inputs.
+  if (input.IsZeroSuggest() ||
+      input.type() == metrics::OmniboxInputType::EMPTY) {
+    return;
+  }
+
+  // Extension suggestions are always provided asynchronously.
+  if (input.omit_asynchronous_matches()) {
+    return;
+  }
+
+  // Do not forward the input to the extensions delegate if the changes to the
+  // input are minimal.
   if (minimal_changes) {
-    // Return early and maintain the current matches list.
     return;
   }
 
-  // Reset done and increment the input ID to discard any stale extension
-  // suggestions that may be incoming later if the current request id and
-  // incoming request ids do not match.
-  Stop(true, false);
-
-  // Do not forward the event to unscoped extensions delegate if:
-  // 1. there are no unscoped extensions
-  // 2. in zero-suggest mode
-  // 3. only synchronous matches are needed and the changes are not
-  // minimal. Minimal changes will not need an async call.
-  // 4. in keyword mode.
-  std::set<std::string> unscoped_extensions = GetUnscopedModeExtensionIds();
-  bool skip_unscoped_extensions_matches =
-      unscoped_extensions.empty() || input.IsZeroSuggest() ||
-      (input.omit_asynchronous_matches()) || input.InKeywordMode();
-
-  if (skip_unscoped_extensions_matches) {
-    ClearSuggestionGroupsMap();
+  // Do not forward the input to the extensions delegate if there are no
+  // unscoped extensions.
+  std::set<std::string> unscoped_extensions =
+      GetTemplateURLService()->GetUnscopedModeExtensionIds();
+  if (unscoped_extensions.empty()) {
     return;
   }
 
-  delegate_->IncrementRequestId();
+  // Forward the input to the extensions delegate.
   delegate_->Start(input, minimal_changes, unscoped_extensions);
 }
 
 void UnscopedExtensionProvider::Stop(bool clear_cached_results,
                                      bool due_to_user_inactivity) {
   AutocompleteProvider::Stop(clear_cached_results, due_to_user_inactivity);
-  if (due_to_user_inactivity) {
-    delegate_->IncrementRequestId();
-  }
+  delegate_->Stop(clear_cached_results);
 }
 
 TemplateURLService* UnscopedExtensionProvider::GetTemplateURLService() const {
@@ -86,24 +83,7 @@
 }
 
 void UnscopedExtensionProvider::AddToSuggestionGroupsMap(
-    omnibox::GroupId groupId,
-    const std::string& header_text) {
-  // Should never be adding to suggestion groups map beyond max extension
-  // limit.
-  DCHECK_LT(next_available_section_index_, kReservedSectionMap.size());
-  omnibox::GroupConfig group;
-  group.set_section(kReservedSectionMap.at(next_available_section_index_++));
-  group.set_render_type(omnibox::GroupConfig_RenderType_DEFAULT_VERTICAL);
-  group.set_header_text(header_text);
-  suggestion_groups_map_[groupId].MergeFrom(group);
-}
-
-void UnscopedExtensionProvider::ClearSuggestionGroupsMap() {
-  suggestion_groups_map_.clear();
-  next_available_section_index_ = 0;
-}
-
-std::set<std::string> UnscopedExtensionProvider::GetUnscopedModeExtensionIds()
-    const {
-  return GetTemplateURLService()->GetUnscopedModeExtensionIds();
+    omnibox::GroupId group_id,
+    omnibox::GroupConfig group_config) {
+  suggestion_groups_map_[group_id].MergeFrom(group_config);
 }
diff --git a/components/omnibox/browser/unscoped_extension_provider.h b/components/omnibox/browser/unscoped_extension_provider.h
index b21651d..3a3b374 100644
--- a/components/omnibox/browser/unscoped_extension_provider.h
+++ b/components/omnibox/browser/unscoped_extension_provider.h
@@ -12,12 +12,13 @@
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_provider.h"
 #include "components/omnibox/browser/omnibox_suggestions_watcher.h"
-#include "components/omnibox/browser/unscoped_extension_provider_delegate.h"
+#include "third_party/omnibox_proto/groups.pb.h"
 
 class AutocompleteInput;
 class AutocompleteProviderClient;
 class AutocompleteProviderListener;
 class TemplateURLService;
+class UnscopedExtensionProviderDelegate;
 
 // Provides suggestions from extension that are allowed to run in unscoped mode
 // (i.e. without requiring keyword mode).
@@ -29,29 +30,21 @@
   UnscopedExtensionProvider& operator=(const UnscopedExtensionProvider&) =
       delete;
 
-  void AddToSuggestionGroupsMap(omnibox::GroupId groupId,
-                                const std::string& header_text);
-  void ClearSuggestionGroupsMap();
-
   // AutocompleteProvider:
   void Start(const AutocompleteInput& input, bool minimal_changes) override;
   void Stop(bool clear_cached_results, bool due_to_user_inactivity) override;
 
+  // Used by UnscopedExtensionProviderDelegateImpl.
+  TemplateURLService* GetTemplateURLService() const;
+  void AddToSuggestionGroupsMap(omnibox::GroupId group_id,
+                                omnibox::GroupConfig group_config);
   void set_done(bool done) { done_ = done; }
   bool done() const { return done_; }
   ACMatches* matches() { return &matches_; }
 
-  TemplateURLService* GetTemplateURLService() const;
-
  private:
   ~UnscopedExtensionProvider() override;
 
-  // Next section available to be given to an extension groupId. Possible
-  // sections are defined in `kReservedSectionMap`.
-  size_t next_available_section_index_ = 0;
-
-  std::set<std::string> GetUnscopedModeExtensionIds() const;
-
   raw_ptr<AutocompleteProviderClient> client_;
   raw_ptr<TemplateURLService> template_url_service_;
   std::unique_ptr<UnscopedExtensionProviderDelegate> delegate_;
diff --git a/components/omnibox/browser/unscoped_extension_provider_delegate.h b/components/omnibox/browser/unscoped_extension_provider_delegate.h
index 460b866e4..bef3ddc 100644
--- a/components/omnibox/browser/unscoped_extension_provider_delegate.h
+++ b/components/omnibox/browser/unscoped_extension_provider_delegate.h
@@ -10,8 +10,6 @@
 
 #include "components/omnibox/browser/autocomplete_input.h"
 
-class AutocompleteInput;
-
 class UnscopedExtensionProviderDelegate {
  public:
   UnscopedExtensionProviderDelegate();
@@ -26,8 +24,11 @@
                      bool minimal_changes,
                      std::set<std::string> unscoped_mode_extension_ids) = 0;
 
-  // Increments the id of the request sent to the extension.
-  virtual void IncrementRequestId() = 0;
+  // Stops the current request to the extension by incrementing the current
+  // request ID which effectively discards any suggestions that may be incoming
+  // later with a stale request ID. if `clear_cached_results` is true, it also
+  // clears the current list of cached matches and suggestion group information.
+  virtual void Stop(bool clear_cached_results) = 0;
 };
 
 #endif  // COMPONENTS_OMNIBOX_BROWSER_UNSCOPED_EXTENSION_PROVIDER_DELEGATE_H_
diff --git a/components/omnibox/browser/unscoped_extension_provider_unittest.cc b/components/omnibox/browser/unscoped_extension_provider_unittest.cc
index e9dbc2ff..2a8d632 100644
--- a/components/omnibox/browser/unscoped_extension_provider_unittest.cc
+++ b/components/omnibox/browser/unscoped_extension_provider_unittest.cc
@@ -48,7 +48,7 @@
                 Start,
                 (const AutocompleteInput&, bool, std::set<std::string>),
                 (override));
-    MOCK_METHOD(void, IncrementRequestId, (), (override));
+    MOCK_METHOD(void, Stop, (bool clear_cached_suggestions), (override));
   };
 
  protected:
@@ -87,7 +87,7 @@
   input.set_focus_type(metrics::OmniboxFocusType::INTERACTION_DEFAULT);
   input.set_omit_asynchronous_matches(false);
 
-  EXPECT_CALL(*mock_delegate, IncrementRequestId);
+  EXPECT_CALL(*mock_delegate, Stop);
   EXPECT_CALL(*mock_delegate, Start);
 
   InitProvider(std::move(mock_delegate));
@@ -104,7 +104,7 @@
                           TestSchemeClassifier());
   input.set_focus_type(metrics::OmniboxFocusType::INTERACTION_DEFAULT);
 
-  EXPECT_CALL(*mock_delegate, IncrementRequestId).Times(0);
+  EXPECT_CALL(*mock_delegate, Stop);
   EXPECT_CALL(*mock_delegate, Start).Times(0);
 
   InitProvider(std::move(mock_delegate));
@@ -122,7 +122,7 @@
   input.set_focus_type(metrics::OmniboxFocusType::INTERACTION_DEFAULT);
   input.set_omit_asynchronous_matches(true);
 
-  EXPECT_CALL(*mock_delegate, IncrementRequestId).Times(0);
+  EXPECT_CALL(*mock_delegate, Stop);
   EXPECT_CALL(*mock_delegate, Start).Times(0);
 
   InitProvider(std::move(mock_delegate));
@@ -138,7 +138,7 @@
                           TestSchemeClassifier());
   input.set_focus_type(metrics::OmniboxFocusType::INTERACTION_FOCUS);
 
-  EXPECT_CALL(*mock_delegate, IncrementRequestId).Times(0);
+  EXPECT_CALL(*mock_delegate, Stop);
   EXPECT_CALL(*mock_delegate, Start).Times(0);
 
   InitProvider(std::move(mock_delegate));
@@ -152,7 +152,7 @@
                           TestSchemeClassifier());
   input.set_focus_type(metrics::OmniboxFocusType::INTERACTION_DEFAULT);
 
-  EXPECT_CALL(*mock_delegate, IncrementRequestId).Times(0);
+  EXPECT_CALL(*mock_delegate, Stop);
   EXPECT_CALL(*mock_delegate, Start).Times(0);
 
   InitProvider(std::move(mock_delegate));
diff --git a/components/optimization_guide/core/model_execution/on_device_execution.cc b/components/optimization_guide/core/model_execution/on_device_execution.cc
index e84715c7..ceb3152a 100644
--- a/components/optimization_guide/core/model_execution/on_device_execution.cc
+++ b/components/optimization_guide/core/model_execution/on_device_execution.cc
@@ -360,6 +360,11 @@
     return;
   }
   if (safety_result.is_unsafe || safety_result.is_unsupported_language) {
+    if (opts_.safety_checker->safety_cfg()
+            .OnlyCancelUnsafeResponseOnComplete() &&
+        completeness != ResponseCompleteness::kComplete) {
+      return;
+    }
     if (histogram_logger_) {
       histogram_logger_->set_result(Result::kUsedOnDeviceOutputUnsafe);
     }
@@ -439,6 +444,11 @@
     AddModelExecutionLogs(std::move(safety_result.logs));
   }
   if (safety_result.is_unsafe || safety_result.is_unsupported_language) {
+    if (opts_.safety_checker->safety_cfg()
+            .OnlyCancelUnsafeResponseOnComplete() &&
+        completeness != ResponseCompleteness::kComplete) {
+      return;
+    }
     if (histogram_logger_) {
       histogram_logger_->set_result(Result::kUsedOnDeviceOutputUnsafe);
     }
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc b/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc
index a4219efe..4ed25dc 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller_unittest.cc
@@ -3094,6 +3094,53 @@
   EXPECT_THAT(response_.partials(), ElementsAreArray(expected_responses));
 }
 
+TEST_F(OnDeviceModelServiceControllerTest, WaitUntilCompleteToCancel) {
+  FakeSafetyModelAsset safety_asset([]() {
+    auto safety_config = ComposeSafetyConfig();
+    safety_config.set_only_cancel_unsafe_response_on_complete(true);
+    safety_config.mutable_safety_category_thresholds()->Add(ForbidUnsafe());
+    safety_config.add_allowed_languages("en");
+    return safety_config;
+  }());
+  Initialize({
+      .base_model = &standard_assets_.base_model,
+      .safety = &safety_asset,
+      .language = &standard_assets_.language,
+      .adaptations = {&standard_assets_.compose},
+  });
+  auto session = CreateSession();
+  EXPECT_TRUE(session);
+
+  fake_settings_.set_execute_result(
+      {"safe", " safe", " lang:en=1.0", " safe", " unsafe"});
+  session->ExecuteModel(PageUrlRequest("foo"),
+                        response_.GetStreamingCallback());
+
+  // The full output was unsafe so it resulted it in it being filtered.
+  EXPECT_FALSE(response_.GetFinalStatus());
+  EXPECT_EQ(
+      response_.error(),
+      OptimizationGuideModelExecutionError::ModelExecutionError::kFiltered);
+
+  const std::vector<std::string> expected_responses = {
+      // The first two responses are filtered because their language hasn't been
+      // detected yet. Because `only_cancel_unsafe_response_on_complete` is
+      // true, this doesn't cause the input to be cancelled.
+      //
+      // "safe", "safe safe",
+
+      // The next two responses are not filtered because the language has been
+      // reliably detected as a supported language.
+      "safe safe lang:en=1.0", "safe safe lang:en=1.0 safe",
+
+      // The last response is unsafe so it is filtered. Since the output is
+      // complete the response is cancelled.
+      //
+      // "safe safe lang:en=1.0 safe unsafe",
+  };
+  EXPECT_THAT(response_.partials(), ElementsAreArray(expected_responses));
+}
+
 // Validate chunk-by-chunk streaming mode works correctly.
 TEST_F(OnDeviceModelServiceControllerTest, TsInterval1_ChunkByChunk) {
   // Configure a token interval to enable streaming responses.
diff --git a/components/optimization_guide/core/model_execution/safety_config.cc b/components/optimization_guide/core/model_execution/safety_config.cc
index b5e471c..8b991e6 100644
--- a/components/optimization_guide/core/model_execution/safety_config.cc
+++ b/components/optimization_guide/core/model_execution/safety_config.cc
@@ -277,4 +277,8 @@
   return result;
 }
 
+bool SafetyConfig::OnlyCancelUnsafeResponseOnComplete() const {
+  return proto_ && proto_->only_cancel_unsafe_response_on_complete();
+}
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/safety_config.h b/components/optimization_guide/core/model_execution/safety_config.h
index b11dde3..3fafe384 100644
--- a/components/optimization_guide/core/model_execution/safety_config.h
+++ b/components/optimization_guide/core/model_execution/safety_config.h
@@ -89,6 +89,10 @@
       ResponseCompleteness completeness,
       const on_device_model::mojom::SafetyInfoPtr& safety_info) const;
 
+  // Whether this config waits until a unsafe response is complete before
+  // canceling.
+  bool OnlyCancelUnsafeResponseOnComplete() const;
+
  private:
   // Whether the text is in a language not supported by the safety classifier,
   // or the language could not be detected despite the classifier requiring one
diff --git a/components/optimization_guide/proto/BUILD.gn b/components/optimization_guide/proto/BUILD.gn
index f188937..32812a3 100644
--- a/components/optimization_guide/proto/BUILD.gn
+++ b/components/optimization_guide/proto/BUILD.gn
@@ -15,6 +15,7 @@
     "autofill_field_classification_model_metadata.proto",
     "client_side_phishing_model_metadata.proto",
     "common_types.proto",
+    "contextual_cueing_metadata.proto",
     "descriptors.proto",
     "features/bling_prototyping.proto",
     "features/common_quality_data.proto",
diff --git a/components/optimization_guide/proto/contextual_cueing_metadata.proto b/components/optimization_guide/proto/contextual_cueing_metadata.proto
new file mode 100644
index 0000000..f3991d7
--- /dev/null
+++ b/components/optimization_guide/proto/contextual_cueing_metadata.proto
@@ -0,0 +1,61 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+option java_package = "org.chromium.components.optimization_guide.proto";
+option java_outer_classname = "ContextualCueingMetadataProto";
+
+package optimization_guide.proto;
+
+enum ContextualCueingClientSignal {
+  CONTEXTUAL_CUEING_CLIENT_SIGNAL_UNSPECIFIED = 0;
+  // The number of pages in the PDF, if the page being viewed is a PDF.
+  CONTEXTUAL_CUEING_CLIENT_SIGNAL_PDF_PAGE_COUNT = 1;
+  // The number of words in the page being viewed if not a PDF.
+  CONTEXTUAL_CUEING_CLIENT_SIGNAL_CONTENT_LENGTH_WORD_COUNT = 2;
+}
+
+enum ContextualCueingOperator {
+  CONTEXTUAL_CUEING_OPERATOR_UNSPECIFIED = 0;
+  CONTEXTUAL_CUEING_OPERATOR_GREATER_THAN_OR_EQUAL_TO = 1;
+  CONTEXTUAL_CUEING_OPERATOR_LESS_THAN_OR_EQUAL_TO = 2;
+}
+
+message ContextualCueingConditions {
+  // The signal to check.
+  optional ContextualCueingClientSignal signal = 1;
+
+  // The operator to use when comparing the signal to the threshold.
+  optional ContextualCueingOperator cueing_operator = 2;
+
+  oneof threshold {
+    // The int64 threshold value to compare the signal to.
+    int64 int64_threshold = 3;
+  }
+}
+
+message GlicContextualCueingMetadata {
+  // Cueing configurations supported for this page.
+  //
+  // Take the first one that matches something the current client state and
+  // capabilities.
+  //
+  // If there is no match, then the client should not show any cues.
+  repeated GlicCueingConfiguration cueing_configurations = 1;
+}
+
+message GlicCueingConfiguration {
+  // The cue label to show in the tab strip.
+  //
+  // This will always be non-empty.
+  optional string cue_label = 1;
+
+  // The conditions that must be met for this cueing configuration to be
+  // eligible. This should be treated as an AND operation.
+  //
+  // If this list is empty, this cueing configuration is always eligible.
+  repeated ContextualCueingConditions conditions = 2;
+}
diff --git a/components/optimization_guide/proto/text_safety_model_metadata.proto b/components/optimization_guide/proto/text_safety_model_metadata.proto
index e602f8b..96bcc2c 100644
--- a/components/optimization_guide/proto/text_safety_model_metadata.proto
+++ b/components/optimization_guide/proto/text_safety_model_metadata.proto
@@ -140,4 +140,9 @@
 
   // Configures check that run on parsed model output, with request as context.
   repeated ResponseSafetyCheck response_check = 6;
+
+  // When enabled, the unsafe text does not cancel a pending response, but
+  // instead just doesn't send it until it is safe again. If the text is still
+  // unsafe on complete, then it cancels the pending response.
+  optional bool only_cancel_unsafe_response_on_complete = 7;
 }
diff --git a/components/os_crypt/async/browser/freedesktop_secret_key_provider.cc b/components/os_crypt/async/browser/freedesktop_secret_key_provider.cc
index e42e545..fd9313c 100644
--- a/components/os_crypt/async/browser/freedesktop_secret_key_provider.cc
+++ b/components/os_crypt/async/browser/freedesktop_secret_key_provider.cc
@@ -87,14 +87,6 @@
           interface_name, method_name, std::move(callback)));
 }
 
-scoped_refptr<dbus::Bus> CreateBus() {
-  dbus::Bus::Options options;
-  options.bus_type = dbus::Bus::SESSION;
-  options.connection_type = dbus::Bus::PRIVATE;
-  options.dbus_task_runner = dbus_thread_linux::GetTaskRunner();
-  return base::MakeRefCounted<dbus::Bus>(options);
-}
-
 const char* InitStatusToString(
     FreedesktopSecretKeyProvider::InitStatus status) {
   switch (status) {
@@ -255,7 +247,7 @@
       product_name_(product_name),
       bus_(std::move(bus)) {
   if (!bus_) {
-    bus_ = CreateBus();
+    bus_ = dbus_thread_linux::GetSharedSessionBus();
   }
 }
 
diff --git a/components/os_crypt/async/browser/secret_portal_key_provider.cc b/components/os_crypt/async/browser/secret_portal_key_provider.cc
index 818f4e3..ad5fcca 100644
--- a/components/os_crypt/async/browser/secret_portal_key_provider.cc
+++ b/components/os_crypt/async/browser/secret_portal_key_provider.cc
@@ -40,14 +40,6 @@
 constexpr char kSaltForHkdf[] = "fdo_portal_secret_salt";
 constexpr char kInfoForHkdf[] = "HKDF-SHA-256 AES-256-GCM";
 
-scoped_refptr<dbus::Bus> CreateBus() {
-  dbus::Bus::Options options;
-  options.bus_type = dbus::Bus::SESSION;
-  options.connection_type = dbus::Bus::PRIVATE;
-  options.dbus_task_runner = dbus_thread_linux::GetTaskRunner();
-  return base::MakeRefCounted<dbus::Bus>(options);
-}
-
 }  // namespace
 
 // static
@@ -59,12 +51,12 @@
 
 SecretPortalKeyProvider::SecretPortalKeyProvider(PrefService* local_state,
                                                  bool use_for_encryption)
-    : SecretPortalKeyProvider(local_state, CreateBus(), use_for_encryption) {}
+    : SecretPortalKeyProvider(local_state,
+                              dbus_thread_linux::GetSharedSessionBus(),
+                              use_for_encryption) {}
 
 SecretPortalKeyProvider::~SecretPortalKeyProvider() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  bus_->GetDBusTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, bus_));
 }
 
 SecretPortalKeyProvider::SecretPortalKeyProvider(PrefService* local_state,
diff --git a/components/os_crypt/async/browser/secret_portal_key_provider_unittest.cc b/components/os_crypt/async/browser/secret_portal_key_provider_unittest.cc
index ef25f690..c3d7f777 100644
--- a/components/os_crypt/async/browser/secret_portal_key_provider_unittest.cc
+++ b/components/os_crypt/async/browser/secret_portal_key_provider_unittest.cc
@@ -122,8 +122,6 @@
   }
 
   void TearDown() override {
-    EXPECT_CALL(*mock_bus_, ShutdownAndBlock());
-
     // Shutdown the bus to ensure clean-up
     key_provider_.reset();
 
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/ConnectionInfoView.java b/components/page_info/android/java/src/org/chromium/components/page_info/ConnectionInfoView.java
index 96d0823..d0c079d 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/ConnectionInfoView.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/ConnectionInfoView.java
@@ -246,7 +246,7 @@
                         }
 
                         @Override
-                        public void onDestroy() {
+                        public void webContentsDestroyed() {
                             dismiss(DialogDismissalCause.UNKNOWN);
                         }
                     };
@@ -263,7 +263,7 @@
         @Override
         public void onDismiss(PropertyModel model, @DialogDismissalCause int dismissalCause) {
             mPopup.onDismiss();
-            mWebContentsObserver.destroy();
+            mWebContentsObserver.observe(null);
             mDialogModel = null;
         }
 
diff --git a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java
index 41cabf3..440557c 100644
--- a/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java
+++ b/components/page_info/android/java/src/org/chromium/components/page_info/PageInfoController.java
@@ -324,9 +324,7 @@
                     }
 
                     @Override
-                    public void onDestroy() {
-                        // Force the dialog to close immediately in case the destroy was from Chrome
-                        // quitting.
+                    public void webContentsDestroyed() {
                         PageInfoController.this.destroy();
                     }
 
@@ -351,6 +349,11 @@
     }
 
     private void destroy() {
+        if (mWebContentsObserver != null) {
+            mWebContentsObserver.observe(null);
+            mWebContentsObserver = null;
+        }
+
         if (mDialog != null) {
             mDialog.destroy();
             mDialog = null;
@@ -457,8 +460,9 @@
             mCurrentSubpageController.onSubpageRemoved();
             mCurrentSubpageController = null;
         }
-        mWebContentsObserver.destroy();
-        mWebContentsObserver = null;
+
+        destroy();
+
         PageInfoControllerJni.get().destroy(mNativePageInfoController, PageInfoController.this);
         mNativePageInfoController = 0;
         mContext = null;
diff --git a/components/policy/resources/templates/policies.yaml b/components/policy/resources/templates/policies.yaml
index 3352ed9..82ce65f 100644
--- a/components/policy/resources/templates/policies.yaml
+++ b/components/policy/resources/templates/policies.yaml
@@ -1325,7 +1325,7 @@
   1324: WebRtcIPHandlingUrl
   1325: GenAIPhotoEditingSettings
   1326: PartitionedBlobUrlUsage
-  1327: GlicEnabled
+  1327: GlicSettings
   1328: DefaultControlledFrameSetting
   1329: ControlledFrameAllowedForUrls
   1330: ControlledFrameBlockedForUrls
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/GlicEnabled.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/GlicSettings.yaml
similarity index 77%
rename from components/policy/resources/templates/policy_definitions/Miscellaneous/GlicEnabled.yaml
rename to components/policy/resources/templates/policy_definitions/Miscellaneous/GlicSettings.yaml
index 59c4652..40b2530 100644
--- a/components/policy/resources/templates/policy_definitions/Miscellaneous/GlicEnabled.yaml
+++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/GlicSettings.yaml
@@ -1,4 +1,4 @@
-caption: Enable <ph name="GLIC">Glic</ph>
+caption: Control <ph name="GLIC">Glic</ph> access
 desc: |-
   If this policy is Enabled or not set, users are able to use the
   <ph name="GLIC">Glic</ph> feature. Setting this policy to Disabled prevents
@@ -8,20 +8,25 @@
 owners:
 - bokan@chromium.org
 - file://chrome/browser/glic/OWNERS
-example_value: true
+example_value: 0
 future_on:
 - chrome.*
 features:
   dynamic_refresh: true
   per_profile: true
-type: main
+type: int-enum
 schema:
-  type: boolean
+  type: integer
+  enum:
+  - 0
+  - 1
 items:
 - caption: Enable use of <ph name="GLIC">Glic</ph>
-  value: true
+  name: Enabled
+  value: 0
 - caption: Disable use of <ph name="GLIC">Glic</ph>.
-  value: false
-default: true
+  name: Disabled
+  value: 1
+default: 0
 tags:
 - google-sharing
diff --git a/components/policy/test/data/pref_mapping/GlicEnabled.json b/components/policy/test/data/pref_mapping/GlicSettings.json
similarity index 77%
rename from components/policy/test/data/pref_mapping/GlicEnabled.json
rename to components/policy/test/data/pref_mapping/GlicSettings.json
index adefe72..feebfdd 100644
--- a/components/policy/test/data/pref_mapping/GlicEnabled.json
+++ b/components/policy/test/data/pref_mapping/GlicSettings.json
@@ -7,10 +7,10 @@
     ],
     "simple_policy_pref_mapping_test": {
       "pref_name": "glic.enabled_by_policy",
-      "default_value": true,
+      "default_value": 0,
       "values_to_test": [
-        true,
-        false
+        0,
+        1
       ]
     }
   }
diff --git a/components/power_monitor/power_monitor_device_source_linux.cc b/components/power_monitor/power_monitor_device_source_linux.cc
index 96800ac8..42901c0 100644
--- a/components/power_monitor/power_monitor_device_source_linux.cc
+++ b/components/power_monitor/power_monitor_device_source_linux.cc
@@ -18,20 +18,8 @@
 #include "dbus/object_path.h"
 #include "dbus/object_proxy.h"
 
-namespace {
-
-scoped_refptr<dbus::Bus> CreateBus() {
-  dbus::Bus::Options options;
-  options.bus_type = dbus::Bus::SYSTEM;
-  options.connection_type = dbus::Bus::PRIVATE;
-  options.dbus_task_runner = dbus_thread_linux::GetTaskRunner();
-  return base::MakeRefCounted<dbus::Bus>(options);
-}
-
-}  // namespace
-
 PowerMonitorDeviceSourceLinux::PowerMonitorDeviceSourceLinux()
-    : bus_(CreateBus()) {
+    : bus_(dbus_thread_linux::GetSharedSystemBus()) {
   bus_->GetObjectProxy("org.freedesktop.login1",
                        dbus::ObjectPath("/org/freedesktop/login1"))
       ->ConnectToSignal(
@@ -42,10 +30,7 @@
                          weak_ptr_factory_.GetWeakPtr()));
 }
 
-PowerMonitorDeviceSourceLinux::~PowerMonitorDeviceSourceLinux() {
-  if (bus_)
-    ShutdownBus();
-}
+PowerMonitorDeviceSourceLinux::~PowerMonitorDeviceSourceLinux() = default;
 
 base::PowerStateObserver::BatteryPowerStatus
 PowerMonitorDeviceSourceLinux::GetBatteryPowerStatus() const {
@@ -55,24 +40,16 @@
   return base::PowerStateObserver::BatteryPowerStatus::kUnknown;
 }
 
-void PowerMonitorDeviceSourceLinux::ShutdownBus() {
-  DCHECK(bus_);
-  dbus::Bus* const bus_ptr = bus_.get();
-  bus_ptr->GetDBusTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, std::move(bus_)));
-}
-
 void PowerMonitorDeviceSourceLinux::OnSignalConnected(
     const std::string& interface_name,
     const std::string& signal_name,
     bool connected) {
-  if (connected)
+  if (connected) {
     return;
+  }
 
   DLOG(ERROR) << "Failed to connect to " << interface_name << " for signal "
               << signal_name;
-  if (bus_)
-    ShutdownBus();
 }
 
 void PowerMonitorDeviceSourceLinux::OnPrepareForSleep(dbus::Signal* signal) {
diff --git a/components/power_monitor/power_monitor_device_source_linux.h b/components/power_monitor/power_monitor_device_source_linux.h
index 0306b73..061b089 100644
--- a/components/power_monitor/power_monitor_device_source_linux.h
+++ b/components/power_monitor/power_monitor_device_source_linux.h
@@ -31,7 +31,6 @@
       const override;
 
  private:
-  void ShutdownBus();
   void OnSignalConnected(const std::string& interface_name,
                          const std::string& signal_name,
                          bool connected);
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index 2927842e..2930f74 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -1528,11 +1528,14 @@
     return;
 
   ContentProxySet typeface_content_info;
+  ContentProxySet image_content_info;
   MetafileSkia metafile(mojom::SkiaDocumentType::kMSKP,
                         params->document_cookie);
 
-  // Provide a typeface context to use with serializing to the print compositor.
+  // Provide typeface and image contexts to use with serializing to the print
+  // compositor.
   metafile.UtilizeTypefaceContext(&typeface_content_info);
+  metafile.UtilizeImageContext(&image_content_info);
 
   gfx::Size area_size = params->printable_area.size();
   // Since GetVectorCanvasForNewPage() starts a new recording, it will return
@@ -1834,6 +1837,8 @@
   }
   render_metafile->UtilizeTypefaceContext(
       print_preview_context_.typeface_content_info());
+  render_metafile->UtilizeImageContext(
+      print_preview_context_.image_content_info());
   base::TimeTicks begin_time = base::TimeTicks::Now();
   PrintPageInternalResult result = PrintPageInternal(
       print_params, page_index, print_preview_context_.total_page_count(),
@@ -2242,12 +2247,15 @@
   const mojom::PrintPagesParams& params = *print_pages_params_;
   const mojom::PrintParams& print_params = *params.params;
 
-  // Provide a typeface context to use with serializing to the print compositor.
+  // Provide typeface and image context to use with serializing to the print
+  // compositor.
   ContentProxySet typeface_content_info;
+  ContentProxySet image_content_info;
   MetafileSkia metafile(print_params.printed_doc_type,
                         print_params.document_cookie);
   CHECK(metafile.Init());
   metafile.UtilizeTypefaceContext(&typeface_content_info);
+  metafile.UtilizeImageContext(&image_content_info);
 
   bool generate_tagged_pdf = print_params.generate_tagged_pdf.value_or(
       delegate_->ShouldGenerateTaggedPDF());
@@ -3038,10 +3046,17 @@
   return &typeface_content_info_;
 }
 
+ContentProxySet*
+PrintRenderFrameHelper::PrintPreviewContext::image_content_info() {
+  DCHECK(IsRendering());
+  return &image_content_info_;
+}
+
 void PrintRenderFrameHelper::PrintPreviewContext::ClearContext() {
   prep_frame_view_.reset();
   metafile_.reset();
   typeface_content_info_.clear();
+  image_content_info_.clear();
   pages_to_render_.clear();
   error_ = PrintPreviewErrorBuckets::kNone;
 }
diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h
index ce8ac0a..97cb6458 100644
--- a/components/printing/renderer/print_render_frame_helper.h
+++ b/components/printing/renderer/print_render_frame_helper.h
@@ -557,6 +557,7 @@
     size_t pages_rendered_count() const;
     MetafileSkia* metafile();
     ContentProxySet* typeface_content_info();
+    ContentProxySet* image_content_info();
 
    private:
     enum class State {
@@ -580,6 +581,9 @@
     // The typefaces encountered in the content during document serialization.
     ContentProxySet typeface_content_info_;
 
+    // The images encountered in the content during document serialization.
+    ContentProxySet image_content_info_;
+
     // A document metafile is needed when not using the print compositor.
     std::unique_ptr<MetafileSkia> metafile_;
 
diff --git a/components/privacy_sandbox/tracking_protection_settings.cc b/components/privacy_sandbox/tracking_protection_settings.cc
index 6265222..6671c3a5 100644
--- a/components/privacy_sandbox/tracking_protection_settings.cc
+++ b/components/privacy_sandbox/tracking_protection_settings.cc
@@ -118,7 +118,8 @@
 
 bool TrackingProtectionSettings::IsFpProtectionEnabled() const {
   return pref_service_->GetBoolean(prefs::kFingerprintingProtectionEnabled) &&
-         base::FeatureList::IsEnabled(kFingerprintingProtectionUx);
+         base::FeatureList::IsEnabled(kFingerprintingProtectionUx) &&
+         is_incognito_;
 }
 
 bool TrackingProtectionSettings::IsDoNotTrackEnabled() const {
diff --git a/components/privacy_sandbox/tracking_protection_settings_unittest.cc b/components/privacy_sandbox/tracking_protection_settings_unittest.cc
index a50d6fa0..874c14d 100644
--- a/components/privacy_sandbox/tracking_protection_settings_unittest.cc
+++ b/components/privacy_sandbox/tracking_protection_settings_unittest.cc
@@ -104,11 +104,15 @@
   EXPECT_TRUE(tracking_protection_settings()->IsIpProtectionEnabled());
 }
 
-TEST_F(TrackingProtectionSettingsTest, ReturnsFpProtectionStatus) {
-  EXPECT_FALSE(prefs()->GetBoolean(prefs::kFingerprintingProtectionEnabled));
-  EXPECT_FALSE(tracking_protection_settings()->IsFpProtectionEnabled());
+TEST_F(TrackingProtectionSettingsTest,
+       IsFpProtectionEnabledOnlyReturnsTrueInIncognito) {
   prefs()->SetBoolean(prefs::kFingerprintingProtectionEnabled, true);
-  EXPECT_TRUE(tracking_protection_settings()->IsFpProtectionEnabled());
+  EXPECT_TRUE(TrackingProtectionSettings(prefs(), host_content_settings_map(),
+                                         /*is_incognito=*/true)
+                  .IsFpProtectionEnabled());
+  EXPECT_FALSE(TrackingProtectionSettings(prefs(), host_content_settings_map(),
+                                          /*is_incognito=*/false)
+                   .IsFpProtectionEnabled());
 }
 
 TEST_F(TrackingProtectionSettingsTest, ReturnsTrackingProtection3pcdStatus) {
diff --git a/components/search_engines/template_url_parser.cc b/components/search_engines/template_url_parser.cc
index 3a7f7e5..4482e7ad 100644
--- a/components/search_engines/template_url_parser.cc
+++ b/components/search_engines/template_url_parser.cc
@@ -55,6 +55,9 @@
 // Mime type for as you type suggestions.
 const char kSuggestionType[] = "application/x-suggestions+json";
 
+// Short name and keyword string lengths are capped for security.
+constexpr size_t kMaxNameLength = 1024;
+
 // Returns true if input_encoding contains a valid input encoding string. This
 // doesn't verify that we have a valid encoding for the string, just that the
 // string contains characters that constitute a valid input encoding.
@@ -413,6 +416,14 @@
     return nullptr;
   }
 
+  // To avoid potential downstream issues when using the data (for example
+  // crossing IPC for presentation in search engines UI), data that exceeds
+  // the limit is not finalized and accepted.
+  if (template_url->data().short_name().length() > kMaxNameLength ||
+      template_url->data().keyword().length() > kMaxNameLength) {
+    return nullptr;
+  }
+
   return template_url;
 }
 
diff --git a/components/services/print_compositor/BUILD.gn b/components/services/print_compositor/BUILD.gn
index ee69dc2..b2e2c8a 100644
--- a/components/services/print_compositor/BUILD.gn
+++ b/components/services/print_compositor/BUILD.gn
@@ -66,9 +66,8 @@
       deps += [
         "//cc:test_support",
         "//components/enterprise/watermarking",
+        "//components/enterprise/watermarking:watermark_test_utils",
       ]
-
-      public_deps = [ "//components/enterprise/watermarking/mojom" ]
     }
   }
 }
diff --git a/components/services/print_compositor/print_compositor_impl.cc b/components/services/print_compositor/print_compositor_impl.cc
index 45ea749..33dadae 100644
--- a/components/services/print_compositor/print_compositor_impl.cc
+++ b/components/services/print_compositor/print_compositor_impl.cc
@@ -440,7 +440,8 @@
   }
 
   std::vector<SkDocumentPage> pages(page_count);
-  SkDeserialProcs procs = DeserializationProcs(&subframes, &typefaces_);
+  SkDeserialProcs procs =
+      DeserializationProcs(&subframes, &typefaces_, &images_);
   if (!SkMultiPictureDocument::Read(&stream, pages.data(), page_count,
                                     &procs)) {
     DLOG(ERROR) << "CompositePages: Page reading failed.";
@@ -499,8 +500,8 @@
   // Composite the entire frame.
   SkMemoryStream stream(frame_info->serialized_content.data(),
                         frame_info->serialized_content.size());
-  SkDeserialProcs procs =
-      DeserializationProcs(&subframes, &frame_info->typefaces);
+  SkDeserialProcs procs = DeserializationProcs(
+      &subframes, &frame_info->typefaces, &frame_info->images);
   frame_info->content = SkPicture::MakeFromStream(&stream, &procs);
 }
 
diff --git a/components/services/print_compositor/print_compositor_impl.h b/components/services/print_compositor/print_compositor_impl.h
index f0c67514..7b850b60 100644
--- a/components/services/print_compositor/print_compositor_impl.h
+++ b/components/services/print_compositor/print_compositor_impl.h
@@ -24,6 +24,7 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
 #include "printing/buildflags/buildflags.h"
+#include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/core/SkPicture.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkStream.h"
@@ -158,6 +159,7 @@
       base::flat_map<uint32_t, sk_sp<SkPicture>>;
   using TypefaceDeserializationContext =
       base::flat_map<uint32_t, sk_sp<SkTypeface>>;
+  using ImageDeserializationContext = base::flat_map<uint32_t, sk_sp<SkImage>>;
 
   // Base structure to store a frame's content and its subframe
   // content information.
@@ -178,6 +180,9 @@
 
     // Typefaces used within scope of this frame.
     TypefaceDeserializationContext typefaces;
+
+    // Images used within scope of this frame.
+    ImageDeserializationContext images;
   };
 
   // Other than content, it also stores the status during frame composition.
@@ -285,6 +290,9 @@
   // Context for dealing with all typefaces encountered across multiple pages.
   TypefaceDeserializationContext typefaces_;
 
+  // Context for dealing with all images encountered across multiple pages.
+  ImageDeserializationContext images_;
+
   std::vector<std::unique_ptr<RequestInfo>> requests_;
   std::unique_ptr<DocumentInfo> doc_info_;
 
diff --git a/components/services/print_compositor/print_compositor_impl_unittest.cc b/components/services/print_compositor/print_compositor_impl_unittest.cc
index bf70a4d..2c322a0d 100644
--- a/components/services/print_compositor/print_compositor_impl_unittest.cc
+++ b/components/services/print_compositor/print_compositor_impl_unittest.cc
@@ -21,19 +21,14 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if BUILDFLAG(ENTERPRISE_WATERMARK)
-#include "base/memory/shared_memory_mapping.h"
 #include "base/test/scoped_feature_list.h"
 #include "cc/test/pixel_test_utils.h"                     // nogncheck
 #include "components/enterprise/watermarking/features.h"  // nogncheck
 #include "components/enterprise/watermarking/mojom/watermark.mojom.h"  // nogncheck
 #include "components/enterprise/watermarking/watermark.h"  // nogncheck
-#include "skia/ext/font_utils.h"
+#include "components/enterprise/watermarking/watermark_test_utils.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/core/SkPictureRecorder.h"
-#include "third_party/skia/include/core/SkSerialProcs.h"
-#include "third_party/skia/include/core/SkTextBlob.h"
 #include "third_party/skia/include/docs/SkMultiPictureDocument.h"
 #endif
 
@@ -111,51 +106,14 @@
 };
 
 #if BUILDFLAG(ENTERPRISE_WATERMARK)
-
-watermark::mojom::WatermarkBlockPtr MakeTestWatermarkBlock(
-    const std::string& watermark_text) {
-  // Initialize text blob
-  static constexpr SkScalar kTextSize = 30.0f;
-  SkFont font(skia::DefaultTypeface(), kTextSize, 1.0f, 0.0f);
-  sk_sp<SkTextBlob> blob =
-      SkTextBlob::MakeFromString(watermark_text.c_str(), font);
-
-  // Draw onto SkPicture-backed SkCanvas
-  SkPictureRecorder recorder;
-  SkCanvas* canvas = recorder.beginRecording(
-      SkRect{SkRect::MakeWH(kWatermarkSize.fWidth, kWatermarkSize.fHeight)});
-  SkPaint paint;
-  paint.setColor(SK_ColorWHITE);
-  canvas->drawTextBlob(blob.get(), 0.0f, 0.0f, paint);
-  sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
-
-  // Serialize SkPicture
-  SkDynamicMemoryWStream stream;
-  SkSerialProcs procs;
-  picture->serialize(&stream, &procs);
-  base::MappedReadOnlyRegion region_mapping =
-      base::ReadOnlySharedMemoryRegion::Create(stream.bytesWritten());
-  if (!region_mapping.IsValid()) {
-    return nullptr;
-  }
-  stream.copyTo(region_mapping.mapping.memory());
-
-  // Measure string dimensions
-  SkScalar text_width = font.measureText(
-      watermark_text.c_str(), watermark_text.size(), SkTextEncoding::kUTF8);
-
-  // Construct test data
-  return watermark::mojom::WatermarkBlockPtr(
-      std::in_place, std::move(region_mapping.region), text_width, kTextSize);
-}
-
 class MockPrintCompositorImplEnterpriseWatermark : public PrintCompositorImpl {
  public:
   MockPrintCompositorImplEnterpriseWatermark()
       : PrintCompositorImpl(mojo::NullReceiver(),
                             /*initialize_environment=*/false,
                             /*io_task_runner=*/nullptr) {
-    SetWatermarkBlock(MakeTestWatermarkBlock(kWatermarkText));
+    SetWatermarkBlock(enterprise_watermark::MakeTestWatermarkBlock(
+        kWatermarkText, kWatermarkSize));
   }
 
   ~MockPrintCompositorImplEnterpriseWatermark() override = default;
@@ -250,7 +208,8 @@
                                         kWatermarkSize.fHeight);
     SkCanvas canvas(reference_watermark_);
     canvas.clear(SK_ColorBLACK);
-    const auto watermark_block = MakeTestWatermarkBlock(kWatermarkText);
+    const auto watermark_block = enterprise_watermark::MakeTestWatermarkBlock(
+        kWatermarkText, kWatermarkSize);
     DrawWatermarkBlockForTesting(&canvas, kWatermarkSize, watermark_block);
   }
 
diff --git a/components/sessions/core/tab_restore_service_helper.cc b/components/sessions/core/tab_restore_service_helper.cc
index 668c5ed..84d2238 100644
--- a/components/sessions/core/tab_restore_service_helper.cc
+++ b/components/sessions/core/tab_restore_service_helper.cc
@@ -692,7 +692,10 @@
 
         if (window.tabs.empty()) {
           // Remove the entry if there is nothing left to restore.
-          entries_.erase(entry_iterator);
+          // The entries_ may by changed after the tabs restored and the
+          // entry_iterator may be no longer valid. So call RemoveEntryById here
+          // instead of entries_.erase(entry_iterator).
+          RemoveEntryById(id);
         }
       }
 
@@ -745,7 +748,10 @@
             CHECK(ValidateGroup(group));
             group.tabs.erase(group.tabs.begin() + i);
             if (group.tabs.empty()) {
-              entries_.erase(entry_iterator);
+              // The entries_ may by changed after the tabs restored and the
+              // entry_iterator may be no longer valid. So call RemoveEntryById
+              // here instead of entries_.erase(entry_iterator).
+              RemoveEntryById(id);
             }
 
             break;
@@ -759,7 +765,10 @@
   }
 
   if (entry_id_matches_restore_id) {
-    entries_.erase(entry_iterator);
+    // The entries_ may by changed after the tabs restored and the
+    // entry_iterator may be no longer valid. So call RemoveEntryById here
+    // instead of entries_.erase(entry_iterator).
+    RemoveEntryById(id);
   }
 
   restoring_ = false;
diff --git a/components/variations/BUILD.gn b/components/variations/BUILD.gn
index 2eb17c3..6e23d913 100644
--- a/components/variations/BUILD.gn
+++ b/components/variations/BUILD.gn
@@ -114,14 +114,6 @@
     ]
   }
 
-  if (is_android) {
-    sources += [
-      "android/variations_associated_data_android.cc",
-      "android/variations_seed_bridge.cc",
-      "android/variations_seed_bridge.h",
-    ]
-  }
-
   if (is_chromeos) {
     sources += [
       "variations_crash_keys_chromeos.cc",
@@ -148,7 +140,11 @@
   ]
 
   if (is_android) {
-    deps += [ "//components/variations/android:variations_jni" ]
+    sources += [
+      "android/variations_seed_bridge.cc",
+      "android/variations_seed_bridge.h",
+    ]
+    deps += [ "//components/variations/android:variations_seed_jni" ]
   }
 
   if (is_chromeos) {
@@ -160,6 +156,15 @@
 }
 
 if (is_android) {
+  static_library("variations_associated_data") {
+    sources = [ "android/variations_associated_data_android.cc" ]
+    deps = [
+      ":variations",
+      "//components/variations/android:variations_data_jni",
+      "//components/variations/net",
+    ]
+  }
+
   java_cpp_strings("java_switches_srcjar") {
     # External code should depend on ":variations_java" instead.
     visibility = [ ":*" ]
diff --git a/components/variations/android/BUILD.gn b/components/variations/android/BUILD.gn
index 8fb1ab1..6dbdd640 100644
--- a/components/variations/android/BUILD.gn
+++ b/components/variations/android/BUILD.gn
@@ -5,10 +5,13 @@
 import("//build/config/android/rules.gni")
 import("//third_party/jni_zero/jni_zero.gni")
 
-generate_jni("variations_jni") {
+generate_jni("variations_seed_jni") {
+  sources = [ "java/src/org/chromium/components/variations/firstrun/VariationsSeedBridge.java" ]
+}
+
+generate_jni("variations_data_jni") {
   sources = [
     "java/src/org/chromium/components/variations/VariationsAssociatedData.java",
-    "java/src/org/chromium/components/variations/firstrun/VariationsSeedBridge.java",
   ]
 }
 
@@ -24,7 +27,10 @@
     "//third_party/jni_zero:jni_zero_java",
   ]
 
-  srcjar_deps = [ ":variations_jni" ]
+  srcjar_deps = [
+    ":variations_data_jni",
+    ":variations_seed_jni",
+  ]
   sources = [
     "java/src/org/chromium/components/variations/NormalizedMurmurHashEntropyProvider.java",
     "java/src/org/chromium/components/variations/VariationsAssociatedData.java",
diff --git a/components/variations/android/java/src/org/chromium/components/variations/VariationsAssociatedData.java b/components/variations/android/java/src/org/chromium/components/variations/VariationsAssociatedData.java
index fdbd299d..bfbb226 100644
--- a/components/variations/android/java/src/org/chromium/components/variations/VariationsAssociatedData.java
+++ b/components/variations/android/java/src/org/chromium/components/variations/VariationsAssociatedData.java
@@ -28,6 +28,14 @@
         return VariationsAssociatedDataJni.get().getVariationParamValue(trialName, paramName);
     }
 
+    /**
+     * Get a HashMap with the value of a space-separated string containing the list of current
+     * active variations (as would be reported in the |variation_id| repeated field of the
+     * ClientVariations proto) for a given ID collection. See more at
+     * `VariationsIdsProvider::GetVariationsString()`.
+     *
+     * @return A HashMap with value containing the current active variations.
+     */
     public static HashMap<String, String> getFeedbackMap() {
         HashMap<String, String> map = new HashMap<String, String>();
         map.put("Chrome Variations", VariationsAssociatedDataJni.get().getFeedbackVariations());
@@ -35,11 +43,24 @@
     }
 
     /**
+     * Get the encrypted variations state. Variations state is the commandline variations that a
+     * person can pass in to Chrome, which specifies Chrome features' on or off states. See more at
+     * `VariationsCommandLine::EncryptToString()`
+     *
+     * @return A HashMap with value containing the encrypted variations state.
+     */
+    public static HashMap<String, String> getVariationsStateFeedbackMap() {
+        HashMap<String, String> map = new HashMap<String, String>();
+        map.put("Chrome Variations State", VariationsAssociatedDataJni.get().getVariationsState());
+        return map;
+    }
+
+    /**
      * Returns the list of Google App variations from active finch field trials.
-     * @return A space separated list of ids with leading and trailing space.
-     * For example, " 123 456 ".
-     * IMPORTANT: This string is only approved for integrations with the Android
-     * Google App and must receive a privacy review before extending to other apps.
+     *
+     * @return A space separated list of ids with leading and trailing space. e.g. " 123 456 ".
+     *     IMPORTANT: This string is only approved for integrations with the Android Google App and
+     *     must receive a privacy review before extending to other apps.
      */
     public static String getGoogleAppVariations() {
         String variations = VariationsAssociatedDataJni.get().getGoogleAppVariations();
@@ -52,6 +73,8 @@
 
         String getFeedbackVariations();
 
+        String getVariationsState();
+
         String getGoogleAppVariations();
     }
 }
diff --git a/components/variations/android/variations_associated_data_android.cc b/components/variations/android/variations_associated_data_android.cc
index 3b81de1..f9ce88e3 100644
--- a/components/variations/android/variations_associated_data_android.cc
+++ b/components/variations/android/variations_associated_data_android.cc
@@ -5,12 +5,14 @@
 #include <string>
 
 #include "base/android/jni_string.h"
+#include "base/base64.h"
 #include "base/metrics/field_trial_params.h"
-#include "components/variations/variations_associated_data.h"
+#include "base/metrics/histogram_functions.h"
+#include "components/variations/net/variations_command_line.h"
 #include "components/variations/variations_ids_provider.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
-#include "components/variations/android/variations_jni/VariationsAssociatedData_jni.h"
+#include "components/variations/android/variations_data_jni/VariationsAssociatedData_jni.h"
 
 using base::android::ConvertJavaStringToUTF8;
 using base::android::ConvertUTF8ToJavaString;
@@ -38,6 +40,24 @@
   return ConvertUTF8ToJavaString(env, values);
 }
 
+ScopedJavaLocalRef<jstring> JNI_VariationsAssociatedData_GetVariationsState(
+    JNIEnv* env) {
+  if (!base::FeatureList::IsEnabled(variations::kFeedbackIncludeVariations)) {
+    return nullptr;
+  }
+  std::vector<uint8_t> ciphertext;
+  const auto status =
+      variations::VariationsCommandLine::GetForCurrentProcess().EncryptToString(
+          &ciphertext);
+  base::UmaHistogramEnumeration("Variations.VariationsStateEncryptionStatus",
+                                status);
+  if (status != variations::VariationsStateEncryptionStatus::kSuccess) {
+    return nullptr;
+  }
+  std::string value = base::Base64Encode(ciphertext);
+  return ConvertUTF8ToJavaString(env, value);
+}
+
 ScopedJavaLocalRef<jstring> JNI_VariationsAssociatedData_GetGoogleAppVariations(
     JNIEnv* env) {
   const std::string values =
diff --git a/components/variations/android/variations_seed_bridge.cc b/components/variations/android/variations_seed_bridge.cc
index 39db2c3..9240ddd 100644
--- a/components/variations/android/variations_seed_bridge.cc
+++ b/components/variations/android/variations_seed_bridge.cc
@@ -14,7 +14,7 @@
 #include "base/time/time.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
-#include "components/variations/android/variations_jni/VariationsSeedBridge_jni.h"
+#include "components/variations/android/variations_seed_jni/VariationsSeedBridge_jni.h"
 
 using base::android::AttachCurrentThread;
 using base::android::ConvertJavaStringToUTF8;
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc
index c9f75e4..83bfa29 100644
--- a/components/viz/common/features.cc
+++ b/components/viz/common/features.cc
@@ -264,14 +264,6 @@
 BASE_FEATURE(kVSyncAlignedPresent,
              "VSyncAlignedPresent",
              base::FEATURE_DISABLED_BY_DEFAULT);
-
-// The paramters for the number of supported pending Frames.
-// 1: Support one pending frame. This is the old default.
-// 2: Support two pending frames. New. This is the number of max pending
-//    swap in the scheduler.
-// Others: Error! It will be overwritten to 2 pending frames.
-const base::FeatureParam<int> kNumPendingFrames{&kVSyncAlignedPresent,
-                                                "PendingFrames", 2};
 #endif
 
 BASE_FEATURE(kAllowUndamagedNonrootRenderPassToSkip,
@@ -633,18 +625,6 @@
 bool IsVSyncAlignedPresentEnabled() {
   return base::FeatureList::IsEnabled(features::kVSyncAlignedPresent);
 }
-
-int NumPendingFrameSupported() {
-  // Return the old default if this feature is not enabled.
-  if (!base::FeatureList::IsEnabled(kVSyncAlignedPresent)) {
-    return 1;
-  }
-
-  // Unless 1 pending frame is specified, overwrite all other params to the new
-  // default, 2 pending frames.
-  int num = kNumPendingFrames.Get() == 1 ? 1 : 2;
-  return num;
-}
 #endif
 
 #if BUILDFLAG(IS_CHROMEOS)
diff --git a/components/viz/common/features.h b/components/viz/common/features.h
index 82d6a04..a5042a0 100644
--- a/components/viz/common/features.h
+++ b/components/viz/common/features.h
@@ -168,7 +168,6 @@
 VIZ_COMMON_EXPORT std::optional<double> SnapshotEvictedRootSurfaceScale();
 VIZ_COMMON_EXPORT bool IsCVDisplayLinkBeginFrameSourceEnabled();
 VIZ_COMMON_EXPORT bool IsVSyncAlignedPresentEnabled();
-VIZ_COMMON_EXPORT int NumPendingFrameSupported();
 VIZ_COMMON_EXPORT bool ShouldLogFrameQuadInfo();
 VIZ_COMMON_EXPORT bool IsUsingFrameIntervalDecider();
 VIZ_COMMON_EXPORT std::optional<uint64_t>
diff --git a/components/viz/common/quads/compositor_frame_metadata.cc b/components/viz/common/quads/compositor_frame_metadata.cc
index ecdeeca..104c8b2 100644
--- a/components/viz/common/quads/compositor_frame_metadata.cc
+++ b/components/viz/common/quads/compositor_frame_metadata.cc
@@ -31,6 +31,7 @@
       content_color_usage(other.content_color_usage),
       may_contain_video(other.may_contain_video),
       is_handling_interaction(other.is_handling_interaction),
+      is_handling_animation(other.is_handling_animation),
       root_background_color(other.root_background_color),
       latency_info(other.latency_info),
       referenced_surfaces(other.referenced_surfaces),
diff --git a/components/viz/common/quads/compositor_frame_metadata.h b/components/viz/common/quads/compositor_frame_metadata.h
index 306d16f..09e9c03d 100644
--- a/components/viz/common/quads/compositor_frame_metadata.h
+++ b/components/viz/common/quads/compositor_frame_metadata.h
@@ -112,6 +112,9 @@
   // gesture scroll events.
   bool is_handling_interaction = false;
 
+  // True if this compositor frame contains animations.
+  bool is_handling_animation = false;
+
   // This color is usually obtained from the background color of the <body>
   // element. It can be used for filling in gutter areas around the frame when
   // it's too small to fill the box the parent reserved for it.
diff --git a/components/viz/common/quads/compositor_frame_metadata_unittest.cc b/components/viz/common/quads/compositor_frame_metadata_unittest.cc
index 90609c62d..c0cddaa 100644
--- a/components/viz/common/quads/compositor_frame_metadata_unittest.cc
+++ b/components/viz/common/quads/compositor_frame_metadata_unittest.cc
@@ -62,6 +62,7 @@
   metadata.content_color_usage = gfx::ContentColorUsage::kHDR;
   metadata.may_contain_video = true;
   metadata.is_handling_interaction = true;
+  metadata.is_handling_animation = true;
   metadata.root_background_color = SkColors::kBlue;
   metadata.latency_info.emplace_back();
   metadata.referenced_surfaces.emplace_back(
@@ -94,6 +95,7 @@
   EXPECT_EQ(clone.content_color_usage, metadata.content_color_usage);
   EXPECT_EQ(clone.may_contain_video, metadata.may_contain_video);
   EXPECT_EQ(clone.is_handling_interaction, metadata.is_handling_interaction);
+  EXPECT_EQ(clone.is_handling_animation, metadata.is_handling_animation);
   EXPECT_EQ(clone.root_background_color, metadata.root_background_color);
 
   EXPECT_EQ(clone.latency_info.size(), metadata.latency_info.size());
diff --git a/components/viz/common/resources/transferable_resource.cc b/components/viz/common/resources/transferable_resource.cc
index ba098e9..7eb9b09 100644
--- a/components/viz/common/resources/transferable_resource.cc
+++ b/components/viz/common/resources/transferable_resource.cc
@@ -9,23 +9,6 @@
 namespace viz {
 
 // static
-TransferableResource TransferableResource::MakeSoftwareSharedBitmap(
-    const SharedBitmapId& id,
-    const gpu::SyncToken& sync_token,
-    const gfx::Size& size,
-    SharedImageFormat format,
-    ResourceSource source) {
-  TransferableResource r;
-  r.is_software = true;
-  r.memory_buffer_id_ = id;
-  r.sync_token_ = sync_token;
-  r.size = size;
-  r.format = format;
-  r.resource_source = source;
-  return r;
-}
-
-// static
 TransferableResource TransferableResource::MakeSoftwareSharedImage(
     const scoped_refptr<gpu::ClientSharedImage>& client_shared_image,
     const gpu::SyncToken& sync_token,
@@ -127,9 +110,4 @@
   return out;
 }
 
-bool TransferableResource::IsSoftwareSharedImage() const {
-  CHECK(is_software);
-  return absl::holds_alternative<gpu::Mailbox>(memory_buffer_id_);
-}
-
 }  // namespace viz
diff --git a/components/viz/common/resources/transferable_resource.h b/components/viz/common/resources/transferable_resource.h
index e738d2b2..83527e5 100644
--- a/components/viz/common/resources/transferable_resource.h
+++ b/components/viz/common/resources/transferable_resource.h
@@ -30,8 +30,6 @@
 
 namespace viz {
 
-using MemoryBufferId = absl::variant<gpu::Mailbox, SharedBitmapId>;
-
 struct ReturnedResource;
 
 struct VIZ_COMMON_EXPORT TransferableResource {
@@ -95,12 +93,6 @@
       const MetadataOverride& override = {});
 
   // Following Make* functions are deprecated. Please use the one above.
-  static TransferableResource MakeSoftwareSharedBitmap(
-      const SharedBitmapId& id,
-      const gpu::SyncToken& sync_token,
-      const gfx::Size& size,
-      SharedImageFormat format,
-      ResourceSource source = ResourceSource::kUnknown);
   static TransferableResource MakeSoftwareSharedImage(
       const scoped_refptr<gpu::ClientSharedImage>& client_shared_image,
       const gpu::SyncToken& sync_token,
@@ -133,16 +125,7 @@
   ReturnedResource ToReturnedResource() const;
   static std::vector<ReturnedResource> ReturnResources(
       const std::vector<TransferableResource>& input);
-  bool is_empty() const {
-    return (absl::holds_alternative<gpu::Mailbox>(memory_buffer_id_) &&
-            mailbox().IsZero()) ||
-           (absl::holds_alternative<SharedBitmapId>(memory_buffer_id_) &&
-            shared_bitmap_id().IsZero());
-  }
-
-  // Returns true if this resource (which must be software) is holding a
-  // SharedImage ID rather than a SharedBitmapId.
-  bool IsSoftwareSharedImage() const;
+  bool is_empty() const { return mailbox().IsZero(); }
 
   // TODO(danakj): Some of these fields are only GL, some are only Software,
   // some are both but used for different purposes (like the mailbox name).
@@ -167,9 +150,6 @@
 
   void set_mailbox(const gpu::Mailbox& mailbox) { memory_buffer_id_ = mailbox; }
 
-  void set_shared_bitmap_id(const SharedBitmapId& shared_bitmap_id) {
-    memory_buffer_id_ = shared_bitmap_id;
-  }
   void set_sync_token(const gpu::SyncToken& sync_token) {
     sync_token_ = sync_token;
   }
@@ -179,14 +159,8 @@
 
   // Returns the Mailbox that this instance is storing. Valid to call only if
   // this instance has been created via MakeSoftwareSharedImage() or MakeGpu().
-  const gpu::Mailbox& mailbox() const {
-    return absl::get<gpu::Mailbox>(memory_buffer_id_);
-  }
-  // Returns the SharedBitmapId that this instance is storing. Valid to call
-  // only if this instance has been created via MakeSoftwareSharedBitmap().
-  const SharedBitmapId& shared_bitmap_id() const {
-    return absl::get<SharedBitmapId>(memory_buffer_id_);
-  }
+  const gpu::Mailbox& mailbox() const { return memory_buffer_id_; }
+
   const gpu::SyncToken& sync_token() const { return sync_token_; }
   gpu::SyncToken& mutable_sync_token() { return sync_token_; }
   uint32_t texture_target() const { return texture_target_; }
@@ -266,13 +240,13 @@
   bool operator!=(const TransferableResource& o) const { return !(*this == o); }
 
   // For usage only in Mojo serialization/deserialization.
-  const MemoryBufferId& memory_buffer_id() const { return memory_buffer_id_; }
-  void set_memory_buffer_id(MemoryBufferId memory_buffer_id) {
+  const gpu::Mailbox& memory_buffer_id() const { return memory_buffer_id_; }
+  void set_memory_buffer_id(gpu::Mailbox memory_buffer_id) {
     memory_buffer_id_ = memory_buffer_id;
   }
 
  private:
-  MemoryBufferId memory_buffer_id_;
+  gpu::Mailbox memory_buffer_id_;
 
   // TODO(crbug.com/337538024): Remove once DUMP_WILL_BE_CHECK() in
   // TransferableResource::mailbox() has safely rolled out.
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index 556c719..f43d8a8 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -123,7 +123,6 @@
     "display/resolved_frame_data.h",
     "display/resource_fence.cc",
     "display/resource_fence.h",
-    "display/shared_bitmap_manager.h",
     "display/skia_output_surface.cc",
     "display/skia_output_surface.h",
     "display/skia_renderer.cc",
@@ -148,8 +147,6 @@
     "display_embedder/output_surface_provider.h",
     "display_embedder/output_surface_provider_impl.cc",
     "display_embedder/output_surface_provider_impl.h",
-    "display_embedder/server_shared_bitmap_manager.cc",
-    "display_embedder/server_shared_bitmap_manager.h",
     "display_embedder/skia_output_device.cc",
     "display_embedder/skia_output_device.h",
     "display_embedder/skia_output_device_buffer_queue.cc",
@@ -598,7 +595,6 @@
     "display/viz_pixel_test.cc",
     "display/viz_pixel_test.h",
     "display_embedder/buffer_queue_unittest.cc",
-    "display_embedder/server_shared_bitmap_manager_unittest.cc",
     "display_embedder/skia_output_device_buffer_queue_unittest.cc",
     "display_embedder/skia_output_surface_impl_unittest.cc",
     "display_embedder/software_output_surface_unittest.cc",
diff --git a/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc b/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
index f9e832e1..c0bb6af 100644
--- a/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
+++ b/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.cc
@@ -36,8 +36,7 @@
     : root_local_surface_id_(1, 1, base::UnguessableToken::Create()),
       output_surface_provider_(std::move(png_dir_path)),
       frame_sink_manager_(
-          FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_,
-                                           &output_surface_provider_)) {
+          FrameSinkManagerImpl::InitParams(&output_surface_provider_)) {
   frame_sink_manager_.RegisterFrameSinkId(kEmbeddedFrameSinkId,
                                           /*report_activation=*/false);
   frame_sink_manager_.RegisterFrameSinkId(kRootFrameSinkId,
diff --git a/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.h b/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.h
index 80927d7..bb45d64 100644
--- a/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.h
+++ b/components/viz/service/compositor_frame_fuzzer/fuzzer_browser_process.h
@@ -12,7 +12,6 @@
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/service/compositor_frame_fuzzer/compositor_frame_fuzzer_util.h"
 #include "components/viz/service/compositor_frame_fuzzer/fuzzer_software_output_surface_provider.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/test/fake_compositor_frame_sink_client.h"
 #include "components/viz/test/fake_display_client.h"
@@ -52,7 +51,6 @@
 
   const LocalSurfaceId root_local_surface_id_;
 
-  ServerSharedBitmapManager shared_bitmap_manager_;
   FuzzerSoftwareOutputSurfaceProvider output_surface_provider_;
   FrameSinkManagerImpl frame_sink_manager_;
 
diff --git a/components/viz/service/display/direct_renderer.h b/components/viz/service/display/direct_renderer.h
index 1995936..472906e 100644
--- a/components/viz/service/display/direct_renderer.h
+++ b/components/viz/service/display/direct_renderer.h
@@ -120,6 +120,9 @@
     // integration testing.
     gfx::CALayerResult ca_layer_error_code = gfx::kCALayerSuccess;
 #endif
+
+    bool is_handling_interaction_or_animation = false;
+
     std::optional<int64_t> choreographer_vsync_id;
     int64_t swap_trace_id = -1;
   };
diff --git a/components/viz/service/display/display.cc b/components/viz/service/display/display.cc
index 4efa0584..80fcf23 100644
--- a/components/viz/service/display/display.cc
+++ b/components/viz/service/display/display.cc
@@ -1107,6 +1107,7 @@
         std::move(animation_thread_ids), std::move(renderer_main_thread_ids),
         boost_type);
 
+    bool has_interactive_or_animated_frame = false;
     for (const auto& surface_id : aggregator_->previous_contained_surfaces()) {
       surface = surface_manager_->GetSurfaceForId(surface_id);
       if (surface) {
@@ -1115,6 +1116,11 @@
         if (helper) {
           presentation_group_timing.AddPresentationHelper(std::move(helper));
         }
+
+        has_interactive_or_animated_frame |=
+            surface->HasActiveFrame() &&
+            (surface->GetActiveFrameMetadata().is_handling_interaction ||
+             surface->GetActiveFrameMetadata().is_handling_animation);
       }
     }
 
@@ -1150,11 +1156,14 @@
     swap_frame_data.ca_layer_error_code =
         overlay_processor_->GetCALayerErrorCode();
 #endif
+    swap_frame_data.is_handling_interaction_or_animation =
+        has_interactive_or_animated_frame;
 
     // We must notify scheduler and increase |pending_swaps_| before calling
     // SwapBuffers() as it can call DidReceiveSwapBuffersAck synchronously.
-    if (scheduler_)
+    if (scheduler_) {
       scheduler_->DidSwapBuffers();
+    }
     pending_swaps_++;
 
     UMA_HISTOGRAM_COUNTS_100("Compositing.Display.PendingSwaps",
diff --git a/components/viz/service/display/display_damage_tracker_unittest.cc b/components/viz/service/display/display_damage_tracker_unittest.cc
index eaeb166..cbfed845 100644
--- a/components/viz/service/display/display_damage_tracker_unittest.cc
+++ b/components/viz/service/display/display_damage_tracker_unittest.cc
@@ -13,7 +13,6 @@
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/service/display/display_resource_provider_software.h"
 #include "components/viz/service/display/surface_aggregator.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/test/compositor_frame_helpers.h"
@@ -33,7 +32,7 @@
 class DisplayDamageTrackerTest : public testing::Test {
  public:
   DisplayDamageTrackerTest()
-      : manager_(FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_)),
+      : manager_(FrameSinkManagerImpl::InitParams()),
         resource_provider_(&shared_image_manager_, &gpu_scheduler_),
         aggregator_(manager_.surface_manager(), &resource_provider_, false),
         root_client_(&manager_, kRootFrameSinkId),
@@ -117,7 +116,6 @@
     fake_begin_frame_source_.TestOnBeginFrame(last_begin_frame_args_);
   }
 
-  ServerSharedBitmapManager shared_bitmap_manager_;
   gpu::SharedImageManager shared_image_manager_;
   gpu::SyncPointManager sync_point_manager_;
   gpu::Scheduler gpu_scheduler_{&sync_point_manager_};
diff --git a/components/viz/service/display/display_resource_provider.cc b/components/viz/service/display/display_resource_provider.cc
index 27196bfb..ed19cd98 100644
--- a/components/viz/service/display/display_resource_provider.cc
+++ b/components/viz/service/display/display_resource_provider.cc
@@ -244,12 +244,6 @@
     }
 
     ResourceId local_id = resource_id_generator_.GenerateNextId();
-
-    // If using legacy shared bitmaps, verify that the format is supported.
-    DCHECK(!transferable_resource.is_software ||
-           transferable_resource.IsSoftwareSharedImage() ||
-           (!transferable_resource.IsSoftwareSharedImage() &&
-            transferable_resource.format.IsBitmapFormatSupported()));
     resources_.emplace(local_id,
                        ChildResource(child_id, transferable_resource));
     child_info.child_to_parent_map[transferable_resource.id] = local_id;
diff --git a/components/viz/service/display/display_resource_provider_software.cc b/components/viz/service/display/display_resource_provider_software.cc
index 58fcc6e..dd89a31b 100644
--- a/components/viz/service/display/display_resource_provider_software.cc
+++ b/components/viz/service/display/display_resource_provider_software.cc
@@ -50,7 +50,6 @@
 
   // Determine whether this resource is using a software SharedImage or a legacy
   // shared bitmap.
-  DCHECK(resource->transferable.IsSoftwareSharedImage());
   DCHECK(shared_image_manager_);
   auto it = resource_shared_images_.find(id);
   if (it == resource_shared_images_.end()) {
diff --git a/components/viz/service/display/display_resource_provider_software.h b/components/viz/service/display/display_resource_provider_software.h
index ef84dff..e954260 100644
--- a/components/viz/service/display/display_resource_provider_software.h
+++ b/components/viz/service/display/display_resource_provider_software.h
@@ -24,7 +24,6 @@
 
 namespace viz {
 
-
 // DisplayResourceProvider implementation used with SoftwareRenderer.
 class VIZ_SERVICE_EXPORT DisplayResourceProviderSoftware
     : public DisplayResourceProvider {
diff --git a/components/viz/service/display/display_unittest.cc b/components/viz/service/display/display_unittest.cc
index 295e8ba..3fc965c 100644
--- a/components/viz/service/display/display_unittest.cc
+++ b/components/viz/service/display/display_unittest.cc
@@ -55,7 +55,6 @@
 #include "components/viz/service/display/display_client.h"
 #include "components/viz/service/display/display_scheduler.h"
 #include "components/viz/service/display/overlay_processor_stub.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/surfaces/surface.h"
@@ -169,7 +168,7 @@
 class DisplayTest : public testing::Test {
  public:
   DisplayTest()
-      : manager_(FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_)),
+      : manager_(FrameSinkManagerImpl::InitParams()),
         support_(
             std::make_unique<CompositorFrameSinkSupport>(nullptr,
                                                          &manager_,
@@ -277,7 +276,6 @@
   }
 
   DebugRendererSettings debug_settings_;
-  ServerSharedBitmapManager shared_bitmap_manager_;
   gpu::SharedImageManager shared_image_manager_;
   gpu::SyncPointManager sync_point_manager_;
   gpu::Scheduler gpu_scheduler_{&sync_point_manager_};
diff --git a/components/viz/service/display/renderer_perftest.cc b/components/viz/service/display/renderer_perftest.cc
index ad4a350..dd1d166 100644
--- a/components/viz/service/display/renderer_perftest.cc
+++ b/components/viz/service/display/renderer_perftest.cc
@@ -39,7 +39,6 @@
 #include "components/viz/service/display/overlay_processor_stub.h"
 #include "components/viz/service/display/skia_renderer.h"
 #include "components/viz/service/display/viz_perftest.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/display_embedder/skia_output_surface_dependency_impl.h"
 #include "components/viz/service/display_embedder/skia_output_surface_impl.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
@@ -233,7 +232,7 @@
 class RendererPerfTest : public VizPerfTest {
  public:
   RendererPerfTest()
-      : manager_(FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_)),
+      : manager_(FrameSinkManagerImpl::InitParams()),
         support_(
             std::make_unique<CompositorFrameSinkSupport>(nullptr,
                                                          &manager_,
@@ -605,7 +604,6 @@
   WaitForSwapDisplayClient client_;
   ParentLocalSurfaceIdAllocator id_allocator_;
   std::unique_ptr<BeginFrameSource> begin_frame_source_;
-  ServerSharedBitmapManager shared_bitmap_manager_;
   FrameSinkManagerImpl manager_;
   std::unique_ptr<CompositorFrameSinkSupport> support_;
   RendererSettings renderer_settings_;
diff --git a/components/viz/service/display/resolved_frame_data_unittest.cc b/components/viz/service/display/resolved_frame_data_unittest.cc
index cabccef..a95faf6fb 100644
--- a/components/viz/service/display/resolved_frame_data_unittest.cc
+++ b/components/viz/service/display/resolved_frame_data_unittest.cc
@@ -11,7 +11,6 @@
 #include "components/viz/common/quads/offset_tag.h"
 #include "components/viz/common/quads/texture_draw_quad.h"
 #include "components/viz/service/display/display_resource_provider_software.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/surfaces/surface.h"
@@ -117,15 +116,13 @@
     return surface;
   }
 
-  ServerSharedBitmapManager shared_bitmap_manager_;
   gpu::SharedImageManager shared_image_manager_;
   gpu::SyncPointManager sync_point_manager_;
   gpu::Scheduler gpu_scheduler_{&sync_point_manager_};
 
   DisplayResourceProviderSoftware resource_provider_{&shared_image_manager_,
                                                      &gpu_scheduler_};
-  FrameSinkManagerImpl frame_sink_manager_{
-      FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_)};
+  FrameSinkManagerImpl frame_sink_manager_{FrameSinkManagerImpl::InitParams()};
 
   TestSurfaceIdAllocator surface_id_{FrameSinkId(1, 1)};
   std::unique_ptr<CompositorFrameSinkSupport> support_;
diff --git a/components/viz/service/display/shared_bitmap_manager.h b/components/viz/service/display/shared_bitmap_manager.h
deleted file mode 100644
index f69435e..0000000
--- a/components/viz/service/display/shared_bitmap_manager.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2013 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_VIZ_SERVICE_DISPLAY_SHARED_BITMAP_MANAGER_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_SHARED_BITMAP_MANAGER_H_
-
-#include <memory>
-
-#include "base/memory/shared_memory_mapping.h"
-#include "components/viz/common/resources/shared_bitmap.h"
-#include "components/viz/common/resources/shared_image_format.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-
-namespace gfx {
-class Size;
-}
-
-namespace viz {
-
-class SharedBitmapManager {
- public:
-  SharedBitmapManager() = default;
-
-  SharedBitmapManager(const SharedBitmapManager&) = delete;
-  SharedBitmapManager& operator=(const SharedBitmapManager&) = delete;
-
-  virtual ~SharedBitmapManager() = default;
-
-  // Used in the display compositor to find the bitmap associated with an id.
-  virtual std::unique_ptr<SharedBitmap> GetSharedBitmapFromId(
-      const gfx::Size& size,
-      SharedImageFormat format,
-      const SharedBitmapId& id) = 0;
-  virtual base::UnguessableToken GetSharedBitmapTracingGUIDFromId(
-      const SharedBitmapId& id) = 0;
-  // Used for locally allocated bitmaps that are not in shared memory.
-  virtual bool LocalAllocatedSharedBitmap(SkBitmap bitmap,
-                                          const SharedBitmapId& id) = 0;
-  // Used in the display compositor to associate an id to a shm mapping.
-  virtual bool ChildAllocatedSharedBitmap(
-      base::ReadOnlySharedMemoryMapping mapping,
-      const SharedBitmapId& id) = 0;
-  // Used in the display compositor to break an association of an id to a shm
-  // handle.
-  virtual void ChildDeletedSharedBitmap(const SharedBitmapId& id) = 0;
-};
-
-}  // namespace viz
-
-#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_SHARED_BITMAP_MANAGER_H_
diff --git a/components/viz/service/display/skia_renderer.cc b/components/viz/service/display/skia_renderer.cc
index 2f69c38..40b3dd3 100644
--- a/components/viz/service/display/skia_renderer.cc
+++ b/components/viz/service/display/skia_renderer.cc
@@ -1181,6 +1181,11 @@
   output_frame.data.ca_layer_error_code = swap_frame_data.ca_layer_error_code;
 #endif
 
+#if BUILDFLAG(IS_MAC)
+  output_frame.data.is_handling_interaction_or_animation =
+      swap_frame_data.is_handling_interaction_or_animation;
+#endif
+
   if (buffer_queue_) {
     gfx::Rect damage_rect = output_frame.sub_buffer_rect.value_or(
         gfx::Rect(surface_size_for_swap_buffers()));
diff --git a/components/viz/service/display/surface_aggregator_perftest.cc b/components/viz/service/display/surface_aggregator_perftest.cc
index a3918e1..26511be5b 100644
--- a/components/viz/service/display/surface_aggregator_perftest.cc
+++ b/components/viz/service/display/surface_aggregator_perftest.cc
@@ -27,7 +27,6 @@
 #include "components/viz/service/display/aggregated_frame.h"
 #include "components/viz/service/display/display_resource_provider_software.h"
 #include "components/viz/service/display/viz_perftest.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/surfaces/surface_manager.h"
@@ -80,11 +79,9 @@
 
 class SurfaceAggregatorPerfTest : public VizPerfTest {
  public:
-  SurfaceAggregatorPerfTest()
-      : manager_(FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_)) {
+  SurfaceAggregatorPerfTest() : manager_(FrameSinkManagerImpl::InitParams()) {
     resource_provider_ = std::make_unique<DisplayResourceProviderSoftware>(
-        /*shared_image_manager=*/nullptr,
-        /*gpu_scheduler=*/nullptr);
+        /*shared_image_manager=*/nullptr, /*gpu_scheduler=*/nullptr);
   }
 
   void RunTest(int num_surfaces,
@@ -522,7 +519,6 @@
     std::map<ResourceId, TransferableResource> created_resources;
   };
 
-  ServerSharedBitmapManager shared_bitmap_manager_;
   FrameSinkManagerImpl manager_;
   std::unique_ptr<DisplayResourceProvider> resource_provider_;
   std::unique_ptr<SurfaceAggregator> aggregator_;
diff --git a/components/viz/service/display/surface_aggregator_pixeltest.cc b/components/viz/service/display/surface_aggregator_pixeltest.cc
index 6e115fe..7f968de 100644
--- a/components/viz/service/display/surface_aggregator_pixeltest.cc
+++ b/components/viz/service/display/surface_aggregator_pixeltest.cc
@@ -15,7 +15,6 @@
 #include "components/viz/service/display/delegated_ink_point_pixel_test_helper.h"
 #include "components/viz/service/display/surface_aggregator.h"
 #include "components/viz/service/display/viz_pixel_test.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/surfaces/surface.h"
@@ -38,7 +37,7 @@
 class SurfaceAggregatorPixelTest : public VizPixelTestWithParam {
  public:
   SurfaceAggregatorPixelTest()
-      : manager_(FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_)),
+      : manager_(FrameSinkManagerImpl::InitParams()),
         support_(std::make_unique<CompositorFrameSinkSupport>(
             nullptr,
             &manager_,
@@ -53,7 +52,6 @@
   }
 
  protected:
-  ServerSharedBitmapManager shared_bitmap_manager_;
   FrameSinkManagerImpl manager_;
   ParentLocalSurfaceIdAllocator root_allocator_;
   std::unique_ptr<CompositorFrameSinkSupport> support_;
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index deb975d..f03efd31 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -41,7 +41,6 @@
 #include "components/viz/common/surfaces/subtree_capture_id.h"
 #include "components/viz/service/display/aggregated_frame.h"
 #include "components/viz/service/display/display_resource_provider_software.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/surfaces/pending_copy_output_request.h"
@@ -480,13 +479,11 @@
     return shared_image_interface_provider_.GetSharedImageInterface();
   }
 
-  ServerSharedBitmapManager shared_bitmap_manager_;
   gpu::SharedImageManager shared_image_manager_;
   gpu::SyncPointManager sync_point_manager_;
   gpu::Scheduler gpu_scheduler_{&sync_point_manager_};
 
-  FrameSinkManagerImpl manager_{
-      FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_)};
+  FrameSinkManagerImpl manager_{FrameSinkManagerImpl::InitParams()};
   DisplayResourceProviderSoftware resource_provider_{&shared_image_manager_,
                                                      &gpu_scheduler_};
   FakeSurfaceObserver observer_{manager_.surface_manager(), false};
diff --git a/components/viz/service/display_embedder/DEPS b/components/viz/service/display_embedder/DEPS
index c950c21..25b0ca6 100644
--- a/components/viz/service/display_embedder/DEPS
+++ b/components/viz/service/display_embedder/DEPS
@@ -16,7 +16,6 @@
   "+components/viz/service/display/render_pass_alpha_type.h",
   "+components/viz/service/display/renderer_utils.h",
   "+components/viz/service/display/resource_metadata.h",
-  "+components/viz/service/display/shared_bitmap_manager.h",
   "+components/viz/service/display/skia_output_surface.h",
   "+components/viz/service/display/software_output_device.h",
   "+components/viz/service/gl/gpu_service_impl.h",
diff --git a/components/viz/service/display_embedder/image_context_impl.cc b/components/viz/service/display_embedder/image_context_impl.cc
index 26789296..8b5e36631 100644
--- a/components/viz/service/display_embedder/image_context_impl.cc
+++ b/components/viz/service/display_embedder/image_context_impl.cc
@@ -9,6 +9,7 @@
 #include "base/check.h"
 #include "base/check_op.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/trace_event/trace_event.h"
 #include "components/viz/common/resources/shared_image_format_utils.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
diff --git a/components/viz/service/display_embedder/output_surface_provider_impl.cc b/components/viz/service/display_embedder/output_surface_provider_impl.cc
index 9186701..07502f4 100644
--- a/components/viz/service/display_embedder/output_surface_provider_impl.cc
+++ b/components/viz/service/display_embedder/output_surface_provider_impl.cc
@@ -21,7 +21,6 @@
 #include "components/viz/common/features.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
 #include "components/viz/service/display/display_compositor_memory_and_task_controller.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/display_embedder/skia_output_surface_dependency_impl.h"
 #include "components/viz/service/display_embedder/skia_output_surface_impl.h"
 #include "components/viz/service/display_embedder/software_output_surface.h"
diff --git a/components/viz/service/display_embedder/server_shared_bitmap_manager.cc b/components/viz/service/display_embedder/server_shared_bitmap_manager.cc
deleted file mode 100644
index e66c6d5..0000000
--- a/components/viz/service/display_embedder/server_shared_bitmap_manager.cc
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2013 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/viz/service/display_embedder/server_shared_bitmap_manager.h"
-
-#include <stdint.h>
-
-#include <string>
-#include <utility>
-
-#include "base/containers/contains.h"
-#include "base/lazy_instance.h"
-#include "base/memory/read_only_shared_memory_region.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/shared_memory_mapping.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
-#include "base/trace_event/process_memory_dump.h"
-#include "components/viz/common/resources/bitmap_allocation.h"
-#include "components/viz/common/resources/resource_sizes.h"
-#include "mojo/public/cpp/system/platform_handle.h"
-#include "ui/gfx/geometry/size.h"
-
-namespace viz {
-
-class BitmapData : public base::RefCounted<BitmapData> {
- public:
-  BitmapData() = default;
-  BitmapData(const BitmapData& other) = delete;
-  BitmapData& operator=(const BitmapData& other) = delete;
-
-  virtual const void* GetMemory() const = 0;
-  virtual size_t GetSize() const = 0;
-  virtual const base::UnguessableToken& GetGUID() const = 0;
-
- protected:
-  friend class base::RefCounted<BitmapData>;
-  virtual ~BitmapData() = default;
-};
-
-// Holds a bitmap stored in local memory.
-class LocalBitmapData : public BitmapData {
- public:
-  explicit LocalBitmapData(SkBitmap bitmap)
-      : bitmap_(std::move(bitmap)), guid_(base::UnguessableToken::Create()) {}
-
-  const void* GetMemory() const override { return bitmap_.getPixels(); }
-  size_t GetSize() const override { return bitmap_.computeByteSize(); }
-  const base::UnguessableToken& GetGUID() const override { return guid_; }
-
- private:
-  ~LocalBitmapData() override = default;
-
-  SkBitmap bitmap_;
-  // GUID to identify this bitmap in memory dumps.
-  base::UnguessableToken guid_;
-};
-
-// Holds a bitmap stored in shared memory.
-class SharedMemoryBitmapData : public BitmapData {
- public:
-  explicit SharedMemoryBitmapData(base::ReadOnlySharedMemoryMapping mapping)
-      : mapping_(std::move(mapping)) {}
-
-  const void* GetMemory() const override { return mapping_.memory(); }
-  size_t GetSize() const override { return mapping_.size(); }
-  const base::UnguessableToken& GetGUID() const override {
-    return mapping_.guid();
-  }
-
- private:
-  ~SharedMemoryBitmapData() override = default;
-
-  base::ReadOnlySharedMemoryMapping mapping_;
-};
-
-namespace {
-
-// Holds a reference on the BitmapData so that the WritableSharedMemoryMapping
-// can outlive the SharedBitmapId registration as long as this SharedBitmap
-// object is held alive.
-class ServerSharedBitmap : public SharedBitmap {
- public:
-  // NOTE: bitmap_data->GetMemory() is read-only but SharedBitmap expects a
-  // uint8_t* pointer, even though all instances returned by a
-  // SharedBitmapManager will be used read-only.
-  explicit ServerSharedBitmap(scoped_refptr<BitmapData> bitmap_data)
-      : SharedBitmap(
-            static_cast<uint8_t*>(const_cast<void*>(bitmap_data->GetMemory()))),
-        bitmap_data_(std::move(bitmap_data)) {}
-
-  ~ServerSharedBitmap() override {
-    // Drop unowned reference before destroying `bitmap_data_`.
-    pixels_ = nullptr;
-  }
-
- private:
-  scoped_refptr<BitmapData> bitmap_data_;
-};
-
-}  // namespace
-
-ServerSharedBitmapManager::ServerSharedBitmapManager() = default;
-
-ServerSharedBitmapManager::~ServerSharedBitmapManager() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(handle_map_.empty());
-}
-
-std::unique_ptr<SharedBitmap> ServerSharedBitmapManager::GetSharedBitmapFromId(
-    const gfx::Size& size,
-    SharedImageFormat format,
-    const SharedBitmapId& id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  auto it = handle_map_.find(id);
-  if (it == handle_map_.end()) {
-    return nullptr;
-  }
-
-  BitmapData* data = it->second.get();
-
-  size_t bitmap_size;
-  if (!ResourceSizes::MaybeSizeInBytes(size, format, &bitmap_size) ||
-      bitmap_size > data->GetSize()) {
-    return nullptr;
-  }
-
-  if (!data->GetMemory()) {
-    return nullptr;
-  }
-
-  return std::make_unique<ServerSharedBitmap>(data);
-}
-
-base::UnguessableToken
-ServerSharedBitmapManager::GetSharedBitmapTracingGUIDFromId(
-    const SharedBitmapId& id) {
-  auto it = handle_map_.find(id);
-  if (it == handle_map_.end())
-    return {};
-  BitmapData* data = it->second.get();
-  return data->GetGUID();
-}
-
-bool ServerSharedBitmapManager::LocalAllocatedSharedBitmap(
-    SkBitmap bitmap,
-    const SharedBitmapId& id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!bitmap.drawsNothing());
-
-  // Duplicate ids are not allowed.
-  if (base::Contains(handle_map_, id))
-    return false;
-
-  handle_map_[id] = base::MakeRefCounted<LocalBitmapData>(std::move(bitmap));
-
-  return true;
-}
-
-bool ServerSharedBitmapManager::ChildAllocatedSharedBitmap(
-    base::ReadOnlySharedMemoryMapping mapping,
-    const SharedBitmapId& id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // Duplicate ids are not allowed.
-  if (base::Contains(handle_map_, id))
-    return false;
-
-  // This function handles public API requests, so verify we unwrapped a shared
-  // memory handle before trying to use the handle.
-  if (!mapping.IsValid())
-    return false;
-
-  handle_map_[id] =
-      base::MakeRefCounted<SharedMemoryBitmapData>(std::move(mapping));
-
-  // Note: |region| will be destroyed at scope exit, releasing the fd.
-  return true;
-}
-
-void ServerSharedBitmapManager::ChildDeletedSharedBitmap(
-    const SharedBitmapId& id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  handle_map_.erase(id);
-}
-
-bool ServerSharedBitmapManager::OnMemoryDump(
-    const base::trace_event::MemoryDumpArgs& args,
-    base::trace_event::ProcessMemoryDump* pmd) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  for (const auto& pair : handle_map_) {
-    const SharedBitmapId& id = pair.first;
-    BitmapData* data = pair.second.get();
-
-    std::string dump_str = base::StringPrintf(
-        "sharedbitmap/%s",
-        base::HexEncode(base::as_byte_span(id.name)).c_str());
-    base::trace_event::MemoryAllocatorDump* dump =
-        pmd->CreateAllocatorDump(dump_str);
-    if (!dump)
-      return false;
-
-    dump->AddScalar(base::trace_event::MemoryAllocatorDump::kNameSize,
-                    base::trace_event::MemoryAllocatorDump::kUnitsBytes,
-                    data->GetSize());
-
-    // This GUID is the same returned by GetSharedBitmapTracingGUIDFromId() so
-    // other components use a consistent GUID for a given SharedBitmapId.
-    base::UnguessableToken bitmap_guid = data->GetGUID();
-    DCHECK(!bitmap_guid.is_empty());
-    pmd->CreateSharedMemoryOwnershipEdge(dump->guid(), bitmap_guid,
-                                         /*importance=*/0);
-  }
-
-  return true;
-}
-
-}  // namespace viz
diff --git a/components/viz/service/display_embedder/server_shared_bitmap_manager.h b/components/viz/service/display_embedder/server_shared_bitmap_manager.h
deleted file mode 100644
index 48b7ad0..0000000
--- a/components/viz/service/display_embedder/server_shared_bitmap_manager.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2013 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_VIZ_SERVICE_DISPLAY_EMBEDDER_SERVER_SHARED_BITMAP_MANAGER_H_
-#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SERVER_SHARED_BITMAP_MANAGER_H_
-
-#include <memory>
-#include <unordered_map>
-
-#include "base/compiler_specific.h"
-#include "base/memory/ref_counted.h"
-#include "base/trace_event/memory_dump_provider.h"
-#include "base/unguessable_token.h"
-#include "components/viz/service/display/shared_bitmap_manager.h"
-#include "components/viz/service/viz_service_export.h"
-
-namespace viz {
-class BitmapData;
-
-// A SharedBitmapManager implementation that lives in-process with the
-// display compositor. It manages mappings from SharedBitmapId to
-// SharedMemory segments. While the returned SharedBitmap is kept alive
-// for a given SharedBitmapId, the backing pixels are guaranteed to remain
-// valid.
-class VIZ_SERVICE_EXPORT ServerSharedBitmapManager
-    : public SharedBitmapManager,
-      public base::trace_event::MemoryDumpProvider {
- public:
-  ServerSharedBitmapManager();
-
-  ServerSharedBitmapManager(const ServerSharedBitmapManager&) = delete;
-  ServerSharedBitmapManager& operator=(const ServerSharedBitmapManager&) =
-      delete;
-
-  ~ServerSharedBitmapManager() override;
-
-  // SharedBitmapManager implementation.
-  std::unique_ptr<SharedBitmap> GetSharedBitmapFromId(
-      const gfx::Size& size,
-      SharedImageFormat format,
-      const SharedBitmapId& id) override;
-  base::UnguessableToken GetSharedBitmapTracingGUIDFromId(
-      const SharedBitmapId& id) override;
-  bool LocalAllocatedSharedBitmap(SkBitmap bitmap,
-                                  const SharedBitmapId& id) override;
-  bool ChildAllocatedSharedBitmap(base::ReadOnlySharedMemoryMapping mapping,
-                                  const SharedBitmapId& id) override;
-  void ChildDeletedSharedBitmap(const SharedBitmapId& id) override;
-
-  // base::trace_event::MemoryDumpProvider implementation.
-  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
-                    base::trace_event::ProcessMemoryDump* pmd) override;
-
- private:
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  std::unordered_map<SharedBitmapId,
-                     scoped_refptr<BitmapData>,
-                     SharedBitmapIdHash>
-      handle_map_;
-};
-
-}  // namespace viz
-
-#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_SERVER_SHARED_BITMAP_MANAGER_H_
diff --git a/components/viz/service/display_embedder/server_shared_bitmap_manager_unittest.cc b/components/viz/service/display_embedder/server_shared_bitmap_manager_unittest.cc
deleted file mode 100644
index 5fa87d0c4..0000000
--- a/components/viz/service/display_embedder/server_shared_bitmap_manager_unittest.cc
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright 2013 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/viz/service/display_embedder/server_shared_bitmap_manager.h"
-
-#include <algorithm>
-#include <utility>
-
-#include "base/containers/span.h"
-#include "components/viz/common/resources/bitmap_allocation.h"
-#include "components/viz/common/resources/resource_sizes.h"
-#include "mojo/public/cpp/system/platform_handle.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/skia/include/core/SkBitmap.h"
-#include "third_party/skia/include/core/SkColor.h"
-#include "third_party/skia/include/core/SkImageInfo.h"
-
-namespace viz {
-namespace {
-
-class ServerSharedBitmapManagerTest : public testing::Test {
- protected:
-  void SetUp() override {
-    manager_ = std::make_unique<ServerSharedBitmapManager>();
-  }
-
-  void TearDown() override { manager_.reset(); }
-
-  ServerSharedBitmapManager* manager() const { return manager_.get(); }
-
- private:
-  std::unique_ptr<ServerSharedBitmapManager> manager_;
-};
-
-TEST_F(ServerSharedBitmapManagerTest, TestCreate) {
-  gfx::Size bitmap_size(1, 1);
-  base::MappedReadOnlyRegion shm = bitmap_allocation::AllocateSharedBitmap(
-      bitmap_size, SinglePlaneFormat::kRGBA_8888);
-  EXPECT_TRUE(shm.IsValid());
-  base::span<uint8_t> span = shm.mapping.GetMemoryAsSpan<uint8_t>();
-  std::fill(span.begin(), span.end(), 0xff);
-
-  SharedBitmapId id = SharedBitmap::GenerateId();
-  manager()->ChildAllocatedSharedBitmap(shm.region.Map(), id);
-
-  std::unique_ptr<SharedBitmap> large_bitmap;
-  large_bitmap = manager()->GetSharedBitmapFromId(
-      gfx::Size(1024, 1024), SinglePlaneFormat::kRGBA_8888, id);
-  EXPECT_FALSE(large_bitmap);
-
-  std::unique_ptr<SharedBitmap> very_large_bitmap;
-  very_large_bitmap = manager()->GetSharedBitmapFromId(
-      gfx::Size(1, (1 << 30) | 1), SinglePlaneFormat::kRGBA_8888, id);
-  EXPECT_FALSE(very_large_bitmap);
-
-  std::unique_ptr<SharedBitmap> negative_size_bitmap;
-  negative_size_bitmap = manager()->GetSharedBitmapFromId(
-      gfx::Size(-1, 1024), SinglePlaneFormat::kRGBA_8888, id);
-  EXPECT_FALSE(negative_size_bitmap);
-
-  SharedBitmapId id2 = SharedBitmap::GenerateId();
-  std::unique_ptr<SharedBitmap> invalid_bitmap;
-  invalid_bitmap = manager()->GetSharedBitmapFromId(
-      bitmap_size, SinglePlaneFormat::kRGBA_8888, id2);
-  EXPECT_FALSE(invalid_bitmap);
-
-  std::unique_ptr<SharedBitmap> shared_bitmap;
-  shared_bitmap = manager()->GetSharedBitmapFromId(
-      bitmap_size, SinglePlaneFormat::kRGBA_8888, id);
-  ASSERT_TRUE(shared_bitmap);
-  EXPECT_TRUE(
-      std::equal(span.begin(), span.begin() + 4, shared_bitmap->pixels()));
-
-  std::unique_ptr<SharedBitmap> large_bitmap2;
-  large_bitmap2 = manager()->GetSharedBitmapFromId(
-      gfx::Size(1024, 1024), SinglePlaneFormat::kRGBA_8888, id);
-  EXPECT_FALSE(large_bitmap2);
-
-  std::unique_ptr<SharedBitmap> shared_bitmap2;
-  shared_bitmap2 = manager()->GetSharedBitmapFromId(
-      bitmap_size, SinglePlaneFormat::kRGBA_8888, id);
-  EXPECT_TRUE(shared_bitmap2->pixels() == shared_bitmap->pixels());
-  shared_bitmap2.reset();
-  EXPECT_TRUE(std::equal(span.begin(), span.end(), shared_bitmap->pixels()));
-
-  manager()->ChildDeletedSharedBitmap(id);
-
-  std::fill(span.begin(), span.end(), 0);
-
-  EXPECT_TRUE(std::equal(span.begin(), span.end(), shared_bitmap->pixels()));
-  shared_bitmap.reset();
-}
-
-TEST_F(ServerSharedBitmapManagerTest, TestLocalCreate) {
-  constexpr gfx::Size bitmap_size(100, 100);
-  SharedBitmapId id = SharedBitmap::GenerateId();
-  void* pixels = nullptr;
-
-  {
-    // Allocate a local bitmap and fill it with red.
-    SkImageInfo info =
-        SkImageInfo::MakeN32Premul(bitmap_size.width(), bitmap_size.height());
-    SkBitmap bitmap;
-    bitmap.allocPixels(info);
-    bitmap.eraseColor(SK_ColorRED);
-
-    pixels = bitmap.getPixels();
-    EXPECT_TRUE(pixels);
-
-    manager()->LocalAllocatedSharedBitmap(std::move(bitmap), id);
-  }
-
-  std::unique_ptr<SharedBitmap> returned_bitmap =
-      manager()->GetSharedBitmapFromId(bitmap_size,
-                                       SinglePlaneFormat::kRGBA_8888, id);
-
-  // Check the shared bitmap returns the address of pixmap allocated earlier.
-  ASSERT_TRUE(returned_bitmap);
-  EXPECT_EQ(pixels, returned_bitmap->pixels());
-
-  manager()->ChildDeletedSharedBitmap(id);
-}
-
-TEST_F(ServerSharedBitmapManagerTest, AddDuplicate) {
-  gfx::Size bitmap_size(1, 1);
-  base::MappedReadOnlyRegion shm = bitmap_allocation::AllocateSharedBitmap(
-      bitmap_size, SinglePlaneFormat::kRGBA_8888);
-  EXPECT_TRUE(shm.IsValid());
-  base::span<uint8_t> span = shm.mapping.GetMemoryAsSpan<uint8_t>();
-  std::fill(span.begin(), span.end(), 0xff);
-  SharedBitmapId id = SharedBitmap::GenerateId();
-
-  // NOTE: Duplicate the mapping to compare its content later.
-  manager()->ChildAllocatedSharedBitmap(shm.region.Map(), id);
-
-  base::MappedReadOnlyRegion shm2 = bitmap_allocation::AllocateSharedBitmap(
-      bitmap_size, SinglePlaneFormat::kRGBA_8888);
-  EXPECT_TRUE(shm2.IsValid());
-  base::span<uint8_t> span2 = shm.mapping.GetMemoryAsSpan<uint8_t>();
-  std::fill(span2.begin(), span2.end(), 0x00);
-
-  manager()->ChildAllocatedSharedBitmap(shm2.region.Map(), id);
-
-  std::unique_ptr<SharedBitmap> shared_bitmap;
-  shared_bitmap = manager()->GetSharedBitmapFromId(
-      bitmap_size, SinglePlaneFormat::kRGBA_8888, id);
-  ASSERT_TRUE(shared_bitmap);
-  EXPECT_TRUE(std::equal(span.begin(), span.end(), shared_bitmap->pixels()));
-  manager()->ChildDeletedSharedBitmap(id);
-}
-
-TEST_F(ServerSharedBitmapManagerTest, SharedMemoryHandle) {
-  gfx::Size bitmap_size(1, 1);
-  base::MappedReadOnlyRegion shm = bitmap_allocation::AllocateSharedBitmap(
-      bitmap_size, SinglePlaneFormat::kRGBA_8888);
-  EXPECT_TRUE(shm.IsValid());
-  base::span<uint8_t> span = shm.mapping.GetMemoryAsSpan<uint8_t>();
-  std::fill(span.begin(), span.end(), 0xff);
-  base::UnguessableToken shared_memory_guid = shm.mapping.guid();
-  EXPECT_FALSE(shared_memory_guid.is_empty());
-
-  SharedBitmapId id = SharedBitmap::GenerateId();
-  manager()->ChildAllocatedSharedBitmap(shm.region.Map(), id);
-
-  base::UnguessableToken tracing_guid =
-      manager()->GetSharedBitmapTracingGUIDFromId(id);
-  EXPECT_EQ(tracing_guid, shared_memory_guid);
-
-  manager()->ChildDeletedSharedBitmap(id);
-}
-
-TEST_F(ServerSharedBitmapManagerTest, InvalidScopedSharedBufferHandle) {
-  SharedBitmapId id = SharedBitmap::GenerateId();
-  base::ReadOnlySharedMemoryMapping invalid_mapping;
-  EXPECT_FALSE(invalid_mapping.IsValid());
-  EXPECT_FALSE(
-      manager()->ChildAllocatedSharedBitmap(std::move(invalid_mapping), id));
-
-  // The client could still send an IPC to say it deleted the shared bitmap,
-  // even though it wasn't valid, which should be ignored.
-  manager()->ChildDeletedSharedBitmap(id);
-}
-
-}  // namespace
-}  // namespace viz
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
index 30a207b..e820b27 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.cc
@@ -18,6 +18,7 @@
 #include "base/system/sys_info.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/time/time.h"
+#include "base/trace_event/trace_id_helper.h"
 #include "base/trace_event/typed_macros.h"
 #include "build/build_config.h"
 #include "cc/base/features.h"
@@ -33,7 +34,6 @@
 #include "components/viz/common/surfaces/video_capture_target.h"
 #include "components/viz/common/viz_utils.h"
 #include "components/viz/service/display/display.h"
-#include "components/viz/service/display/shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_counter.h"
 #include "components/viz/service/frame_sinks/frame_sink_bundle_impl.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
@@ -1168,7 +1168,7 @@
 }
 
 void CompositorFrameSinkSupport::OnBeginFrame(const BeginFrameArgs& args) {
-  int64_t trace_id = ComputeTraceId();
+  int64_t trace_id = base::trace_event::GetNextGlobalTraceId();
   TRACE_EVENT(
       "viz,benchmark,graphics.pipeline", "Graphics.Pipeline",
       perfetto::Flow::Global(trace_id), [trace_id](perfetto::EventContext ctx) {
@@ -1509,15 +1509,6 @@
   NOTREACHED();
 }
 
-int64_t CompositorFrameSinkSupport::ComputeTraceId() {
-  // This is losing some info, but should normally be sufficient to avoid
-  // collisions.
-  ++trace_sequence_;
-  uint64_t client = (frame_sink_id_.client_id() & 0xffff);
-  uint64_t sink = (frame_sink_id_.sink_id() & 0xffff);
-  return (client << 48) | (sink << 32) | trace_sequence_;
-}
-
 bool CompositorFrameSinkSupport::ShouldSendBeginFrame(
     base::TimeTicks frame_time,
     base::TimeDelta vsync_interval) {
@@ -1666,7 +1657,7 @@
 
       view_transition_token_to_animation_manager_[transition_token] =
           SurfaceAnimationManager::CreateWithSave(
-              directive, surface, frame_sink_manager_->shared_bitmap_manager(),
+              directive, surface,
               frame_sink_manager_->GetSharedImageInterface(),
               frame_sink_manager_->reserved_resource_id_tracker(),
               base::BindOnce(&CompositorFrameSinkSupport::
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support.h b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
index 4bc5e62..a852c540 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support.h
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support.h
@@ -316,8 +316,6 @@
   // BeginFrame and FrameAck are done.
   void HandleCallback();
 
-  int64_t ComputeTraceId();
-
   void MaybeEvictSurfaces();
   void EvictLastActiveSurface();
   bool ShouldSendBeginFrame(base::TimeTicks timestamp,
diff --git a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
index 54d0f69..e77db9d 100644
--- a/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
+++ b/components/viz/service/frame_sinks/compositor_frame_sink_support_unittest.cc
@@ -34,7 +34,6 @@
 #include "components/viz/common/surfaces/subtree_capture_id.h"
 #include "components/viz/common/surfaces/surface_id.h"
 #include "components/viz/common/surfaces/surface_info.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/surfaces/surface.h"
 #include "components/viz/test/begin_frame_args_test.h"
@@ -155,7 +154,7 @@
   // testing::Test
   void SetUp() override {
     manager_ = std::make_unique<FrameSinkManagerImpl>(
-        FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_));
+        FrameSinkManagerImpl::InitParams());
     surface_observer_ =
         std::make_unique<FakeSurfaceObserver>(manager_->surface_manager());
     manager_->SetLocalClient(&frame_sink_manager_client_);
@@ -316,7 +315,6 @@
  protected:
   TestSharedImageInterfaceProvider shared_image_interface_provider_;
   std::unique_ptr<base::SimpleTestTickClock> now_src_;
-  ServerSharedBitmapManager shared_bitmap_manager_;
   std::unique_ptr<FrameSinkManagerImpl> manager_;
   testing::NiceMock<MockFrameSinkManagerClient> frame_sink_manager_client_;
   FakeCompositorFrameSinkClient fake_support_client_;
@@ -2199,8 +2197,7 @@
           CompositorFrameTransitionDirective::CreateSave(
               transition_token, maybe_cross_frame_sink,
               /*sequence_id=*/1, {}, {}),
-          surface, &shared_bitmap_manager_, sii, &id_tracker,
-          base::DoNothing());
+          surface, sii, &id_tracker, base::DoNothing());
   ASSERT_TRUE(animation_manager);
 
   EXPECT_FALSE(HasAnimationManagerForToken(transition_token));
diff --git a/components/viz/service/frame_sinks/fling_scheduler_android_unittest.cc b/components/viz/service/frame_sinks/fling_scheduler_android_unittest.cc
index 13a3469..ca4eea3 100644
--- a/components/viz/service/frame_sinks/fling_scheduler_android_unittest.cc
+++ b/components/viz/service/frame_sinks/fling_scheduler_android_unittest.cc
@@ -7,7 +7,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
 #include "components/input/features.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/external_begin_frame_source_android.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/input/input_manager.h"
@@ -52,8 +51,7 @@
  public:
   FlingSchedulerTest()
       : frame_sink_manager_(
-            FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_,
-                                             &output_surface_provider_)) {
+            FrameSinkManagerImpl::InitParams(&output_surface_provider_)) {
     scoped_feature_list_.InitWithFeatures(
         /* enabled_features */ {input::features::kInputOnViz},
         /* disabled_features */ {});
@@ -167,7 +165,6 @@
   }
 
   std::unique_ptr<input::FlingController> fling_controller_;
-  ServerSharedBitmapManager shared_bitmap_manager_;
   TestOutputSurfaceProvider output_surface_provider_;
   FrameSinkManagerImpl frame_sink_manager_;
   base::test::ScopedFeatureList scoped_feature_list_;
diff --git a/components/viz/service/frame_sinks/frame_sink_bundle_impl_unittest.cc b/components/viz/service/frame_sinks/frame_sink_bundle_impl_unittest.cc
index 7e17d24..707e83f90 100644
--- a/components/viz/service/frame_sinks/frame_sink_bundle_impl_unittest.cc
+++ b/components/viz/service/frame_sinks/frame_sink_bundle_impl_unittest.cc
@@ -28,7 +28,6 @@
 #include "components/viz/common/surfaces/frame_sink_bundle_id.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "components/viz/common/surfaces/local_surface_id.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/test/compositor_frame_helpers.h"
 #include "components/viz/test/fake_external_begin_frame_source.h"
@@ -282,11 +281,9 @@
 
   base::SimpleTestTickClock test_clock_;
   DebugRendererSettings debug_settings_;
-  ServerSharedBitmapManager shared_bitmap_manager_;
   TestOutputSurfaceProvider output_surface_provider_;
   FrameSinkManagerImpl manager_{
-      FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_,
-                                       &output_surface_provider_)};
+      FrameSinkManagerImpl::InitParams(&output_surface_provider_)};
   FakeExternalBeginFrameSource begin_frame_source_{0.0f, false};
 
   TestBundleClient test_client_;
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
index 4f4d2308..3a92074 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.cc
@@ -29,7 +29,6 @@
 #include "components/viz/common/surfaces/subtree_capture_id.h"
 #include "components/viz/common/surfaces/video_capture_target.h"
 #include "components/viz/service/display/overdraw_tracker.h"
-#include "components/viz/service/display/shared_bitmap_manager.h"
 #include "components/viz/service/display_embedder/output_surface_provider.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_bundle_impl.h"
@@ -43,13 +42,10 @@
 
 namespace viz {
 
-FrameSinkManagerImpl::InitParams::InitParams() = default;
 FrameSinkManagerImpl::InitParams::InitParams(
-    SharedBitmapManager* shared_bitmap_manager,
     OutputSurfaceProvider* output_surface_provider,
     GmbVideoFramePoolContextProvider* gmb_context_provider)
-    : shared_bitmap_manager(shared_bitmap_manager),
-      output_surface_provider(output_surface_provider),
+    : output_surface_provider(output_surface_provider),
       gmb_context_provider(gmb_context_provider) {}
 FrameSinkManagerImpl::InitParams::InitParams(InitParams&& other) = default;
 FrameSinkManagerImpl::InitParams::~InitParams() = default;
@@ -79,8 +75,7 @@
 operator=(FrameSinkData&& other) = default;
 
 FrameSinkManagerImpl::FrameSinkManagerImpl(const InitParams& params)
-    : shared_bitmap_manager_(params.shared_bitmap_manager),
-      output_surface_provider_(params.output_surface_provider),
+    : output_surface_provider_(params.output_surface_provider),
       gpu_service_(params.gpu_service),
       gmb_context_provider_(params.gmb_context_provider),
       surface_manager_(this,
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_impl.h b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
index d29f933b..078b0e9 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_impl.h
+++ b/components/viz/service/frame_sinks/frame_sink_manager_impl.h
@@ -64,7 +64,6 @@
 class HintSessionFactory;
 class InputManager;
 class OutputSurfaceProvider;
-class SharedBitmapManager;
 class SharedImageInterfaceProvider;
 struct VideoCaptureTarget;
 
@@ -81,16 +80,13 @@
       public HitTestDataProvider {
  public:
   struct VIZ_SERVICE_EXPORT InitParams {
-    InitParams();
     explicit InitParams(
-        SharedBitmapManager* shared_bitmap_manager,
         OutputSurfaceProvider* output_surface_provider = nullptr,
         GmbVideoFramePoolContextProvider* gmb_context_provider = nullptr);
     InitParams(InitParams&& other);
     ~InitParams();
     InitParams& operator=(InitParams&& other);
 
-    raw_ptr<SharedBitmapManager> shared_bitmap_manager = nullptr;
     std::optional<uint32_t> activation_deadline_in_frames =
         kDefaultActivationDeadlineInFrames;
     raw_ptr<OutputSurfaceProvider> output_surface_provider = nullptr;
@@ -268,9 +264,6 @@
 
   SurfaceManager* surface_manager() { return &surface_manager_; }
   const HitTestManager* hit_test_manager() { return &hit_test_manager_; }
-  SharedBitmapManager* shared_bitmap_manager() {
-    return shared_bitmap_manager_;
-  }
 
   virtual InputManager* GetInputManager();  // virtual for testing.
 
@@ -481,10 +474,6 @@
   void MaybeEraseHitTestQuery(const FrameSinkId& frame_sink_id);
   void MaybeAddHitTestQuery(const FrameSinkId& frame_sink_id);
 
-  // SharedBitmapManager for the viz display service for receiving software
-  // resources in CompositorFrameSinks.
-  const raw_ptr<SharedBitmapManager> shared_bitmap_manager_;
-
   // Provides an output surface for CreateRootCompositorFrameSink().
   const raw_ptr<OutputSurfaceProvider> output_surface_provider_;
 
diff --git a/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc b/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
index 06026f14..edf9ed5 100644
--- a/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
+++ b/components/viz/service/frame_sinks/frame_sink_manager_unittest.cc
@@ -15,7 +15,6 @@
 #include "components/viz/common/constants.h"
 #include "components/viz/common/frame_sinks/begin_frame_source.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/input/mock_input_manager.h"
 #include "components/viz/service/input/render_input_router_iterator_impl.h"
@@ -72,8 +71,7 @@
 class FrameSinkManagerTest : public testing::Test {
  public:
   FrameSinkManagerTest()
-      : manager_(FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_,
-                                                  &output_surface_provider_)) {}
+      : manager_(FrameSinkManagerImpl::InitParams(&output_surface_provider_)) {}
   ~FrameSinkManagerTest() override = default;
 
   RootCompositorFrameSinkImpl* GetRootCompositorFrameSinkImpl() {
@@ -180,7 +178,6 @@
 
  protected:
   DebugRendererSettings debug_settings_;
-  ServerSharedBitmapManager shared_bitmap_manager_;
   TestOutputSurfaceProvider output_surface_provider_;
   FrameSinkManagerImpl manager_;
   FakeSurfaceObserver surface_observer_{manager_.surface_manager()};
diff --git a/components/viz/service/frame_sinks/surface_references_unittest.cc b/components/viz/service/frame_sinks/surface_references_unittest.cc
index 6be787b..1c3cbaff6 100644
--- a/components/viz/service/frame_sinks/surface_references_unittest.cc
+++ b/components/viz/service/frame_sinks/surface_references_unittest.cc
@@ -10,7 +10,6 @@
 #include "base/containers/flat_set.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "components/viz/common/surfaces/surface_id.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/surfaces/surface.h"
@@ -125,7 +124,7 @@
   void SetUp() override {
     // Start each test with a fresh SurfaceManager instance.
     manager_ = std::make_unique<FrameSinkManagerImpl>(
-        FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_));
+        FrameSinkManagerImpl::InitParams());
   }
   void TearDown() override {
     supports_.clear();
@@ -136,7 +135,6 @@
   scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
   base::TestMockTimeTaskRunner::ScopedContext scoped_context_;
 
-  ServerSharedBitmapManager shared_bitmap_manager_;
   std::unique_ptr<FrameSinkManagerImpl> manager_;
   std::unordered_map<FrameSinkId,
                      std::unique_ptr<CompositorFrameSinkSupport>,
diff --git a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
index 9bcee58a..ca2ce8f 100644
--- a/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
+++ b/components/viz/service/frame_sinks/surface_synchronization_unittest.cc
@@ -7,7 +7,6 @@
 #include "base/test/simple_test_tick_clock.h"
 #include "components/viz/common/features.h"
 #include "components/viz/common/surfaces/surface_id.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/surfaces/frame_index_constants.h"
@@ -193,7 +192,7 @@
   void SetUp() override {
     testing::Test::SetUp();
     frame_sink_manager_ = std::make_unique<FrameSinkManagerImpl>(
-        FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_));
+        FrameSinkManagerImpl::InitParams());
     surface_observer_ =
         std::make_unique<FakeSurfaceObserver>(surface_manager(), false);
     begin_frame_source_ =
@@ -268,7 +267,6 @@
 
  private:
   std::unique_ptr<base::SimpleTestTickClock> now_src_;
-  ServerSharedBitmapManager shared_bitmap_manager_;
   std::unique_ptr<FrameSinkManagerImpl> frame_sink_manager_;
   std::unique_ptr<FakeSurfaceObserver> surface_observer_;
   std::unique_ptr<FakeExternalBeginFrameSource> begin_frame_source_;
diff --git a/components/viz/service/frame_sinks/video_detector_unittest.cc b/components/viz/service/frame_sinks/video_detector_unittest.cc
index 0c3b77a8..c8dfedc 100644
--- a/components/viz/service/frame_sinks/video_detector_unittest.cc
+++ b/components/viz/service/frame_sinks/video_detector_unittest.cc
@@ -24,7 +24,6 @@
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/service/display/display_resource_provider_software.h"
 #include "components/viz/service/display/surface_aggregator.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/test/compositor_frame_helpers.h"
@@ -243,12 +242,10 @@
   }
 
   base::test::ScopedFeatureList scoped_feature_list_;
-  ServerSharedBitmapManager shared_bitmap_manager_;
   gpu::SharedImageManager shared_image_manager_;
   gpu::SyncPointManager sync_point_manager_;
   gpu::Scheduler gpu_scheduler_{&sync_point_manager_};
-  FrameSinkManagerImpl frame_sink_manager_{
-      FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_)};
+  FrameSinkManagerImpl frame_sink_manager_{FrameSinkManagerImpl::InitParams()};
   DisplayResourceProviderSoftware resource_provider_{&shared_image_manager_,
                                                      &gpu_scheduler_};
   FakeCompositorFrameSinkClient frame_sink_client_;
diff --git a/components/viz/service/hit_test/hit_test_aggregator_unittest.cc b/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
index fa94b079..f1924f61 100644
--- a/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
+++ b/components/viz/service/hit_test/hit_test_aggregator_unittest.cc
@@ -14,7 +14,6 @@
 #include "components/viz/common/surfaces/frame_sink_id.h"
 #include "components/viz/common/surfaces/surface_id.h"
 #include "components/viz/host/host_frame_sink_manager.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/hit_test/hit_test_aggregator_delegate.h"
@@ -57,9 +56,8 @@
 
 class TestFrameSinkManagerImpl : public FrameSinkManagerImpl {
  public:
-  explicit TestFrameSinkManagerImpl(SharedBitmapManager* shared_bitmap_manager)
-      : FrameSinkManagerImpl(
-            FrameSinkManagerImpl::InitParams(shared_bitmap_manager)) {}
+  TestFrameSinkManagerImpl()
+      : FrameSinkManagerImpl(FrameSinkManagerImpl::InitParams()) {}
 
   TestFrameSinkManagerImpl(const TestFrameSinkManagerImpl&) = delete;
   TestFrameSinkManagerImpl& operator=(const TestFrameSinkManagerImpl&) = delete;
@@ -121,8 +119,7 @@
 
   // testing::Test:
   void SetUp() override {
-    frame_sink_manager_ =
-        std::make_unique<TestFrameSinkManagerImpl>(&shared_bitmap_manager_);
+    frame_sink_manager_ = std::make_unique<TestFrameSinkManagerImpl>();
     host_frame_sink_manager_ = std::make_unique<TestHostFrameSinkManager>();
     local_surface_id_lookup_delegate_ =
         std::make_unique<TestLatestLocalSurfaceIdLookupDelegate>();
@@ -230,7 +227,6 @@
   }
 
  private:
-  ServerSharedBitmapManager shared_bitmap_manager_;
   std::unique_ptr<TestHitTestAggregator> hit_test_aggregator_;
   std::unique_ptr<TestFrameSinkManagerImpl> frame_sink_manager_;
   std::unique_ptr<TestHostFrameSinkManager> host_frame_sink_manager_;
diff --git a/components/viz/service/hit_test/hit_test_manager_fuzzer.cc b/components/viz/service/hit_test/hit_test_manager_fuzzer.cc
index 753fa73..7f4426a 100644
--- a/components/viz/service/hit_test/hit_test_manager_fuzzer.cc
+++ b/components/viz/service/hit_test/hit_test_manager_fuzzer.cc
@@ -11,7 +11,6 @@
 #include <vector>
 
 #include "base/command_line.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/hit_test/hit_test_aggregator.h"
@@ -132,9 +131,8 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t num_bytes) {
   FuzzedDataProvider fuzz(data, num_bytes);
-  viz::ServerSharedBitmapManager shared_bitmap_manager;
   viz::FrameSinkManagerImpl frame_sink_manager{
-      viz::FrameSinkManagerImpl::InitParams(&shared_bitmap_manager)};
+      viz::FrameSinkManagerImpl::InitParams()};
   viz::TestLatestLocalSurfaceIdLookupDelegate delegate;
   viz::TestLatestLocalSurfaceIdLookupDelegate* lsi_delegate =
       fuzz.ConsumeBool() ? &delegate : nullptr;
diff --git a/components/viz/service/main/viz_compositor_thread_runner_impl.cc b/components/viz/service/main/viz_compositor_thread_runner_impl.cc
index 4cde07e5..2ff7fe1c 100644
--- a/components/viz/service/main/viz_compositor_thread_runner_impl.cc
+++ b/components/viz/service/main/viz_compositor_thread_runner_impl.cc
@@ -21,7 +21,6 @@
 #include "components/viz/common/switches.h"
 #include "components/viz/service/display_embedder/in_process_gpu_memory_buffer_manager.h"
 #include "components/viz/service/display_embedder/output_surface_provider_impl.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/frame_sinks/gmb_video_frame_pool_context_provider_impl.h"
 #include "components/viz/service/frame_sinks/shared_image_interface_provider.h"
@@ -159,11 +158,6 @@
   DCHECK(!frame_sink_manager_);
   gpu::SchedulerSequence::DefaultDisallowScheduleTaskOnCurrentThread();
 
-  server_shared_bitmap_manager_ = std::make_unique<ServerSharedBitmapManager>();
-  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
-      server_shared_bitmap_manager_.get(), "ServerSharedBitmapManager",
-      task_runner_);
-
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   const bool headless = command_line->HasSwitch(switches::kHeadless);
   const bool run_all_compositor_stages_before_draw =
@@ -192,7 +186,7 @@
 
   // Create FrameSinkManagerImpl.
   FrameSinkManagerImpl::InitParams init_params;
-  init_params.shared_bitmap_manager = server_shared_bitmap_manager_.get();
+
   // Set default activation deadline to infinite if client doesn't provide one.
   init_params.activation_deadline_in_frames = std::nullopt;
   if (params->use_activation_deadline) {
@@ -241,17 +235,11 @@
 
   weak_factory_.InvalidateWeakPtrs();
 
-  if (server_shared_bitmap_manager_) {
-    base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
-        server_shared_bitmap_manager_.get());
-  }
-
   frame_sink_manager_.reset();
   hint_session_factory_.reset();
   output_surface_provider_.reset();
   gmb_video_frame_pool_context_provider_.reset();
   gpu_memory_buffer_manager_.reset();
-  server_shared_bitmap_manager_.reset();
 }
 
 }  // namespace viz
diff --git a/components/viz/service/main/viz_compositor_thread_runner_impl.h b/components/viz/service/main/viz_compositor_thread_runner_impl.h
index 8ae6f4e..3610ef8 100644
--- a/components/viz/service/main/viz_compositor_thread_runner_impl.h
+++ b/components/viz/service/main/viz_compositor_thread_runner_impl.h
@@ -29,7 +29,6 @@
 class HintSessionFactory;
 class InProcessGpuMemoryBufferManager;
 class OutputSurfaceProvider;
-class ServerSharedBitmapManager;
 class SharedImageInterfaceProvider;
 
 #if BUILDFLAG(IS_ANDROID)
@@ -83,7 +82,6 @@
 
   // Start variables to be accessed only on |task_runner_|.
   std::unique_ptr<HintSessionFactory> hint_session_factory_;
-  std::unique_ptr<ServerSharedBitmapManager> server_shared_bitmap_manager_;
   std::unique_ptr<InProcessGpuMemoryBufferManager> gpu_memory_buffer_manager_;
   std::unique_ptr<OutputSurfaceProvider> output_surface_provider_;
   // `gmb_video_frame_pool_context_provider_` depends on
diff --git a/components/viz/service/surfaces/surface_unittest.cc b/components/viz/service/surfaces/surface_unittest.cc
index 5ddfda42..f5bc156d 100644
--- a/components/viz/service/surfaces/surface_unittest.cc
+++ b/components/viz/service/surfaces/surface_unittest.cc
@@ -13,7 +13,6 @@
 #include "components/viz/common/frame_sinks/copy_output_result.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/common/surfaces/subtree_capture_id.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/service/surfaces/pending_copy_output_request.h"
@@ -36,13 +35,10 @@
 
 class SurfaceTest : public testing::Test {
  public:
-  SurfaceTest()
-      : frame_sink_manager_(
-            FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_)) {}
+  SurfaceTest() : frame_sink_manager_(FrameSinkManagerImpl::InitParams()) {}
 
  protected:
   std::unique_ptr<base::SimpleTestTickClock> now_src_;
-  ServerSharedBitmapManager shared_bitmap_manager_;
   FrameSinkManagerImpl frame_sink_manager_;
 };
 
diff --git a/components/viz/service/transitions/DEPS b/components/viz/service/transitions/DEPS
index 6b47897..673c020b 100644
--- a/components/viz/service/transitions/DEPS
+++ b/components/viz/service/transitions/DEPS
@@ -1,7 +1,6 @@
 # Please consult components/viz/README.md about allowable dependencies.
 
 include_rules = [
-  "+components/viz/service/display/shared_bitmap_manager.h",
   "+components/viz/service/frame_sinks",
   "+components/viz/service/surfaces",
   "+gpu/command_buffer/common",
diff --git a/components/viz/service/transitions/surface_animation_manager.cc b/components/viz/service/transitions/surface_animation_manager.cc
index f0903f03c..590206d 100644
--- a/components/viz/service/transitions/surface_animation_manager.cc
+++ b/components/viz/service/transitions/surface_animation_manager.cc
@@ -133,19 +133,17 @@
 SurfaceAnimationManager::CreateWithSave(
     const CompositorFrameTransitionDirective& directive,
     Surface* surface,
-    SharedBitmapManager* shared_bitmap_manager,
     gpu::SharedImageInterface* shared_image_interface,
     ReservedResourceIdTracker* id_tracker,
     SaveDirectiveCompleteCallback sequence_id_finished_callback) {
   return base::WrapUnique(new SurfaceAnimationManager(
-      directive, surface, shared_bitmap_manager, shared_image_interface,
-      id_tracker, std::move(sequence_id_finished_callback)));
+      directive, surface, shared_image_interface, id_tracker,
+      std::move(sequence_id_finished_callback)));
 }
 
 SurfaceAnimationManager::SurfaceAnimationManager(
     const CompositorFrameTransitionDirective& directive,
     Surface* surface,
-    SharedBitmapManager* shared_bitmap_manager,
     gpu::SharedImageInterface* shared_image_interface,
     ReservedResourceIdTracker* id_tracker,
     SaveDirectiveCompleteCallback sequence_id_finished_callback)
diff --git a/components/viz/service/transitions/surface_animation_manager.h b/components/viz/service/transitions/surface_animation_manager.h
index df7c02b4..ffcf14df 100644
--- a/components/viz/service/transitions/surface_animation_manager.h
+++ b/components/viz/service/transitions/surface_animation_manager.h
@@ -16,7 +16,6 @@
 #include "components/viz/common/quads/compositor_render_pass.h"
 #include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
 #include "components/viz/common/resources/resource_id.h"
-#include "components/viz/service/display/shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/surface_resource_holder.h"
 #include "components/viz/service/surfaces/surface_saved_frame.h"
 #include "components/viz/service/transitions/transferable_resource_tracker.h"
@@ -50,7 +49,6 @@
   static std::unique_ptr<SurfaceAnimationManager> CreateWithSave(
       const CompositorFrameTransitionDirective& directive,
       Surface* surface,
-      SharedBitmapManager* shared_bitmap_manager,
       gpu::SharedImageInterface* shared_image_interface,
       ReservedResourceIdTracker* id_tracker,
       SaveDirectiveCompleteCallback sequence_id_finished_callback);
@@ -91,7 +89,6 @@
   SurfaceAnimationManager(
       const CompositorFrameTransitionDirective& directive,
       Surface* surface,
-      SharedBitmapManager* shared_bitmap_manager,
       gpu::SharedImageInterface* shared_image_interface,
       ReservedResourceIdTracker* id_tracker,
       SaveDirectiveCompleteCallback sequence_id_finished_callback);
diff --git a/components/viz/test/BUILD.gn b/components/viz/test/BUILD.gn
index f86f32b..2eae54e 100644
--- a/components/viz/test/BUILD.gn
+++ b/components/viz/test/BUILD.gn
@@ -89,8 +89,6 @@
     "test_output_surface_provider.h",
     "test_raster_interface.cc",
     "test_raster_interface.h",
-    "test_shared_bitmap_manager.cc",
-    "test_shared_bitmap_manager.h",
     "test_shared_image_interface_provider.cc",
     "test_shared_image_interface_provider.h",
     "test_surface_id_allocator.cc",
diff --git a/components/viz/test/test_shared_bitmap_manager.cc b/components/viz/test/test_shared_bitmap_manager.cc
deleted file mode 100644
index 025ce2db..0000000
--- a/components/viz/test/test_shared_bitmap_manager.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright 2014 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/viz/test/test_shared_bitmap_manager.h"
-
-#include <stdint.h>
-
-#include <utility>
-
-#include "base/memory/read_only_shared_memory_region.h"
-#include "base/notreached.h"
-#include "components/viz/common/resources/bitmap_allocation.h"
-#include "mojo/public/cpp/system/platform_handle.h"
-
-namespace viz {
-
-TestSharedBitmapManager::TestSharedBitmapManager() = default;
-
-TestSharedBitmapManager::~TestSharedBitmapManager() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-}
-
-std::unique_ptr<SharedBitmap> TestSharedBitmapManager::GetSharedBitmapFromId(
-    const gfx::Size&,
-    SharedImageFormat,
-    const SharedBitmapId& id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  const auto it = mapping_map_.find(id);
-  if (it == mapping_map_.end())
-    return nullptr;
-  // NOTE: pixels needs to be writable for legacy reasons, but SharedBitmap
-  // instances returned by a SharedBitmapManager are always read-only.
-  auto* pixels = static_cast<uint8_t*>(const_cast<void*>(it->second.memory()));
-  return std::make_unique<SharedBitmap>(pixels);
-}
-
-base::UnguessableToken
-TestSharedBitmapManager::GetSharedBitmapTracingGUIDFromId(
-    const SharedBitmapId& id) {
-  const auto it = mapping_map_.find(id);
-  if (it == mapping_map_.end())
-    return {};
-  return it->second.guid();
-}
-
-bool TestSharedBitmapManager::ChildAllocatedSharedBitmap(
-    base::ReadOnlySharedMemoryMapping mapping,
-    const SharedBitmapId& id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  // TestSharedBitmapManager is both the client and service side. So the
-  // notification here should be about a bitmap that was previously allocated
-  // with AllocateSharedBitmap().
-  if (mapping_map_.find(id) == mapping_map_.end()) {
-    mapping_map_.emplace(id, std::move(mapping));
-  }
-
-  // The same bitmap id should not be notified more than once.
-  DCHECK_EQ(notified_set_.count(id), 0u);
-  notified_set_.insert(id);
-  return true;
-}
-
-bool TestSharedBitmapManager::LocalAllocatedSharedBitmap(
-    SkBitmap bitmap,
-    const SharedBitmapId& id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  NOTIMPLEMENTED();
-  return false;
-}
-
-void TestSharedBitmapManager::ChildDeletedSharedBitmap(
-    const SharedBitmapId& id) {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-
-  notified_set_.erase(id);
-  mapping_map_.erase(id);
-}
-
-}  // namespace viz
diff --git a/components/viz/test/test_shared_bitmap_manager.h b/components/viz/test/test_shared_bitmap_manager.h
deleted file mode 100644
index 3807ff7..0000000
--- a/components/viz/test/test_shared_bitmap_manager.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2014 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_VIZ_TEST_TEST_SHARED_BITMAP_MANAGER_H_
-#define COMPONENTS_VIZ_TEST_TEST_SHARED_BITMAP_MANAGER_H_
-
-#include <map>
-#include <memory>
-#include <set>
-
-#include "base/memory/shared_memory_mapping.h"
-#include "base/sequence_checker.h"
-#include "components/viz/service/display/shared_bitmap_manager.h"
-
-namespace viz {
-
-class TestSharedBitmapManager : public SharedBitmapManager {
- public:
-  TestSharedBitmapManager();
-  ~TestSharedBitmapManager() override;
-
-  // SharedBitmapManager implementation.
-  std::unique_ptr<SharedBitmap> GetSharedBitmapFromId(
-      const gfx::Size& size,
-      SharedImageFormat format,
-      const SharedBitmapId& id) override;
-  base::UnguessableToken GetSharedBitmapTracingGUIDFromId(
-      const SharedBitmapId& id) override;
-  bool ChildAllocatedSharedBitmap(base::ReadOnlySharedMemoryMapping mapping,
-                                  const SharedBitmapId& id) override;
-  bool LocalAllocatedSharedBitmap(SkBitmap bitmap,
-                                  const SharedBitmapId& id) override;
-  void ChildDeletedSharedBitmap(const SharedBitmapId& id) override;
-
- private:
-  SEQUENCE_CHECKER(sequence_checker_);
-
-  std::map<SharedBitmapId, base::ReadOnlySharedMemoryMapping> mapping_map_;
-  std::set<SharedBitmapId> notified_set_;
-};
-
-}  // namespace viz
-
-#endif  // COMPONENTS_VIZ_TEST_TEST_SHARED_BITMAP_MANAGER_H_
diff --git a/content/browser/preloading/prefetch/prefetch_container.cc b/content/browser/preloading/prefetch/prefetch_container.cc
index 0faadd4..5e0ce60 100644
--- a/content/browser/preloading/prefetch/prefetch_container.cc
+++ b/content/browser/preloading/prefetch/prefetch_container.cc
@@ -1199,9 +1199,7 @@
 
 const PrefetchResponseReader* PrefetchContainer::GetNonRedirectResponseReader()
     const {
-  if (redirect_chain_.empty()) {
-    return nullptr;
-  }
+  CHECK(!redirect_chain_.empty());
   if (!redirect_chain_.back()->response_reader_->GetHead()) {
     // Either the last PrefetchResponseReader is for a redirect response, or for
     // a final response not yet receiving its header.
@@ -1486,11 +1484,11 @@
   }
 
   DVLOG(1) << *this << "(GetServableState)"
-           << "(streaming_loader=" << streaming_loader_.get() << ")"
-           << "(redirect_chain.empty=" << redirect_chain_.empty() << ")";
+           << "(streaming_loader=" << streaming_loader_.get()
+           << ", LoadState=" << load_state_ << ")";
   // Can only block until head if the request has been started using a
   // streaming URL loader and head/failure/redirect hasn't been received yet.
-  if (streaming_loader_ && !redirect_chain_.empty() &&
+  if (streaming_loader_ &&
       redirect_chain_.back()->response_reader_->IsWaitingForResponse()) {
     return ServableState::kShouldBlockUntilHeadReceived;
   }
diff --git a/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc b/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc
index 48de9e1f..b71b88e 100644
--- a/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc
+++ b/content/browser/renderer_host/embedded_frame_sink_provider_impl_unittest.cc
@@ -15,7 +15,6 @@
 #include "build/build_config.h"
 #include "components/viz/common/quads/compositor_frame.h"
 #include "components/viz/host/host_frame_sink_manager.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "components/viz/test/compositor_frame_helpers.h"
 #include "components/viz/test/fake_host_frame_sink_client.h"
@@ -146,7 +145,7 @@
 
     // The FrameSinkManagerImpl implementation is in-process here for tests.
     frame_sink_manager_ = std::make_unique<viz::FrameSinkManagerImpl>(
-        viz::FrameSinkManagerImpl::InitParams(&shared_bitmap_manager_));
+        viz::FrameSinkManagerImpl::InitParams());
     host_frame_sink_manager_->SetLocalManager(frame_sink_manager_.get());
     frame_sink_manager_->SetLocalClient(host_frame_sink_manager_.get());
 
@@ -169,7 +168,6 @@
   // A MessageLoop is required for mojo bindings which are used to
   // connect to graphics services.
   base::test::SingleThreadTaskEnvironment task_environment_;
-  viz::ServerSharedBitmapManager shared_bitmap_manager_;
   viz::FakeHostFrameSinkClient host_frame_sink_client_;
   std::unique_ptr<viz::HostFrameSinkManager> host_frame_sink_manager_;
   std::unique_ptr<viz::FrameSinkManagerImpl> frame_sink_manager_;
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 344d6a2..27c692d5 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -5867,9 +5867,52 @@
   // Cases with a UrlLoader are handled in OnStartChecksComplete.
   MaybeDispatchNavigateEventForCrossDocumentTraversal();
 
+  InheritServiceWorkerControllerFromParentIfNeeded();
+
   CommitNavigation();
 }
 
+void NavigationRequest::InheritServiceWorkerControllerFromParentIfNeeded() {
+  if (!base::FeatureList::IsEnabled(features::kServiceWorkerSrcdocSupport)) {
+    return;
+  }
+
+  CHECK(!loader_);
+  CHECK(!service_worker_handle_);
+  RenderFrameHostImpl* parent = frame_tree_node()->parent();
+  if (!parent || !GetURL().IsAboutSrcdoc()) {
+    return;
+  }
+  base::WeakPtr<ServiceWorkerClient> parent_service_worker_client =
+      parent->GetLastCommittedServiceWorkerClient();
+  if (!parent_service_worker_client) {
+    return;
+  }
+
+  if ((frame_tree_node_->pending_frame_policy().sandbox_flags &
+       network::mojom::WebSandboxFlags::kOrigin) ==
+      network::mojom::WebSandboxFlags::kOrigin) {
+    return;
+  }
+  StoragePartition* partition = GetStoragePartitionWithCurrentSiteInfo();
+  auto* service_worker_context = static_cast<ServiceWorkerContextWrapper*>(
+      partition->GetServiceWorkerContext());
+  // As ServiceWorkerMainResourceHandle is not used for intercepting the srcdoc
+  // iframe main resource, the fetch event client id is not used. Use empty
+  // string as fetch_event_client_id when creating it.
+  service_worker_handle_ = std::make_unique<ServiceWorkerMainResourceHandle>(
+      service_worker_context, base::DoNothing(),
+      /*fetch_event_client_id=*/std::string(), parent_service_worker_client);
+  service_worker_handle_->set_service_worker_client(
+      service_worker_context->context()
+          ->service_worker_client_owner()
+          .CreateServiceWorkerClientForWindow(
+              IsSecureFrame(frame_tree_node_->parent()),
+              frame_tree_node_->frame_tree_node_id()));
+  service_worker_handle_->service_worker_client()->InheritControllerFrom(
+      *parent_service_worker_client, net::SimplifyUrlForRequest(GetURL()));
+}
+
 void NavigationRequest::RunCommitDeferringConditions() {
   commit_deferrer_->RegisterDeferringConditions(*this);
   commit_deferrer_->ProcessChecks();
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index 097243b..befaf810 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -2148,6 +2148,13 @@
   // contexts or if DocumentIsolationPolicy is not supported.
   void SanitizeDocumentIsolationPolicyHeader();
 
+  // Sets up service worker client info to inherit controller from the parent
+  // frame if it is a same origin srcdoc iframe.
+  // This method creates a ServiceWorkerClient associated with the navigating
+  // frame. It should not be called when NavigationURLLoader is used as that
+  // would also create ServiceWorkerClient and cause conflict.
+  void InheritServiceWorkerControllerFromParentIfNeeded();
+
   // Never null. The pointee node owns this navigation request instance.
   // This field is not a raw_ptr because of incompatibilities with tracing
   // (TRACE_EVENT*), perfetto::TracedDictionary::Add and gmock/EXPECT_THAT.
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc
index a8d867c1..2f95024 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -754,8 +754,6 @@
   CHECK(GetBackgroundColor());
 
   SkColor color = *GetBackgroundColor();
-  bool opaque = SkColorGetA(color) == SK_AlphaOPAQUE;
-  window_->layer()->SetFillsBoundsOpaquely(opaque);
   window_->layer()->SetColor(color);
 }
 
diff --git a/content/browser/service_worker/service_worker_client.cc b/content/browser/service_worker/service_worker_client.cc
index 3f4d8071..0beb070 100644
--- a/content/browser/service_worker/service_worker_client.cc
+++ b/content/browser/service_worker/service_worker_client.cc
@@ -400,7 +400,6 @@
 blink::mojom::ServiceWorkerClientType ServiceWorkerClient::GetClientType()
     const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(client_info_);
   return absl::visit(
       base::Overloaded(
           [](GlobalRenderFrameHostId render_frame_host_id) {
@@ -412,30 +411,24 @@
           [](blink::SharedWorkerToken shared_worker_token) {
             return blink::mojom::ServiceWorkerClientType::kSharedWorker;
           }),
-      *client_info_);
+      client_info_);
 }
 
 bool ServiceWorkerClient::IsContainerForWindowClient() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return client_info_ &&
-         absl::holds_alternative<GlobalRenderFrameHostId>(*client_info_);
+  return absl::holds_alternative<GlobalRenderFrameHostId>(client_info_);
 }
 
 bool ServiceWorkerClient::IsContainerForWorkerClient() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  using blink::mojom::ServiceWorkerClientType;
-  if (!client_info_) {
-    return false;
-  }
-
-  return absl::holds_alternative<blink::DedicatedWorkerToken>(*client_info_) ||
-         absl::holds_alternative<blink::SharedWorkerToken>(*client_info_);
+  return absl::holds_alternative<blink::DedicatedWorkerToken>(client_info_) ||
+         absl::holds_alternative<blink::SharedWorkerToken>(client_info_);
 }
 
 ServiceWorkerClientInfo ServiceWorkerClient::GetServiceWorkerClientInfo()
     const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return *client_info_;
+  return client_info_;
 }
 
 blink::mojom::ServiceWorkerContainerInfoForClientPtr
@@ -629,7 +622,7 @@
                                  shared_worker_token, origin)
                            : std::nullopt;
           }),
-      *client_info_);
+      client_info_);
 
   if (storage_key) {
     return *storage_key;
@@ -752,7 +745,7 @@
 GlobalRenderFrameHostId ServiceWorkerClient::GetRenderFrameHostId() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(IsContainerForWindowClient());
-  return absl::get<GlobalRenderFrameHostId>(*client_info_);
+  return absl::get<GlobalRenderFrameHostId>(client_info_);
 }
 
 int ServiceWorkerClient::GetProcessId() const {
@@ -849,7 +842,7 @@
                             static_cast<int32_t>(GetClientType()));
     SCOPED_CRASH_KEY_BOOL("SWC_OnEBFC", "is_execution_ready",
                           is_execution_ready());
-    SCOPED_CRASH_KEY_BOOL("SWC_OnEBFC", "is_blob_url",
+    SCOPED_CRASH_KEY_BOOL("SWC_OnEBFC", "is_blob_or_about_url",
                           url() != GetUrlForScopeMatch());
     SCOPED_CRASH_KEY_BOOL("SWC_OnEBFC", "is_inherited", is_inherited());
     CHECK(!controller_->BFCacheContainsControllee(client_uuid()));
@@ -864,7 +857,7 @@
   // TODO(crbug.com/330928087): remove check when this issue resolved.
   SCOPED_CRASH_KEY_BOOL("SWC_OnRFBFC", "is_in_bfcache",
                         is_in_back_forward_cache_);
-  SCOPED_CRASH_KEY_BOOL("SWC_OnRFBFC", "is_blob_url",
+  SCOPED_CRASH_KEY_BOOL("SWC_OnRFBFC", "is_blob_or_about_url",
                         url() != GetUrlForScopeMatch());
   SCOPED_CRASH_KEY_BOOL("SWC_OnRFBFC", "is_inherited", is_inherited());
   if (controller_) {
@@ -1007,7 +1000,7 @@
                             static_cast<int32_t>(GetClientType()));
     SCOPED_CRASH_KEY_BOOL("SWV_RCFBCM", "is_execution_ready",
                           is_execution_ready());
-    SCOPED_CRASH_KEY_BOOL("SWV_RCFBCM", "is_blob_url",
+    SCOPED_CRASH_KEY_BOOL("SWV_RCFBCM", "is_blob_or_about_url",
                           url() != GetUrlForScopeMatch());
     SCOPED_CRASH_KEY_BOOL("SWV_RCFBCM", "is_inherited", is_inherited());
     CHECK(!version->BFCacheContainsControllee(client_uuid()));
@@ -1082,26 +1075,39 @@
 #endif  // DCHECK_IS_ON()
 
 const GURL& ServiceWorkerClient::GetUrlForScopeMatch() const {
-  if (!scope_match_url_for_blob_client_.is_empty()) {
-    return scope_match_url_for_blob_client_;
+  if (!scope_match_url_for_client_.is_empty()) {
+    return scope_match_url_for_client_;
   }
   return url_;
 }
 
 void ServiceWorkerClient::InheritControllerFrom(
     ServiceWorkerClient& creator_host,
-    const GURL& blob_url) {
+    const GURL& client_url) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(base::FeatureList::IsEnabled(kSharedWorkerBlobURLFix) ||
-         blink::mojom::ServiceWorkerClientType::kDedicatedWorker ==
-             GetClientType());
-  DCHECK(blob_url.SchemeIsBlob());
+  DCHECK(GetClientType() ==
+             blink::mojom::ServiceWorkerClientType::kDedicatedWorker ||
+         (base::FeatureList::IsEnabled(kSharedWorkerBlobURLFix) &&
+          GetClientType() ==
+              blink::mojom::ServiceWorkerClientType::kSharedWorker) ||
+         (base::FeatureList::IsEnabled(features::kServiceWorkerSrcdocSupport) &&
+          GetClientType() == blink::mojom::ServiceWorkerClientType::kWindow &&
+          client_url.IsAboutSrcdoc()));
+  // Only expect srcdoc url or blob url of same origin as creator for
+  // client_url.
+  DCHECK((client_url.SchemeIsBlob() &&
+          url::Origin::Create(client_url)
+              .IsSameOriginWith(creator_host.key().origin())) ||
+         (base::FeatureList::IsEnabled(features::kServiceWorkerSrcdocSupport) &&
+          client_url.IsAboutSrcdoc()));
 
-  UpdateUrls(blob_url, creator_host.top_frame_origin(), creator_host.key());
-
-  // Let `scope_match_url_for_blob_client_` be the creator's url for scope match
+  // Let `scope_match_url_for_client_` be the creator's url for scope match
   // because a client should be handled by the service worker of its creator.
-  scope_match_url_for_blob_client_ = creator_host.GetUrlForScopeMatch();
+  // Update it before UpdateUrls so that CheckOnUpdateUrls inside UpdateUrls
+  // checks with the updated GetUrlForScopeMatch().
+  scope_match_url_for_client_ = creator_host.GetUrlForScopeMatch();
+
+  UpdateUrls(client_url, creator_host.top_frame_origin(), creator_host.key());
 
   // Inherit the controller of the creator.
   if (creator_host.controller_registration()) {
diff --git a/content/browser/service_worker/service_worker_client.h b/content/browser/service_worker/service_worker_client.h
index d1cb7a1eb..4f428fb 100644
--- a/content/browser/service_worker/service_worker_client.h
+++ b/content/browser/service_worker/service_worker_client.h
@@ -312,16 +312,16 @@
 
   // For service worker clients. Returns the URL that is used for scope matching
   // algorithm. This can be different from url() in the case of blob URL
-  // workers. In that case, url() may be like "blob://https://a.test" and the
-  // scope matching URL is "https://a.test", inherited from the parent container
-  // host.
+  // workers or srcdoc/about:blank iframes. In that case, url() may be like
+  // "blob://https://a.test" or "about:srcdoc" and the scope matching URL is
+  // "https://a.test", inherited from the parent container host.
   const GURL& GetUrlForScopeMatch() const;
 
-  // For service worker clients that are dedicated workers. Inherits the
-  // controller of the creator document or worker. Used when the client was
-  // created with a blob URL.
+  // For service worker clients that are dedicated workers and srcdoc iframe.
+  // Inherits the controller of the creator document or worker. Used when the
+  // client was created with a blob, about:srcdoc or about:blank URL.
   void InheritControllerFrom(ServiceWorkerClient& creator_host,
-                             const GURL& blob_url);
+                             const GURL& client_url);
 
   void SetContainerReady();
 
@@ -501,11 +501,12 @@
   base::flat_set<PendingUpdateVersion> versions_to_update_;
 
   // The type of client.
-  std::optional<ServiceWorkerClientInfo> client_info_;
+  ServiceWorkerClientInfo client_info_;
 
   // The URL used for service worker scope matching. It is empty except in the
-  // case of a service worker client with a blob URL.
-  GURL scope_match_url_for_blob_client_;
+  // case of a service worker client with a blob, about:blank or about:srcdoc
+  // URL.
+  GURL scope_match_url_for_client_;
 
   // Become true if the container is inherited by other container.
   bool is_inherited_ = false;
diff --git a/content/browser/service_worker/service_worker_client_utils.cc b/content/browser/service_worker/service_worker_client_utils.cc
index 89e844e..5a9147c 100644
--- a/content/browser/service_worker/service_worker_client_utils.cc
+++ b/content/browser/service_worker/service_worker_client_utils.cc
@@ -335,6 +335,8 @@
     return;
   }
 
+  const url::Origin controller_origin =
+      url::Origin::Create(controller->script_url());
   for (const auto& it : clients_info) {
     blink::mojom::ServiceWorkerClientInfoPtr info =
         GetWindowClientInfo(std::get<0>(it), std::get<1>(it), std::get<2>(it));
@@ -347,12 +349,15 @@
       continue;
     DCHECK(!info->client_uuid.empty());
 
-    // We can get info for a frame that was navigating end ended up with a
+    // TODO(crbug.com/385901567): Investigate/clarify the intention of this
+    // check.
+    // We can get info for a frame that was navigating and ended up with a
     // different URL than expected. In such case, we should make sure to not
     // expose cross-origin WindowClient.
-    if (info->url.DeprecatedGetOriginAsURL() !=
-        controller->script_url().DeprecatedGetOriginAsURL())
+    auto* rfh = RenderFrameHostImpl::FromID(std::get<0>(it));
+    if (!controller_origin.IsSameOriginWith(rfh->GetLastCommittedOrigin())) {
       continue;
+    }
 
     clients.push_back(std::move(info));
   }
diff --git a/content/browser/service_worker/service_worker_container_host.cc b/content/browser/service_worker/service_worker_container_host.cc
index bd5b9f3..7e72fd314 100644
--- a/content/browser/service_worker/service_worker_container_host.cc
+++ b/content/browser/service_worker/service_worker_container_host.cc
@@ -122,7 +122,7 @@
     return;
   }
 
-  std::vector<GURL> urls = {url(), options->scope, script_url};
+  std::vector<GURL> urls = {url_for_access_check(), options->scope, script_url};
   if (!service_worker_security_utils::AllOriginsMatchAndCanAccessServiceWorkers(
           urls)) {
     mojo::ReportBadMessage(ServiceWorkerConsts::kBadMessageImproperOrigins);
@@ -135,7 +135,8 @@
   }
 
   if (!service_worker_security_utils::
-          OriginCanRegisterServiceWorkerFromJavascript(url())) {
+          OriginCanRegisterServiceWorkerFromJavascript(
+              url_for_access_check())) {
     mojo::ReportBadMessage(ServiceWorkerConsts::kBadMessageImproperOrigins);
     // ReportBadMessage() will terminate the renderer process, but Mojo
     // complains if the callback is not run. Just run it with nonsense
@@ -197,7 +198,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (!CanServeContainerHostMethods(
-          &callback, url(), GURL(),
+          &callback, url_for_access_check(), GURL(),
           ServiceWorkerConsts::kServiceWorkerGetRegistrationErrorPrefix,
           nullptr)) {
     return;
@@ -238,7 +239,7 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   if (!CanServeContainerHostMethods(
-          &callback, url(), GURL(),
+          &callback, url_for_access_check(), GURL(),
           ServiceWorkerConsts::kServiceWorkerGetRegistrationsErrorPrefix,
           std::nullopt)) {
     return;
@@ -770,6 +771,11 @@
   return service_worker_client().url();
 }
 
+const GURL& ServiceWorkerContainerHostForClient::url_for_access_check() const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return service_worker_client().GetUrlForScopeMatch();
+}
+
 const base::WeakPtr<ServiceWorkerContextCore>&
 ServiceWorkerContainerHostForServiceWorker::context() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -787,6 +793,12 @@
   return url_;
 }
 
+const GURL& ServiceWorkerContainerHostForServiceWorker::url_for_access_check()
+    const {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  return url_;
+}
+
 ServiceWorkerHost*
 ServiceWorkerContainerHostForServiceWorker::service_worker_host() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -1027,7 +1039,7 @@
     *out_error = ServiceWorkerConsts::kBadMessageInvalidURL;
     return false;
   }
-  std::vector<GURL> urls = {url(), client_url};
+  std::vector<GURL> urls = {url_for_access_check(), client_url};
   if (!service_worker_security_utils::AllOriginsMatchAndCanAccessServiceWorkers(
           urls)) {
     *out_error = ServiceWorkerConsts::kBadMessageImproperOrigins;
@@ -1044,7 +1056,7 @@
     *out_error = ServiceWorkerConsts::kBadMessageFromNonWindow;
     return false;
   }
-  if (!OriginCanAccessServiceWorkers(url())) {
+  if (!OriginCanAccessServiceWorkers(url_for_access_check())) {
     *out_error = ServiceWorkerConsts::kBadMessageImproperOrigins;
     return false;
   }
diff --git a/content/browser/service_worker/service_worker_container_host.h b/content/browser/service_worker/service_worker_container_host.h
index 432a38c..719147c 100644
--- a/content/browser/service_worker/service_worker_container_host.h
+++ b/content/browser/service_worker/service_worker_container_host.h
@@ -175,6 +175,11 @@
 
   // The URL of this context.
   virtual const GURL& url() const = 0;
+  // The url to use for access check, the same url as the one used for scope
+  // match. This is needed for srcdoc iframes where url() is "about:srcdoc" and
+  // url_for_access_check() is the parent client's URL that matches the service
+  // worker's origin.
+  virtual const GURL& url_for_access_check() const = 0;
 
   // Calls ContentBrowserClient::AllowServiceWorker(). Returns true if content
   // settings allows service workers to run at |scope|. If this container is for
@@ -304,6 +309,7 @@
   const base::WeakPtr<ServiceWorkerContextCore>& context() const override;
   base::WeakPtr<ServiceWorkerContainerHost> AsWeakPtr() override;
   const GURL& url() const override;
+  const GURL& url_for_access_check() const override;
   bool AllowServiceWorker(const GURL& scope, const GURL& script_url) override;
   void DispatchExtendableMessageEvent(
       scoped_refptr<ServiceWorkerVersion> version,
@@ -470,6 +476,7 @@
   const base::WeakPtr<ServiceWorkerContextCore>& context() const override;
   base::WeakPtr<ServiceWorkerContainerHost> AsWeakPtr() override;
   const GURL& url() const override;
+  const GURL& url_for_access_check() const override;
   bool AllowServiceWorker(const GURL& scope, const GURL& script_url) override;
   void DispatchExtendableMessageEvent(
       scoped_refptr<ServiceWorkerVersion> version,
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index 907f55f..a1696fbd 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -520,7 +520,8 @@
       service_worker_client.container_host()
           ? service_worker_client.container_host()->ukm_source_id()
           : ukm::kInvalidSourceId,
-      service_worker_client.url(), service_worker_client.GetClientType());
+      service_worker_client.GetUrlForScopeMatch(),
+      service_worker_client.GetClientType());
 }
 
 void ServiceWorkerClientOwner::DestroyServiceWorkerClient(
@@ -698,7 +699,8 @@
   observer_list_->Notify(
       FROM_HERE, &ServiceWorkerContextCoreObserver::OnClientIsExecutionReady,
       service_worker_client.container_host()->ukm_source_id(),
-      service_worker_client.url(), service_worker_client.GetClientType());
+      service_worker_client.GetUrlForScopeMatch(),
+      service_worker_client.GetClientType());
 }
 
 bool ServiceWorkerContextCore::MaybeHasRegistrationForStorageKey(
diff --git a/content/browser/service_worker/service_worker_object_host.cc b/content/browser/service_worker/service_worker_object_host.cc
index be6642f9..836c424 100644
--- a/content/browser/service_worker/service_worker_object_host.cc
+++ b/content/browser/service_worker/service_worker_object_host.cc
@@ -21,7 +21,8 @@
     scoped_refptr<ServiceWorkerVersion> version)
     : context_(context),
       container_host_(container_host),
-      container_origin_(url::Origin::Create(container_host_->url())),
+      container_origin_(
+          url::Origin::Create(container_host_->url_for_access_check())),
       version_(std::move(version)) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(context_ && container_host_ && version_);
@@ -88,7 +89,8 @@
     std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort);
     return;
   }
-  DCHECK_EQ(container_origin_, url::Origin::Create(container_host_->url()));
+  DCHECK_EQ(container_origin_,
+            url::Origin::Create(container_host_->url_for_access_check()));
 
   // As we don't track tasks between workers and renderers, we can nullify the
   // message's parent task ID.
diff --git a/content/browser/service_worker/service_worker_registration_object_host.cc b/content/browser/service_worker/service_worker_registration_object_host.cc
index 2da8e04..a1dffc8 100644
--- a/content/browser/service_worker/service_worker_registration_object_host.cc
+++ b/content/browser/service_worker/service_worker_registration_object_host.cc
@@ -347,11 +347,12 @@
     return false;
   }
 
-  std::vector<GURL> urls = {container_host_->url(), registration_->scope()};
+  std::vector<GURL> urls = {container_host_->url_for_access_check(),
+                            registration_->scope()};
   if (!service_worker_security_utils::AllOriginsMatchAndCanAccessServiceWorkers(
           urls)) {
     SCOPED_CRASH_KEY_STRING256("SWROH_CSROHM", "host_url",
-                               container_host_->url().spec());
+                               container_host_->url_for_access_check().spec());
     SCOPED_CRASH_KEY_STRING256("SWROH_CSROHM", "reg_scope",
                                registration_->scope().spec());
     receivers_.ReportBadMessage(
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index f204b28..5fb02d4 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -1695,7 +1695,7 @@
       context_->service_worker_client_owner().GetServiceWorkerClientByClientID(
           client_uuid);
   if (!service_worker_client ||
-      service_worker_client->url().DeprecatedGetOriginAsURL() !=
+      service_worker_client->GetUrlForScopeMatch().DeprecatedGetOriginAsURL() !=
           script_url_.DeprecatedGetOriginAsURL()) {
     // The promise will be resolved to 'undefined'.
     // Note that we don't BadMessage here since Clients#get() can be passed an
@@ -1795,7 +1795,7 @@
     }
   }
 
-  if (service_worker_client->url().DeprecatedGetOriginAsURL() !=
+  if (service_worker_client->GetUrlForScopeMatch().DeprecatedGetOriginAsURL() !=
       script_url_.DeprecatedGetOriginAsURL()) {
     associated_interface_receiver_.ReportBadMessage(
         "Received Client#postMessage() request for a cross-origin client.");
@@ -1851,7 +1851,7 @@
     std::move(callback).Run(nullptr /* client */);
     return;
   }
-  if (service_worker_client->url().DeprecatedGetOriginAsURL() !=
+  if (service_worker_client->GetUrlForScopeMatch().DeprecatedGetOriginAsURL() !=
       script_url_.DeprecatedGetOriginAsURL()) {
     associated_interface_receiver_.ReportBadMessage(
         "Received WindowClient#focus() request for a cross-origin client.");
@@ -1908,7 +1908,7 @@
                             std::string("The client was not found."));
     return;
   }
-  if (service_worker_client->url().DeprecatedGetOriginAsURL() !=
+  if (service_worker_client->GetUrlForScopeMatch().DeprecatedGetOriginAsURL() !=
       script_url_.DeprecatedGetOriginAsURL()) {
     associated_interface_receiver_.ReportBadMessage(
         "Received WindowClient#navigate() request for a cross-origin client.");
diff --git a/content/browser/webid/federated_auth_disconnect_request.cc b/content/browser/webid/federated_auth_disconnect_request.cc
index dd70424..0002aeb 100644
--- a/content/browser/webid/federated_auth_disconnect_request.cc
+++ b/content/browser/webid/federated_auth_disconnect_request.cc
@@ -5,6 +5,7 @@
 #include "content/browser/webid/federated_auth_disconnect_request.h"
 
 #include "base/notreached.h"
+#include "content/browser/webid/flags.h"
 #include "content/browser/webid/webid_utils.h"
 #include "content/public/browser/federated_identity_api_permission_context_delegate.h"
 #include "content/public/browser/federated_identity_permission_context_delegate.h"
@@ -109,8 +110,13 @@
   provider_fetcher_ = std::make_unique<FederatedProviderFetcher>(
       *render_frame_host_, network_manager_.get());
   GURL config_url = options_->config->config_url;
+  // TODO(crbug.com/390626180): It seems ok to ignore the well-known checks in
+  // all cases here. However, keeping this unchanged for now when the IDP
+  // registration API is not enabled since we only really need this for that
+  // case.
   provider_fetcher_->Start(
-      {GURL(config_url)}, blink::mojom::RpMode::kPassive, /*icon_ideal_size=*/0,
+      {{config_url, IsFedCmIdPRegistrationEnabled()}},
+      blink::mojom::RpMode::kPassive, /*icon_ideal_size=*/0,
       /*icon_minimum_size=*/0,
       base::BindOnce(
           &FederatedAuthDisconnectRequest::OnAllConfigAndWellKnownFetched,
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index fbada39c..e597b24 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -1215,10 +1215,18 @@
     fetch_data_.pending_idps = std::move(pending_idps);
   }
 
+  std::vector<FederatedProviderFetcher::FetchRequest> idps;
+  for (const auto& idp : fetch_data_.pending_idps) {
+    auto idp_get = token_request_get_infos_.find(idp);
+    CHECK(idp_get != token_request_get_infos_.end());
+    idps.emplace_back(
+        idp, idp_get->second.provider->config->from_idp_registration_api);
+  }
+
   provider_fetcher_ = std::make_unique<FederatedProviderFetcher>(
       render_frame_host(), network_manager_.get());
   provider_fetcher_->Start(
-      fetch_data_.pending_idps, rp_mode_, icon_ideal_size, icon_minimum_size,
+      idps, rp_mode_, icon_ideal_size, icon_minimum_size,
       base::BindOnce(&FederatedAuthRequestImpl::OnAllConfigAndWellKnownFetched,
                      weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index ba03272d..b310b43 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -151,6 +151,7 @@
   std::optional<std::vector<std::string>> fields;
   const char* params_json;
   std::optional<std::string> type;
+  bool from_idp_registration_api = false;
 };
 
 // Parameters for a call to RequestToken.
@@ -1136,6 +1137,8 @@
       options->config = blink::mojom::IdentityProviderConfig::New();
       options->config->config_url = GURL(identity_provider.provider);
       options->config->client_id = identity_provider.client_id;
+      options->config->from_idp_registration_api =
+          identity_provider.from_idp_registration_api;
       options->nonce = identity_provider.nonce;
       options->login_hint = identity_provider.login_hint;
       options->domain_hint = identity_provider.domain_hint;
@@ -1814,6 +1817,32 @@
   EXPECT_FALSE(DidFetch(FetchedEndpoint::ACCOUNTS));
 }
 
+// Test that when the provider url is not in the well-known, it succeeds when it
+// is registered.
+TEST_F(FederatedAuthRequestImplTest, WellKnownNotInListButRegistered) {
+  base::test::ScopedFeatureList list;
+  list.InitAndEnableFeature(features::kFedCmIdPRegistration);
+
+  const char* idp_config_url =
+      kDefaultRequestParameters.identity_providers[0].provider;
+  const char* kWellKnownMismatchConfigUrl = "https://mismatch.example";
+  EXPECT_NE(std::string(idp_config_url), kWellKnownMismatchConfigUrl);
+
+  MockConfiguration config = kConfigurationValid;
+  config.idp_info[idp_config_url].well_known = {
+      {kWellKnownMismatchConfigUrl}, {ParseStatus::kSuccess, net::HTTP_OK}};
+  RequestParameters requestParameters = kDefaultRequestParameters;
+  requestParameters.identity_providers[0].from_idp_registration_api = true;
+
+  // Need to simulate there is actually a registered IdP, or the call will fail.
+  std::vector<GURL> registry;
+  registry.emplace_back(kProviderUrlFull);
+  EXPECT_CALL(*test_permission_delegate_, GetRegisteredIdPs())
+      .WillOnce(Return(registry));
+
+  RunAuthTest(requestParameters, kExpectationSuccess, config);
+}
+
 // Test that the well-known file has too many provider urls.
 TEST_F(FederatedAuthRequestImplTest, WellKnownHasTooManyProviderUrls) {
   RequestExpectations expectation = {
diff --git a/content/browser/webid/federated_auth_user_info_request.cc b/content/browser/webid/federated_auth_user_info_request.cc
index 523a49dc..5abd838 100644
--- a/content/browser/webid/federated_auth_user_info_request.cc
+++ b/content/browser/webid/federated_auth_user_info_request.cc
@@ -165,8 +165,13 @@
   // destroyed.
   provider_fetcher_ = std::make_unique<FederatedProviderFetcher>(
       *render_frame_host_, network_manager_.get());
+  // TODO(crbug.com/390626180): It seems ok to ignore the well-known checks in
+  // all cases here. However, keeping this unchanged for now when the IDP
+  // registration API is not enabled since we only really need this for that
+  // case.
   provider_fetcher_->Start(
-      {idp_config_url_}, blink::mojom::RpMode::kPassive, /*icon_ideal_size=*/0,
+      {{idp_config_url_, IsFedCmIdPRegistrationEnabled()}},
+      blink::mojom::RpMode::kPassive, /*icon_ideal_size=*/0,
       /*icon_minimum_size=*/0,
       base::BindOnce(
           &FederatedAuthUserInfoRequest::OnAllConfigAndWellKnownFetched,
diff --git a/content/browser/webid/federated_provider_fetcher.cc b/content/browser/webid/federated_provider_fetcher.cc
index ac42508..e8879626 100644
--- a/content/browser/webid/federated_provider_fetcher.cc
+++ b/content/browser/webid/federated_provider_fetcher.cc
@@ -56,21 +56,23 @@
 FederatedProviderFetcher::~FederatedProviderFetcher() = default;
 
 void FederatedProviderFetcher::Start(
-    const std::set<GURL>& identity_provider_config_urls,
+    const std::vector<FetchRequest>& requested_providers,
     blink::mojom::RpMode rp_mode,
     int icon_ideal_size,
     int icon_minimum_size,
     RequesterCallback callback) {
   callback_ = std::move(callback);
 
-  for (const GURL& identity_provider_config_url :
-       identity_provider_config_urls) {
+  for (const auto& request : requested_providers) {
     FetchResult fetch_result;
-    fetch_result.identity_provider_config_url = identity_provider_config_url;
+    fetch_result.identity_provider_config_url =
+        request.identity_provider_config_url;
+    fetch_result.force_skip_well_known_enforcement =
+        request.force_skip_well_known_enforcement;
     fetch_results_.push_back(std::move(fetch_result));
 
-    pending_well_known_fetches_.insert(identity_provider_config_url);
-    pending_config_fetches_.insert(identity_provider_config_url);
+    pending_well_known_fetches_.insert(request.identity_provider_config_url);
+    pending_config_fetches_.insert(request.identity_provider_config_url);
   }
 
   // In a separate loop to avoid invalidating references when adding elements to
@@ -97,8 +99,7 @@
   constexpr char kWellKnownFileStr[] = "well-known file";
 
   if (status.parse_status != IdpNetworkRequestManager::ParseStatus::kSuccess &&
-      !ShouldSkipWellKnownEnforcementForIdp(
-          fetch_result.identity_provider_config_url)) {
+      !ShouldSkipWellKnownEnforcementForIdp(fetch_result)) {
     std::optional<std::string> additional_console_error_message =
         webid::ComputeConsoleMessageForHttpResponseCode(kWellKnownFileStr,
                                                         status.response_code);
@@ -275,8 +276,7 @@
   //     contains the config url passed in the JS call
 
   // (a)
-  if (ShouldSkipWellKnownEnforcementForIdp(
-          result.identity_provider_config_url)) {
+  if (ShouldSkipWellKnownEnforcementForIdp(result)) {
     return;
   }
 
@@ -353,14 +353,18 @@
 }
 
 bool FederatedProviderFetcher::ShouldSkipWellKnownEnforcementForIdp(
-    const GURL& idp_url) {
+    const FetchResult& fetch_result) {
   if (IsFedCmWithoutWellKnownEnforcementEnabled()) {
     return true;
   }
+  if (fetch_result.force_skip_well_known_enforcement) {
+    return true;
+  }
 
   // Skip if RP and IDP are same-site.
-  return webid::IsSameSite(render_frame_host_->GetLastCommittedOrigin(),
-                           url::Origin::Create(idp_url));
+  return webid::IsSameSite(
+      render_frame_host_->GetLastCommittedOrigin(),
+      url::Origin::Create(fetch_result.identity_provider_config_url));
 }
 
 }  // namespace content
diff --git a/content/browser/webid/federated_provider_fetcher.h b/content/browser/webid/federated_provider_fetcher.h
index b2c1a4b..d6c9546 100644
--- a/content/browser/webid/federated_provider_fetcher.h
+++ b/content/browser/webid/federated_provider_fetcher.h
@@ -42,12 +42,22 @@
     FetchResult(const FetchResult&);
     ~FetchResult();
     GURL identity_provider_config_url;
+    bool force_skip_well_known_enforcement = false;
     IdpNetworkRequestManager::WellKnown wellknown;
     IdpNetworkRequestManager::Endpoints endpoints;
     std::optional<IdentityProviderMetadata> metadata;
     std::optional<FetchError> error;
   };
 
+  struct FetchRequest {
+    GURL identity_provider_config_url;
+    bool force_skip_well_known_enforcement = false;
+    FetchRequest(const GURL& url, bool force_skip_well_known_enforcement)
+        : identity_provider_config_url(url),
+          force_skip_well_known_enforcement(force_skip_well_known_enforcement) {
+    }
+  };
+
   using RequesterCallback = base::OnceCallback<void(std::vector<FetchResult>)>;
 
   // TODO(crbug.com/40283354): Remove |render_frame_host| when the IDP signin
@@ -61,7 +71,7 @@
 
   // Starts fetch of config and well-known files. Start() should be called at
   // most once per FederatedProviderFetcher instance.
-  void Start(const std::set<GURL>& identity_provider_config_urls,
+  void Start(const std::vector<FetchRequest>& requested_providers,
              blink::mojom::RpMode rp_mode,
              int icon_ideal_size,
              int icon_minimum_size,
@@ -91,7 +101,7 @@
 
   void RunCallbackIfDone();
 
-  bool ShouldSkipWellKnownEnforcementForIdp(const GURL& idp_url);
+  bool ShouldSkipWellKnownEnforcementForIdp(const FetchResult& fetch_result);
 
   raw_ref<RenderFrameHost> render_frame_host_;
 
diff --git a/content/browser/webid/federated_provider_fetcher_unittest.cc b/content/browser/webid/federated_provider_fetcher_unittest.cc
index 7b03525a..0780245 100644
--- a/content/browser/webid/federated_provider_fetcher_unittest.cc
+++ b/content/browser/webid/federated_provider_fetcher_unittest.cc
@@ -38,7 +38,7 @@
       std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
   FederatedProviderFetcher fetcher(*main_rfh(), network_manager.get());
 
-  EXPECT_CALL(*network_manager, FetchConfig(_, _, _, _, _))
+  EXPECT_CALL(*network_manager, FetchConfig)
       .WillOnce(WithArg<4>(
           [](IdpNetworkRequestManager::FetchConfigCallback callback) {
             IdpNetworkRequestManager::Endpoints endpoints;
@@ -53,7 +53,7 @@
           }));
 
   // Returns a 404 for the fetch of the well-known file.
-  EXPECT_CALL(*network_manager, FetchWellKnown(_, _))
+  EXPECT_CALL(*network_manager, FetchWellKnown)
       .WillOnce(WithArg<1>(
           [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
             IdpNetworkRequestManager::WellKnown well_known;
@@ -66,7 +66,9 @@
 
   // Asserts that we get a kWellKnownHttpNotFound.
   fetcher.Start(
-      {GURL("https://idp.example/fedcm.json")}, blink::mojom::RpMode::kPassive,
+      {{GURL("https://idp.example/fedcm.json"),
+        /*force_skip_well_known_enforcement=*/false}},
+      blink::mojom::RpMode::kPassive,
       /*icon_ideal_size=*/0,
       /*icon_minimum_size=*/0,
       base::BindLambdaForTesting(
@@ -90,7 +92,7 @@
       std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
   FederatedProviderFetcher fetcher(*main_rfh(), network_manager.get());
 
-  EXPECT_CALL(*network_manager, FetchConfig(_, _, _, _, _))
+  EXPECT_CALL(*network_manager, FetchConfig)
       .WillOnce(WithArg<4>(
           [](IdpNetworkRequestManager::FetchConfigCallback callback) {
             IdpNetworkRequestManager::Endpoints endpoints;
@@ -105,7 +107,7 @@
           }));
 
   // Returns a 404 for the fetch of the well-known file.
-  EXPECT_CALL(*network_manager, FetchWellKnown(_, _))
+  EXPECT_CALL(*network_manager, FetchWellKnown)
       .WillOnce(WithArg<1>(
           [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
             IdpNetworkRequestManager::WellKnown well_known;
@@ -118,7 +120,9 @@
 
   // Asserts that we get no error in the result.
   fetcher.Start(
-      {GURL("https://idp.example/fedcm.json")}, blink::mojom::RpMode::kPassive,
+      {{GURL("https://idp.example/fedcm.json"),
+        /*force_skip_well_known_enforcement=*/false}},
+      blink::mojom::RpMode::kPassive,
       /*icon_ideal_size=*/0,
       /*icon_minimum_size=*/0,
       base::BindLambdaForTesting(
@@ -140,7 +144,7 @@
   FederatedProviderFetcher fetcher(*main_rfh(), network_manager.get());
 
   // Returns a 404 for the fetch of the config file.
-  EXPECT_CALL(*network_manager, FetchConfig(_, _, _, _, _))
+  EXPECT_CALL(*network_manager, FetchConfig)
       .WillOnce(WithArg<4>(
           [](IdpNetworkRequestManager::FetchConfigCallback callback) {
             std::move(callback).Run(
@@ -148,7 +152,7 @@
                 /*endpoints=*/{}, /*metadata=*/{});
           }));
 
-  EXPECT_CALL(*network_manager, FetchWellKnown(_, _))
+  EXPECT_CALL(*network_manager, FetchWellKnown)
       .WillOnce(WithArg<1>(
           [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
             IdpNetworkRequestManager::WellKnown well_known;
@@ -161,7 +165,9 @@
 
   // Asserts that we get a kConfigHttpNotFound.
   fetcher.Start(
-      {GURL("https://idp.example/fedcm.json")}, blink::mojom::RpMode::kPassive,
+      {{GURL("https://idp.example/fedcm.json"),
+        /*force_skip_well_known_enforcement=*/false}},
+      blink::mojom::RpMode::kPassive,
       /*icon_ideal_size=*/0,
       /*icon_minimum_size=*/0,
       base::BindLambdaForTesting(
@@ -183,14 +189,14 @@
   FederatedProviderFetcher fetcher(*main_rfh(), network_manager.get());
 
   // Returns a 200 but with an empty and invalid response.
-  EXPECT_CALL(*network_manager, FetchConfig(_, _, _, _, _))
+  EXPECT_CALL(*network_manager, FetchConfig)
       .WillOnce(WithArg<4>(
           [](IdpNetworkRequestManager::FetchConfigCallback callback) {
             std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
                                     /*endpoints=*/{}, /*metadata=*/{});
           }));
 
-  EXPECT_CALL(*network_manager, FetchWellKnown(_, _))
+  EXPECT_CALL(*network_manager, FetchWellKnown)
       .WillOnce(WithArg<1>(
           [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
             IdpNetworkRequestManager::WellKnown well_known;
@@ -203,7 +209,9 @@
 
   // Asserts that we get a kConfigHttpNotFound.
   fetcher.Start(
-      {GURL("https://idp.example/fedcm.json")}, blink::mojom::RpMode::kPassive,
+      {{GURL("https://idp.example/fedcm.json"),
+        /*force_skip_well_known_enforcement=*/false}},
+      blink::mojom::RpMode::kPassive,
       /*icon_ideal_size=*/0,
       /*icon_minimum_size=*/0,
       base::BindLambdaForTesting(
@@ -225,7 +233,7 @@
   FederatedProviderFetcher fetcher(*main_rfh(), network_manager.get());
 
   // Returns a 200 but with an empty and invalid response.
-  EXPECT_CALL(*network_manager, FetchConfig(_, _, _, _, _))
+  EXPECT_CALL(*network_manager, FetchConfig)
       .WillOnce(WithArg<4>(
           [](IdpNetworkRequestManager::FetchConfigCallback callback) {
             IdpNetworkRequestManager::Endpoints endpoints;
@@ -239,7 +247,7 @@
                                     endpoints, metadata);
           }));
 
-  EXPECT_CALL(*network_manager, FetchWellKnown(_, _))
+  EXPECT_CALL(*network_manager, FetchWellKnown)
       .WillOnce(WithArg<1>(
           [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
             IdpNetworkRequestManager::WellKnown well_known;
@@ -252,7 +260,9 @@
 
   // Asserts that we get a kConfigHttpNotFound.
   fetcher.Start(
-      {GURL("https://idp.example/fedcm.json")}, blink::mojom::RpMode::kPassive,
+      {{GURL("https://idp.example/fedcm.json"),
+        /*force_skip_well_known_enforcement=*/false}},
+      blink::mojom::RpMode::kPassive,
       /*icon_ideal_size=*/0,
       /*icon_minimum_size=*/0,
       base::BindLambdaForTesting(
@@ -272,7 +282,7 @@
   FederatedProviderFetcher fetcher(*main_rfh(), network_manager.get());
 
   // Returns a 200 but with an empty and invalid response.
-  EXPECT_CALL(*network_manager, FetchConfig(_, _, _, _, _))
+  EXPECT_CALL(*network_manager, FetchConfig)
       .WillOnce(WithArg<4>(
           [](IdpNetworkRequestManager::FetchConfigCallback callback) {
             IdpNetworkRequestManager::Endpoints endpoints;
@@ -286,7 +296,7 @@
                                     endpoints, metadata);
           }));
 
-  EXPECT_CALL(*network_manager, FetchWellKnown(_, _))
+  EXPECT_CALL(*network_manager, FetchWellKnown)
       .WillOnce(WithArg<1>(
           [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
             IdpNetworkRequestManager::WellKnown well_known;
@@ -301,7 +311,9 @@
 
   // Asserts that we get a kConfigHttpNotFound.
   fetcher.Start(
-      {GURL("https://idp.example/fedcm.json")}, blink::mojom::RpMode::kPassive,
+      {{GURL("https://idp.example/fedcm.json"),
+        /*force_skip_well_known_enforcement=*/false}},
+      blink::mojom::RpMode::kPassive,
       /*icon_ideal_size=*/0,
       /*icon_minimum_size=*/0,
       base::BindLambdaForTesting(
@@ -328,7 +340,7 @@
   FederatedProviderFetcher fetcher(*main_rfh(), network_manager.get());
 
   // Returns a 200 but with an empty and invalid response.
-  EXPECT_CALL(*network_manager, FetchConfig(_, _, _, _, _))
+  EXPECT_CALL(*network_manager, FetchConfig)
       .WillOnce(WithArg<4>(
           [](IdpNetworkRequestManager::FetchConfigCallback callback) {
             IdpNetworkRequestManager::Endpoints endpoints;
@@ -342,7 +354,7 @@
                                     endpoints, metadata);
           }));
 
-  EXPECT_CALL(*network_manager, FetchWellKnown(_, _))
+  EXPECT_CALL(*network_manager, FetchWellKnown)
       .WillOnce(WithArg<1>(
           [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
             IdpNetworkRequestManager::WellKnown well_known;
@@ -358,7 +370,9 @@
 
   // Asserts that we get no error in the result.
   fetcher.Start(
-      {GURL("https://idp.example/fedcm.json")}, blink::mojom::RpMode::kPassive,
+      {{GURL("https://idp.example/fedcm.json"),
+        /*force_skip_well_known_enforcement=*/false}},
+      blink::mojom::RpMode::kPassive,
       /*icon_ideal_size=*/0,
       /*icon_minimum_size=*/0,
       base::BindLambdaForTesting(
@@ -382,7 +396,7 @@
   FederatedProviderFetcher fetcher(*main_rfh(), network_manager.get());
 
   // Returns a 200 but with an empty and invalid response.
-  EXPECT_CALL(*network_manager, FetchConfig(_, _, _, _, _))
+  EXPECT_CALL(*network_manager, FetchConfig)
       .WillOnce(WithArg<4>(
           [](IdpNetworkRequestManager::FetchConfigCallback callback) {
             IdpNetworkRequestManager::Endpoints endpoints;
@@ -396,7 +410,7 @@
                                     endpoints, metadata);
           }));
 
-  EXPECT_CALL(*network_manager, FetchWellKnown(_, _))
+  EXPECT_CALL(*network_manager, FetchWellKnown)
       .WillOnce(WithArg<1>(
           [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
             IdpNetworkRequestManager::WellKnown well_known;
@@ -411,7 +425,9 @@
 
   // Asserts that we get no error in the result.
   fetcher.Start(
-      {GURL("https://idp.example/fedcm.json")}, blink::mojom::RpMode::kPassive,
+      {{GURL("https://idp.example/fedcm.json"),
+        /*force_skip_well_known_enforcement=*/false}},
+      blink::mojom::RpMode::kPassive,
       /*icon_ideal_size=*/0,
       /*icon_minimum_size=*/0,
       base::BindLambdaForTesting(
@@ -755,4 +771,54 @@
             blink::mojom::FederatedAuthRequestResult::kConfigHttpNotFound);
 }
 
+TEST_F(FederatedProviderFetcherTest, RegisteredIdpSkipsWellKnownCheck) {
+  base::test::ScopedFeatureList list;
+  list.InitAndEnableFeature(features::kFedCmIdPRegistration);
+  auto network_manager =
+      std::make_unique<StrictMock<MockIdpNetworkRequestManager>>();
+  FederatedProviderFetcher fetcher(*main_rfh(), network_manager.get());
+
+  EXPECT_CALL(*network_manager, FetchConfig)
+      .WillOnce(WithArg<4>(
+          [](IdpNetworkRequestManager::FetchConfigCallback callback) {
+            IdpNetworkRequestManager::Endpoints endpoints;
+            endpoints.token = GURL("https://idp.example/token.php");
+            endpoints.accounts = GURL("https://idp.example/accounts.php");
+
+            IdentityProviderMetadata metadata;
+            metadata.idp_login_url =
+                GURL("https://idp.example/idp_login_url.php");
+            std::move(callback).Run({ParseStatus::kSuccess, net::HTTP_OK},
+                                    endpoints, metadata);
+          }));
+
+  // Returns a 404 for the fetch of the well-known file.
+  EXPECT_CALL(*network_manager, FetchWellKnown)
+      .WillOnce(WithArg<1>(
+          [](IdpNetworkRequestManager::FetchWellKnownCallback callback) {
+            IdpNetworkRequestManager::WellKnown well_known;
+            std::move(callback).Run(
+                {ParseStatus::kHttpNotFoundError, net::HTTP_NOT_FOUND},
+                well_known);
+          }));
+
+  base::RunLoop loop;
+
+  // Asserts that we get success despite well-known failing.
+  fetcher.Start(
+      {{GURL("https://idp.example/fedcm.json"),
+        /*force_skip_well_known_enforcement=*/true}},
+      blink::mojom::RpMode::kPassive,
+      /*icon_ideal_size=*/0,
+      /*icon_minimum_size=*/0,
+      base::BindLambdaForTesting(
+          [&loop](std::vector<FederatedProviderFetcher::FetchResult> result) {
+            EXPECT_EQ(result.size(), 1ul);
+            EXPECT_FALSE(result[0].error);
+            loop.Quit();
+          }));
+
+  loop.Run();
+}
+
 }  // namespace content
diff --git a/content/common/features.cc b/content/common/features.cc
index 6c1142b..5323e486 100644
--- a/content/common/features.cc
+++ b/content/common/features.cc
@@ -379,20 +379,6 @@
 #endif
 );
 
-// (crbug.com/1371756): When enabled, the static routing API starts
-// ServiceWorker when the routing result of a main resource request was network
-// fallback.
-BASE_FEATURE(kServiceWorkerStaticRouterStartServiceWorker,
-             "ServiceWorkerStaticRouterStartServiceWorker",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
-// (crbug.com/340949948): Killswitch for the fix to address the ServiceWorker
-// main and subreosurce loader lifetime issue, which introduces fetch() failure
-// in the sw fetch handler.
-BASE_FEATURE(kServiceWorkerStaticRouterRaceRequestFix,
-             "kServiceWorkerStaticRouterRaceRequestFix",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 // The set of ServiceWorker to bypass while making navigation request.
 // They are represented by a comma separated list of HEX encoded SHA256 hash of
 // the ServiceWorker's scripts.
@@ -407,6 +393,26 @@
         &kServiceWorkerBypassFetchHandlerHashStrings,
         "script_checksum_to_bypass", ""};
 
+// (crbug.com/41411856): When enabled, the srcdoc iframes are controlled by the
+// same service worker that controls their parent.
+BASE_FEATURE(kServiceWorkerSrcdocSupport,
+             "ServiceWorkerSrcdocSupport",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+// (crbug.com/340949948): Killswitch for the fix to address the ServiceWorker
+// main and subreosurce loader lifetime issue, which introduces fetch() failure
+// in the sw fetch handler.
+BASE_FEATURE(kServiceWorkerStaticRouterRaceRequestFix,
+             "kServiceWorkerStaticRouterRaceRequestFix",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
+// (crbug.com/1371756): When enabled, the static routing API starts
+// ServiceWorker when the routing result of a main resource request was network
+// fallback.
+BASE_FEATURE(kServiceWorkerStaticRouterStartServiceWorker,
+             "ServiceWorkerStaticRouterStartServiceWorker",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 // Enables skipping the early call to CommitPending when navigating away from a
 // crashed frame.
 BASE_FEATURE(kSkipEarlyCommitPendingForCrashedFrame,
diff --git a/content/common/features.h b/content/common/features.h
index e1f2f46..0f9451f 100644
--- a/content/common/features.h
+++ b/content/common/features.h
@@ -89,12 +89,13 @@
 CONTENT_EXPORT BASE_DECLARE_FEATURE(
     kServiceWorkerAvoidMainThreadForInitialization);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(
-    kServiceWorkerStaticRouterStartServiceWorker);
-CONTENT_EXPORT BASE_DECLARE_FEATURE(kServiceWorkerStaticRouterRaceRequestFix);
-CONTENT_EXPORT BASE_DECLARE_FEATURE(
     kServiceWorkerBypassFetchHandlerHashStrings);
 CONTENT_EXPORT extern const base::FeatureParam<std::string>
     kServiceWorkerBypassFetchHandlerBypassedHashStrings;
+CONTENT_EXPORT BASE_DECLARE_FEATURE(kServiceWorkerSrcdocSupport);
+CONTENT_EXPORT BASE_DECLARE_FEATURE(kServiceWorkerStaticRouterRaceRequestFix);
+CONTENT_EXPORT BASE_DECLARE_FEATURE(
+    kServiceWorkerStaticRouterStartServiceWorker);
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kSkipEarlyCommitPendingForCrashedFrame);
 #if BUILDFLAG(IS_MAC)
 CONTENT_EXPORT BASE_DECLARE_FEATURE(kTextInputClient);
diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
index e3ce5415..dfb135b 100644
--- a/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityImpl.java
@@ -666,7 +666,7 @@
             // opening the Tab Switcher. Timers will restart during the next onAttach.
             if (mWebContentsObserver != null) {
                 mHistogramRecorder.recordAccessibilityUsageHistograms();
-                mWebContentsObserver.destroy();
+                mWebContentsObserver.observe(null);
                 mWebContentsObserver = null;
             }
 
@@ -781,7 +781,7 @@
         if (mDelegate.getWebContents() == null) {
             deleteEarly();
         } else {
-            if (mWebContentsObserver != null) mWebContentsObserver.destroy();
+            if (mWebContentsObserver != null) mWebContentsObserver.observe(null);
             WindowEventObserverManager.from(mDelegate.getWebContents()).removeObserver(this);
             ((WebContentsImpl) mDelegate.getWebContents())
                     .removeUserData(WebContentsAccessibilityImpl.class);
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
index ff4ac77c..783df3d22 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
@@ -310,7 +310,7 @@
         mNativeWebContentsAndroid = 0;
         mNavigationController = null;
         if (mObserverProxy != null) {
-            mObserverProxy.destroy();
+            mObserverProxy.webContentsDestroyed();
             mObserverProxy = null;
         }
     }
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
index 8a692f5..f461adc 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java
@@ -505,15 +505,17 @@
         finishObserverCall();
     }
 
+    @Override
     @CalledByNative
-    protected void webContentsDestroyed() {
+    public void webContentsDestroyed() {
         ThreadUtils.assertOnUiThread();
         RewindableIterator<WebContentsObserver> observersIterator = mObservers.rewindableIterator();
         for (; observersIterator.hasNext(); ) {
-            observersIterator.next().destroy();
+            WebContentsObserver observer = observersIterator.next();
+            observer.webContentsDestroyed();
+            observer.observe(null);
         }
-        // All observer destroy() implementations should result in their removal
-        // from the proxy.
+        // All observer observe(null) implementations should result in their removal from the proxy.
         String remainingObservers = "These observers were not removed: ";
         if (!mObservers.isEmpty()) {
             for (observersIterator.rewind(); observersIterator.hasNext(); ) {
diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java b/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
index 7ccf89e..600897c 100644
--- a/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
+++ b/content/public/android/java/src/org/chromium/content_public/browser/WebContentsObserver.java
@@ -245,30 +245,12 @@
     public void mediaSessionCreated(MediaSession mediaSession) {}
 
     /**
-     * Notified when {@link #destroy()} has been triggered. This can be the result of the {@link
-     * #getWebContents()} being destroyed, or because an embedder has triggered {@link #onDestroy()}
-     * manually.
+     * Called when {@link #getWebContents()} is being destroyed.
      *
-     * <p>As an embedder, this method should be overridden and not called directly.
+     * <p>After this call, clients should assume that {@link #getWebContents()} will be imminently
+     * destroyed and the C++ counterpart deleted.
      */
-    protected void onDestroy() {}
-
-    /**
-     * To be called when this object should no longer observe any WebContents and also to carry out
-     * any cleanup handled in {@link #onDestroy}.
-     *
-     * <p>Only the first call to {@link #destroy()}} will trigger {@link #onDestroy()} and
-     * unregister this observer from the linked {@link #getWebContents()} (if one had been
-     * registered).
-     */
-    public final void destroy() {
-        if (mHasBeenDestroyed) return;
-        mHasBeenDestroyed = true;
-
-        onDestroy();
-
-        observe(null);
-    }
+    public void webContentsDestroyed() {}
 
     /**
      * Updates the {@link WebContents} that this class is observing, and if null, stops observing
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/VSyncPausedTest.java b/content/public/android/javatests/src/org/chromium/content/browser/VSyncPausedTest.java
index fbc07f14..8625217 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/VSyncPausedTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/VSyncPausedTest.java
@@ -63,7 +63,7 @@
 
     @After
     public void tearDown() {
-        ThreadUtils.runOnUiThreadBlocking(() -> mObserver.destroy());
+        ThreadUtils.runOnUiThreadBlocking(() -> mObserver.observe(null));
     }
 
     @Test
diff --git a/content/test/data/gpu/webcodecs/copyTo.html b/content/test/data/gpu/webcodecs/copyTo.html
index d802f29..af001e9f 100644
--- a/content/test/data/gpu/webcodecs/copyTo.html
+++ b/content/test/data/gpu/webcodecs/copyTo.html
@@ -21,6 +21,23 @@
     return { r: r, g: g, b: b };
   }
 
+  function compareArrayBuffers(buffer1, buffer2) {
+    const view1 = new DataView(buffer1);
+    const view2 = new DataView(buffer2);
+
+    if (view1.byteLength !== view2.byteLength) {
+      return false;
+    }
+
+    for (let i = 0; i < view1.byteLength; i++) {
+      if (view1.getUint8(i) !== view2.getUint8(i)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
   async function validateFourColorsBytes(frame) {
     const m = 4;
     const tolerance = 8;
@@ -42,7 +59,11 @@
       }
       let size = frame.allocationSize(options);
       let buffer = new ArrayBuffer(size);
+      let shared_buffer = new SharedArrayBuffer(size);
       let layout = await frame.copyTo(buffer, options);
+      await frame.copyTo(shared_buffer, options);
+      TEST.assert(compareArrayBuffers(buffer, shared_buffer),
+                  'Readback mismatch between shared and non-shared buffers');
       let view = new DataView(buffer);
       let rgb = null;
 
diff --git a/content/test/gpu/gpu_tests/common_browser_args.py b/content/test/gpu/gpu_tests/common_browser_args.py
index 97d0b7f0..c8910c69 100644
--- a/content/test/gpu/gpu_tests/common_browser_args.py
+++ b/content/test/gpu/gpu_tests/common_browser_args.py
@@ -25,8 +25,6 @@
     '--enable-direct-composition-video-overlays'
 ENABLE_DIRECT_COMPOSITION_VP_SCALING = '--disable_vp_scaling=0'
 ENABLE_DXVA_VIDEO_DECODER = '--enable-features=DXVAVideoDecoding'
-ENABLE_PLATFORM_HEVC_ENCODER_SUPPORT =\
-    '--enable-features=PlatformHEVCEncoderSupport'
 ENABLE_EXPERIMENTAL_WEB_PLATFORM_FEATURES =\
     '--enable-experimental-web-platform-features'
 ENABLE_GPU_BENCHMARKING = '--enable-gpu-benchmarking'
diff --git a/content/test/gpu/gpu_tests/webcodecs_integration_test.py b/content/test/gpu/gpu_tests/webcodecs_integration_test.py
index 2571ed1..c8041bf 100644
--- a/content/test/gpu/gpu_tests/webcodecs_integration_test.py
+++ b/content/test/gpu/gpu_tests/webcodecs_integration_test.py
@@ -322,7 +322,7 @@
         '--use-fake-device-for-media-stream',
         '--use-fake-ui-for-media-stream',
         '--enable-blink-features=SharedArrayBuffer',
-        cba.ENABLE_PLATFORM_HEVC_ENCODER_SUPPORT,
+        '--enable-features=VideoFrameAsyncCopyTo',
         cba.ENABLE_EXPERIMENTAL_WEB_PLATFORM_FEATURES,
     ] + cba.ENABLE_WEBGPU_FOR_TESTING
 
diff --git a/device/gamepad/gamepad_platform_data_fetcher.h b/device/gamepad/gamepad_platform_data_fetcher.h
index 2e32c0f..e1039f55 100644
--- a/device/gamepad/gamepad_platform_data_fetcher.h
+++ b/device/gamepad/gamepad_platform_data_fetcher.h
@@ -43,7 +43,13 @@
 
 #elif BUILDFLAG(IS_WIN)
 
-  manager->AddFactory(new WgiDataFetcherWin::Factory());
+  // Windows.Gaming.Input is available in Windows 10.0.10240.0 and later.
+  if (base::FeatureList::IsEnabled(
+          features::kEnableWindowsGamingInputDataFetcher)) {
+    manager->AddFactory(new WgiDataFetcherWin::Factory());
+  } else {
+    manager->AddFactory(new XInputDataFetcherWin::Factory());
+  }
   manager->AddFactory(new NintendoDataFetcher::Factory());
   manager->AddFactory(new RawInputDataFetcher::Factory());
 
diff --git a/device/gamepad/public/cpp/gamepad_features.cc b/device/gamepad/public/cpp/gamepad_features.cc
index e725297..7aeb1a9f 100644
--- a/device/gamepad/public/cpp/gamepad_features.cc
+++ b/device/gamepad/public/cpp/gamepad_features.cc
@@ -14,6 +14,15 @@
 
 namespace features {
 
+// Enables the Windows.Gaming.Input data fetcher.
+//
+// Note: This feature is used by the "never expire" flag
+// chrome://flags/#enable-windows-gaming-input-data-fetcher and should not be
+// removed. See crbug.com/40287784.
+BASE_FEATURE(kEnableWindowsGamingInputDataFetcher,
+             "EnableWindowsGamingInputDataFetcher",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 // Enables gamepad multitouch
 BASE_FEATURE(kEnableGamepadMultitouch,
              "EnableGamepadMultitouch",
diff --git a/device/gamepad/public/cpp/gamepad_features.h b/device/gamepad/public/cpp/gamepad_features.h
index e817ee80..4a5bb760 100644
--- a/device/gamepad/public/cpp/gamepad_features.h
+++ b/device/gamepad/public/cpp/gamepad_features.h
@@ -10,6 +10,8 @@
 
 namespace features {
 
+GAMEPAD_FEATURES_EXPORT BASE_DECLARE_FEATURE(
+    kEnableWindowsGamingInputDataFetcher);
 GAMEPAD_FEATURES_EXPORT BASE_DECLARE_FEATURE(kEnableGamepadMultitouch);
 
 GAMEPAD_FEATURES_EXPORT bool IsGamepadMultitouchEnabled();
diff --git a/extensions/browser/api/webcam_private/visca_webcam.cc b/extensions/browser/api/webcam_private/visca_webcam.cc
index ef66d74..99174b46 100644
--- a/extensions/browser/api/webcam_private/visca_webcam.cc
+++ b/extensions/browser/api/webcam_private/visca_webcam.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "extensions/browser/api/webcam_private/visca_webcam.h"
 
 #include <stddef.h>
diff --git a/extensions/browser/extension_registrar.cc b/extensions/browser/extension_registrar.cc
index 1559ee2..286071d 100644
--- a/extensions/browser/extension_registrar.cc
+++ b/extensions/browser/extension_registrar.cc
@@ -31,6 +31,7 @@
 #include "extensions/browser/task_queue_util.h"
 #include "extensions/common/manifest.h"
 #include "extensions/common/manifest_handlers/background_info.h"
+#include "extensions/common/manifest_handlers/shared_module_info.h"
 #include "extensions/common/permissions/permissions_data.h"
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
 
@@ -347,6 +348,30 @@
   DisableExtension(extension_id, disable_reasons);
 }
 
+void ExtensionRegistrar::EnabledReloadableExtensions() {
+  std::vector<std::string> extensions_to_enable;
+  for (const auto& e : registry_->disabled_extensions()) {
+    if (extension_prefs_->GetDisableReasons(e->id()) ==
+        disable_reason::DISABLE_RELOAD) {
+      extensions_to_enable.push_back(e->id());
+    }
+  }
+  for (const std::string& extension : extensions_to_enable) {
+    EnableExtension(extension);
+  }
+}
+
+void ExtensionRegistrar::RemoveComponentExtension(
+    const std::string& extension_id) {
+  scoped_refptr<const Extension> extension(
+      registry_->enabled_extensions().GetByID(extension_id));
+  RemoveExtension(extension_id, UnloadedExtensionReason::UNINSTALL);
+  if (extension.get()) {
+    registry_->TriggerOnUninstalled(extension.get(),
+                                    UNINSTALL_REASON_COMPONENT_REMOVED);
+  }
+}
+
 void ExtensionRegistrar::RemoveDisableReasonAndMaybeEnable(
     const std::string& extension_id,
     disable_reason::DisableReason reason_to_remove) {
@@ -543,6 +568,51 @@
   return true;
 }
 
+void ExtensionRegistrar::UninstallMigratedExtensions(
+    base::span<const char* const> migrated_ids) {
+  const ExtensionSet installed_extensions =
+      registry_->GenerateInstalledExtensionsSet();
+  for (const auto* extension_id : migrated_ids) {
+    auto* extension = installed_extensions.GetByID(extension_id);
+    if (extension) {
+      UninstallExtension(extension_id, UNINSTALL_REASON_COMPONENT_REMOVED,
+                         nullptr);
+      extension_prefs_->MarkObsoleteComponentExtensionAsRemoved(
+          extension->id(), extension->location());
+    }
+  }
+}
+
+void ExtensionRegistrar::FinishInstallation(const Extension* extension) {
+  const Extension* existing_extension =
+      registry_->GetInstalledExtension(extension->id());
+  bool is_update = false;
+  std::string old_name;
+  if (existing_extension) {
+    is_update = true;
+    old_name = existing_extension->name();
+  }
+  registry_->TriggerOnWillBeInstalled(extension, is_update, old_name);
+
+  // Unpacked extensions default to allowing file access, but if that has been
+  // overridden, don't reset the value.
+  if (Manifest::ShouldAlwaysAllowFileAccess(extension->location()) &&
+      !extension_prefs_->HasAllowFileAccessSetting(extension->id())) {
+    extension_prefs_->SetAllowFileAccess(extension->id(), true);
+  }
+
+  AddExtension(extension);
+
+  // Notify observers that need to know when an installation is complete.
+  registry_->TriggerOnInstalled(extension, is_update);
+
+  // Check extensions that may have been delayed only because this shared module
+  // was not available.
+  if (SharedModuleInfo::IsSharedModule(extension)) {
+    delegate_->FinishDelayedInstallationsIfAny();
+  }
+}
+
 bool ExtensionRegistrar::CanBlockExtension(const Extension* extension) const {
   DCHECK(extension);
   return extension->location() != ManifestLocation::kComponent &&
diff --git a/extensions/browser/extension_registrar.h b/extensions/browser/extension_registrar.h
index 0960ab6..ce6bf41e 100644
--- a/extensions/browser/extension_registrar.h
+++ b/extensions/browser/extension_registrar.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/containers/span.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
@@ -104,6 +105,10 @@
     virtual void ShowExtensionDisabledError(const Extension* extension,
                                             bool is_remote_install) = 0;
 
+    // Finishes the deplayed installations if there are any delayed
+    // extensions ready to be installed.
+    virtual void FinishDelayedInstallationsIfAny() = 0;
+
     // Returns true if |extension| can be added.
     virtual bool CanAddExtension(const Extension* extension) = 0;
 
@@ -139,7 +144,7 @@
   // Note: Extensions will not be removed from other sets (blocklisted or
   // blocked). ExtensionService handles that, since it also adds it to those
   // sets. TODO(michaelpg): Make ExtensionRegistrar the sole mutator of
-  // ExtensionRegsitry to simplify this usage.
+  // ExtensionRegistry to simplify this usage.
   void RemoveExtension(const ExtensionId& extension_id,
                        UnloadedExtensionReason reason);
 
@@ -161,6 +166,13 @@
       const std::string& extension_id,
       disable_reason::DisableReason disable_reasons);
 
+  // Attempts to enable all disabled extensions which the only disabled reason
+  // is reloading.
+  void EnabledReloadableExtensions();
+
+  // Removes the specified component extension.
+  void RemoveComponentExtension(const std::string& extension_id);
+
   // Removes the disable reason and enable the extension if there are no disable
   // reasons left and is not blocked for another reason.
   void RemoveDisableReasonAndMaybeEnable(const std::string& extension_id,
@@ -192,6 +204,12 @@
       std::u16string* error,
       base::OnceClosure done_callback = base::NullCallback());
 
+  // Uninstalls extensions that have been migrated to component extensions.
+  void UninstallMigratedExtensions(base::span<const char* const> migrated_ids);
+
+  // Finishes installing |extension| and notifying the observers.
+  void FinishInstallation(const Extension* extension);
+
   // TODO(michaelpg): Add methods for blocklisting and blocking extensions.
 
   // Helper method to determine if an extension can be blocked.
diff --git a/extensions/browser/extension_registrar_unittest.cc b/extensions/browser/extension_registrar_unittest.cc
index dfd7d09..151dcb86 100644
--- a/extensions/browser/extension_registrar_unittest.cc
+++ b/extensions/browser/extension_registrar_unittest.cc
@@ -85,6 +85,7 @@
                     const base::FilePath& path,
                     LoadErrorBehavior load_error_behavior));
   MOCK_METHOD2(ShowExtensionDisabledError, void(const Extension*, bool));
+  MOCK_METHOD0(FinishDelayedInstallationsIfAny, void());
   MOCK_METHOD1(CanEnableExtension, bool(const Extension* extension));
   MOCK_METHOD1(CanDisableExtension, bool(const Extension* extension));
   MOCK_METHOD1(ShouldBlockExtension, bool(const Extension* extension));
diff --git a/extensions/common/file_util.cc b/extensions/common/file_util.cc
index 7d98410..f677967 100644
--- a/extensions/common/file_util.cc
+++ b/extensions/common/file_util.cc
@@ -601,4 +601,17 @@
           extension_path.Append(GetIndexedRulesetDirectoryRelativePath())};
 }
 
+void MaybeCleanupMetadataFolder(const base::FilePath& extension_path) {
+  const std::vector<base::FilePath> reserved_filepaths =
+      GetReservedMetadataFilePaths(extension_path);
+  for (const auto& file : reserved_filepaths) {
+    base::DeletePathRecursively(file);
+  }
+
+  const base::FilePath& metadata_dir = extension_path.Append(kMetadataFolder);
+  if (base::IsDirectoryEmpty(metadata_dir)) {
+    base::DeletePathRecursively(metadata_dir);
+  }
+}
+
 }  // namespace extensions::file_util
diff --git a/extensions/common/file_util.h b/extensions/common/file_util.h
index 713efeb..fd36e007 100644
--- a/extensions/common/file_util.h
+++ b/extensions/common/file_util.h
@@ -177,6 +177,10 @@
 std::vector<base::FilePath> GetReservedMetadataFilePaths(
     const base::FilePath& extension_path);
 
+// Deletes files reserved for use by the Extension system in the kMetadataFolder
+// and the kMetadataFolder itself if it is empty.
+void MaybeCleanupMetadataFolder(const base::FilePath& extension_path);
+
 }  // namespace file_util
 }  // namespace extensions
 
diff --git a/extensions/shell/browser/shell_extension_loader.cc b/extensions/shell/browser/shell_extension_loader.cc
index 6d212b2..887447a 100644
--- a/extensions/shell/browser/shell_extension_loader.cc
+++ b/extensions/shell/browser/shell_extension_loader.cc
@@ -175,6 +175,8 @@
     const Extension* extension,
     bool is_remote_install) {}
 
+void ShellExtensionLoader::FinishDelayedInstallationsIfAny() {}
+
 bool ShellExtensionLoader::CanEnableExtension(const Extension* extension) {
   return true;
 }
diff --git a/extensions/shell/browser/shell_extension_loader.h b/extensions/shell/browser/shell_extension_loader.h
index 3c3a70b..665420e 100644
--- a/extensions/shell/browser/shell_extension_loader.h
+++ b/extensions/shell/browser/shell_extension_loader.h
@@ -69,6 +69,7 @@
       ExtensionRegistrar::LoadErrorBehavior load_error_behavior) override;
   void ShowExtensionDisabledError(const Extension* extension,
                                   bool is_remote_install) override;
+  void FinishDelayedInstallationsIfAny() override;
   bool CanEnableExtension(const Extension* extension) override;
   bool CanDisableExtension(const Extension* extension) override;
   bool ShouldBlockExtension(const Extension* extension) override;
diff --git a/gpu/command_buffer/client/query_tracker.h b/gpu/command_buffer/client/query_tracker.h
index fc0090e1..b31e5d87 100644
--- a/gpu/command_buffer/client/query_tracker.h
+++ b/gpu/command_buffer/client/query_tracker.h
@@ -18,6 +18,7 @@
 #include "base/atomicops.h"
 #include "base/containers/circular_deque.h"
 #include "base/containers/flat_map.h"
+#include "base/functional/callback.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
 #include "gles2_impl_export.h"
diff --git a/gpu/command_buffer/client/shared_image_pool.h b/gpu/command_buffer/client/shared_image_pool.h
index d43207a..8180021 100644
--- a/gpu/command_buffer/client/shared_image_pool.h
+++ b/gpu/command_buffer/client/shared_image_pool.h
@@ -197,8 +197,13 @@
                             std::move(unused_resource_expiration_time)));
   }
 
-  // Clears the pool, deleting all contained images.
-  ~SharedImagePool() override = default;
+  // Clears the pool, deleting all contained images. Also sends an IPC to
+  // destroy the corresponding service side pool.
+  ~SharedImagePool() override {
+    if (sii_) {
+      sii_->DestroySharedImagePool(pool_id_);
+    }
+  }
 
   // Retrieves an image from the pool or creates a new one if the pool is empty.
   scoped_refptr<ClientImageType> GetImage() {
@@ -260,9 +265,20 @@
       std::optional<base::TimeDelta> unused_resource_expiration_time)
       : SharedImagePoolBase(SharedImagePoolId::Create(),
                             image_info,
-                            std::move(sii),
+                            sii,
                             std::move(max_pool_size),
-                            std::move(unused_resource_expiration_time)) {}
+                            std::move(unused_resource_expiration_time)) {
+    mojo::PendingReceiver<gpu::mojom::SharedImagePoolClientInterface>
+        client_receiver;
+    auto client_remote = client_receiver.InitWithNewPipeAndPassRemote();
+    receiver_.Bind(std::move(client_receiver));
+    receiver_.set_disconnect_handler(base::BindOnce(
+        &SharedImagePool::OnDisconnectedSharedImagePoolClientInterface,
+        base::Unretained(this)));
+    sii->CreateSharedImagePool(pool_id_, std::move(client_remote));
+  }
+
+  void OnDisconnectedSharedImagePoolClientInterface() { ClearInternal(); }
 
   mojo::Receiver<mojom::SharedImagePoolClientInterface> receiver_{this};
 
diff --git a/gpu/command_buffer/client/test_shared_image_interface.h b/gpu/command_buffer/client/test_shared_image_interface.h
index 1e9a877..44219d4 100644
--- a/gpu/command_buffer/client/test_shared_image_interface.h
+++ b/gpu/command_buffer/client/test_shared_image_interface.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/synchronization/lock.h"
 #include "build/build_config.h"
@@ -15,6 +16,8 @@
 #include "gpu/command_buffer/common/shared_image_capabilities.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/ipc/client/shared_image_interface_proxy.h"
+#include "gpu/ipc/common/shared_image_pool_client_interface.mojom.h"
+#include "mojo/public/cpp/bindings/remote.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -106,8 +109,23 @@
   void CreateSharedImagePool(
       const SharedImagePoolId& pool_id,
       mojo::PendingRemote<mojom::SharedImagePoolClientInterface> client_remote)
-      override {}
-  void DestroySharedImagePool(const SharedImagePoolId& pool_id) override {}
+      override {
+    auto it = remote_map_.find(pool_id);
+    CHECK(it == remote_map_.end());
+
+    mojo::Remote<mojom::SharedImagePoolClientInterface> remote;
+    remote.Bind(std::move(client_remote));
+    remote_map_.emplace(pool_id, std::move(remote));
+  }
+
+  void DestroySharedImagePool(const SharedImagePoolId& pool_id) override {
+    auto it = remote_map_.find(pool_id);
+    if (it != remote_map_.end()) {
+      // Disconnect the remote and remove the entry.
+      it->second.reset();
+      remote_map_.erase(it);
+    }
+  }
 
   size_t shared_image_count() const { return shared_images_.size(); }
   const gfx::Size& MostRecentSize() const { return most_recent_size_; }
@@ -168,6 +186,13 @@
   // If non-null, this will be used to back mappable SharedImages with test
   // GpuMemoryBuffers.
   std::unique_ptr<TestGpuMemoryBufferManager> test_gmb_manager_;
+
+  // This is used to simply keep the SharedImagePoolClientInterface alive for
+  // the duration of the SharedImagePool. Not keeping it alive and bound
+  // triggers diconnect_handlers causing unexpected behaviour in the test.
+  base::flat_map<SharedImagePoolId,
+                 mojo::Remote<mojom::SharedImagePoolClientInterface>>
+      remote_map_;
 };
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/common/gles2_cmd_format.h b/gpu/command_buffer/common/gles2_cmd_format.h
index a393b1972b..13f6648b 100644
--- a/gpu/command_buffer/common/gles2_cmd_format.h
+++ b/gpu/command_buffer/common/gles2_cmd_format.h
@@ -14,7 +14,6 @@
 #include "base/atomicops.h"
 #include "base/check_op.h"
 #include "base/rand_util.h"
-#include "base/trace_event/trace_event.h"
 #include "gpu/command_buffer/common/cmd_buffer_common.h"
 #include "gpu/command_buffer/common/common_cmd_format.h"
 #include "gpu/command_buffer/common/constants.h"
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
index 6cb728ca..309cf1d 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -19,6 +19,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/ranges/algorithm.h"
 #include "base/strings/string_split.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "gpu/command_buffer/service/command_buffer_service.h"
 #include "gpu/command_buffer/service/decoder_client.h"
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index 18699a3..243ca357 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -19,6 +19,7 @@
 #include "base/numerics/checked_math.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "components/viz/common/resources/shared_image_format_utils.h"
 #include "gpu/command_buffer/common/discardable_handle.h"
diff --git a/gpu/command_buffer/service/service_utils.cc b/gpu/command_buffer/service/service_utils.cc
index 13fce38..d653f60 100644
--- a/gpu/command_buffer/service/service_utils.cc
+++ b/gpu/command_buffer/service/service_utils.cc
@@ -11,6 +11,7 @@
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
diff --git a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc
index cd75cc9..41fead8 100644
--- a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.cc
@@ -18,6 +18,7 @@
 #include "gpu/vulkan/vulkan_image.h"
 #include "gpu/vulkan/vulkan_implementation.h"
 #include "gpu/vulkan/vulkan_util.h"
+#include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkColorSpace.h"
 #include "third_party/skia/include/gpu/MutableTextureState.h"
@@ -701,6 +702,31 @@
   return true;
 }
 
+bool AngleVulkanImageBacking::ReadbackToMemory(
+    const std::vector<SkPixmap>& pixmaps) {
+  if (!BeginAccessSkia(/*read_only=*/true)) {
+    return false;
+  }
+
+  absl::Cleanup cleanup = [&]() { EndAccessSkia(); };
+
+  CHECK_EQ(pixmaps.size(), vk_textures_.size());
+  for (int i = 0; i < format().NumberOfPlanes(); i++) {
+    const auto color_type = viz::ToClosestSkColorType(format(), i);
+    const gfx::Size plane_size = format().GetPlaneSize(i, size());
+
+    CHECK_EQ(color_type, pixmaps[i].colorType());
+    CHECK_EQ(plane_size.width(), pixmaps[i].width());
+    CHECK_EQ(plane_size.height(), pixmaps[i].height());
+
+    if (!vk_textures_[i].Readback(gr_context(), pixmaps[i])) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 AngleVulkanImageBacking::TextureHolderGL::TextureHolderGL() = default;
 AngleVulkanImageBacking::TextureHolderGL::TextureHolderGL(
     TextureHolderGL&& other) = default;
diff --git a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.h b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.h
index 90faeaa8..f18c6e6 100644
--- a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.h
+++ b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing.h
@@ -38,6 +38,7 @@
   // SharedImageBacking implementation.
   SharedImageBackingType GetType() const override;
   bool UploadFromMemory(const std::vector<SkPixmap>& pixmaps) override;
+  bool ReadbackToMemory(const std::vector<SkPixmap>& pixmaps) override;
   void Update(std::unique_ptr<gfx::GpuFence> in_fence) override;
   std::unique_ptr<GLTexturePassthroughImageRepresentation>
   ProduceGLTexturePassthrough(SharedImageManager* manager,
diff --git a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing_factory_unittest.cc
index be82739..a24885d 100644
--- a/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/angle_vulkan_image_backing_factory_unittest.cc
@@ -6,6 +6,7 @@
 
 #include "base/no_destructor.h"
 #include "base/test/scoped_feature_list.h"
+#include "cc/test/pixel_test_utils.h"
 #include "components/viz/common/resources/shared_image_format.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
@@ -182,6 +183,54 @@
   VerifyPixelsWithReadbackGanesh(mailbox, bitmaps);
 }
 
+TEST_P(AngleVulkanImageBackingFactoryTest, ReadbackToMemory) {
+  viz::SharedImageFormat format = GetFormat();
+
+  auto mailbox = Mailbox::Generate();
+  gfx::Size size(9, 9);
+  auto color_space = gfx::ColorSpace::CreateSRGB();
+  GrSurfaceOrigin surface_origin = kTopLeft_GrSurfaceOrigin;
+  SkAlphaType alpha_type = kPremul_SkAlphaType;
+  gpu::SharedImageUsageSet usage = kUsage;
+  gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
+
+  bool supported = backing_factory_->CanCreateSharedImage(
+      usage, format, size, /*thread_safe=*/false, gfx::EMPTY_BUFFER,
+      GrContextType::kVulkan, {});
+  ASSERT_TRUE(supported);
+
+  auto backing = backing_factory_->CreateSharedImage(
+      mailbox, format, surface_handle, size, color_space, surface_origin,
+      alpha_type, usage, "TestLabel", /*is_thread_safe=*/false);
+  ASSERT_TRUE(backing);
+
+  std::vector<SkBitmap> src_bitmaps =
+      AllocateRedBitmaps(format, size, /*added_stride=*/0);
+
+  // Upload from bitmap with expected stride.
+  ASSERT_TRUE(backing->UploadFromMemory(GetSkPixmaps(src_bitmaps)));
+
+  const int num_planes = format.NumberOfPlanes();
+
+  // Do readback into bitmap with same stride and validate pixels match what
+  // was uploaded.
+  std::vector<SkBitmap> readback_bitmaps(num_planes);
+  for (int plane = 0; plane < num_planes; ++plane) {
+    auto& info = src_bitmaps[plane].info();
+    size_t stride = info.minRowBytes();
+    readback_bitmaps[plane].allocPixels(info, stride);
+  }
+
+  std::vector<SkPixmap> pixmaps = GetSkPixmaps(readback_bitmaps);
+  ASSERT_TRUE(backing->ReadbackToMemory(pixmaps));
+
+  for (int plane = 0; plane < num_planes; ++plane) {
+    EXPECT_TRUE(cc::MatchesBitmap(readback_bitmaps[plane], src_bitmaps[plane],
+                                  cc::ExactPixelComparator()))
+        << "plane_index=" << plane;
+  }
+}
+
 std::string TestParamToString(
     const testing::TestParamInfo<viz::SharedImageFormat>& param_info) {
   return param_info.param.ToTestParamString();
diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
index 9f349418..902e4901 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
@@ -25,6 +25,7 @@
 #include "base/memory/raw_ptr.h"
 #include "base/strings/strcat.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/trace_event/trace_event.h"
 #include "gpu/command_buffer/common/shared_image_trace_utils.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/dxgi_shared_handle_manager.h"
diff --git a/gpu/command_buffer/service/shared_image/egl_image_backing_factory.cc b/gpu/command_buffer/service/shared_image/egl_image_backing_factory.cc
index 5f43307..076eedb 100644
--- a/gpu/command_buffer/service/shared_image/egl_image_backing_factory.cc
+++ b/gpu/command_buffer/service/shared_image/egl_image_backing_factory.cc
@@ -116,7 +116,9 @@
 
   if ((usage.HasAny(SHARED_IMAGE_USAGE_WEBGPU_READ |
                     SHARED_IMAGE_USAGE_WEBGPU_WRITE)) &&
-      (use_webgpu_adapter_ != WebGPUAdapterName::kOpenGLES)) {
+      (gr_context_type != GrContextType::kGL ||
+       gl::GetGLImplementation() != gl::kGLImplementationEGLANGLE ||
+       gl::GetANGLEImplementation() != gl::ANGLEImplementation::kOpenGL)) {
     return false;
   }
 
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc b/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc
index cda93bb..dce70da 100644
--- a/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/external_vk_image_backing.cc
@@ -1274,6 +1274,60 @@
   return success;
 }
 
+bool ExternalVkImageBacking::ReadbackToMemory(
+    const std::vector<SkPixmap>& pixmaps) {
+  DCHECK_EQ(pixmaps.size(), vk_textures_.size());
+
+  std::vector<ExternalSemaphore> external_semaphores;
+  if (!BeginAccess(/*readonly=*/true, &external_semaphores, /*is_gl=*/false)) {
+    DLOG(ERROR) << "BeginAccess() failed.";
+    return false;
+  }
+  auto* gr_context = context_state_->gr_context();
+  WaitSemaphoresOnGrContext(gr_context, &external_semaphores);
+
+  bool success = true;
+  for (size_t plane = 0; plane < vk_textures_.size(); ++plane) {
+    if (!vk_textures_[plane].Readback(gr_context, pixmaps[plane])) {
+      success = false;
+    }
+  }
+
+  if (!need_synchronization()) {
+    DCHECK(external_semaphores.empty());
+    EndAccess(/*readonly=*/true, ExternalSemaphore(), /*is_gl=*/false);
+    return success;
+  }
+
+  for (auto& vk_texture : vk_textures_) {
+    gr_context->setBackendTextureState(
+        vk_texture.backend_texture,
+        skgpu::MutableTextureStates::MakeVulkan(VK_IMAGE_LAYOUT_UNDEFINED,
+                                                VK_QUEUE_FAMILY_EXTERNAL));
+  }
+
+  auto end_access_semaphore = external_semaphore_pool()->GetOrCreateSemaphore();
+  VkSemaphore vk_end_access_semaphore = end_access_semaphore.GetVkSemaphore();
+  GrBackendSemaphore end_access_backend_semaphore =
+      GrBackendSemaphores::MakeVk(vk_end_access_semaphore);
+  GrFlushInfo flush_info = {
+      .fNumSemaphores = 1,
+      .fSignalSemaphores = &end_access_backend_semaphore,
+  };
+  gr_context->flush(flush_info);
+
+  // Submit so the |end_access_semaphore| is ready for waiting.
+  gr_context->submit();
+
+  EndAccess(/*readonly=*/true, std::move(end_access_semaphore),
+            /*is_gl=*/false);
+
+  // |external_semaphores| have been waited on and can be reused when submitted
+  // GPU work is done.
+  ReturnPendingSemaphoresWithFenceHelper(std::move(external_semaphores));
+  return success;
+}
+
 bool ExternalVkImageBacking::UploadToGLTexture(
     const std::vector<SkPixmap>& pixmaps) {
   DCHECK(use_separate_gl_texture());
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_backing.h b/gpu/command_buffer/service/shared_image/external_vk_image_backing.h
index 0894bde..c2b27fd 100644
--- a/gpu/command_buffer/service/shared_image/external_vk_image_backing.h
+++ b/gpu/command_buffer/service/shared_image/external_vk_image_backing.h
@@ -161,6 +161,7 @@
   SharedImageBackingType GetType() const override;
   void Update(std::unique_ptr<gfx::GpuFence> in_fence) override;
   bool UploadFromMemory(const std::vector<SkPixmap>& pixmaps) override;
+  bool ReadbackToMemory(const std::vector<SkPixmap>& pixmaps) override;
   scoped_refptr<gfx::NativePixmap> GetNativePixmap() override;
   gfx::GpuMemoryBufferHandle GetGpuMemoryBufferHandle() override;
 
diff --git a/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc
index 31cb18a8..792fef4 100644
--- a/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/external_vk_image_backing_factory_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/functional/callback_helpers.h"
 #include "base/ranges/algorithm.h"
 #include "build/build_config.h"
+#include "cc/test/pixel_test_utils.h"
 #include "components/viz/common/gpu/vulkan_in_process_context_provider.h"
 #include "components/viz/common/resources/shared_image_format.h"
 #include "gpu/command_buffer/service/service_utils.h"
@@ -530,6 +531,54 @@
   VerifyPixelsWithReadbackGanesh(mailbox, bitmaps);
 }
 
+TEST_P(ExternalVkImageBackingFactoryWithFormatTest, ReadbackToMemory) {
+  viz::SharedImageFormat format = get_format();
+
+  auto mailbox = Mailbox::Generate();
+  gfx::Size size(9, 9);
+  auto color_space = gfx::ColorSpace::CreateSRGB();
+  GrSurfaceOrigin surface_origin = kTopLeft_GrSurfaceOrigin;
+  SkAlphaType alpha_type = kPremul_SkAlphaType;
+  gpu::SharedImageUsageSet usage =
+      SHARED_IMAGE_USAGE_DISPLAY_READ | SHARED_IMAGE_USAGE_CPU_UPLOAD;
+  gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle;
+
+  bool supported = backing_factory_->CanCreateSharedImage(
+      usage, format, size, /*thread_safe=*/false, gfx::EMPTY_BUFFER,
+      GrContextType::kVulkan, {});
+  ASSERT_TRUE(supported);
+
+  auto backing = backing_factory_->CreateSharedImage(
+      mailbox, format, surface_handle, size, color_space, surface_origin,
+      alpha_type, usage, "TestLabel", /*is_thread_safe=*/false);
+  ASSERT_TRUE(backing);
+
+  std::vector<SkBitmap> src_bitmaps =
+      AllocateRedBitmaps(format, size, /*added_stride=*/0);
+
+  // Upload from bitmap with expected stride.
+  ASSERT_TRUE(backing->UploadFromMemory(GetSkPixmaps(src_bitmaps)));
+
+  const int num_planes = format.NumberOfPlanes();
+  // Do readback into bitmap with same stride and validate pixels match what
+  // was uploaded.
+  std::vector<SkBitmap> readback_bitmaps(num_planes);
+  for (int plane = 0; plane < num_planes; ++plane) {
+    auto& info = src_bitmaps[plane].info();
+    size_t stride = info.minRowBytes();
+    readback_bitmaps[plane].allocPixels(info, stride);
+  }
+
+  std::vector<SkPixmap> pixmaps = GetSkPixmaps(readback_bitmaps);
+  ASSERT_TRUE(backing->ReadbackToMemory(pixmaps));
+
+  for (int plane = 0; plane < num_planes; ++plane) {
+    EXPECT_TRUE(cc::MatchesBitmap(readback_bitmaps[plane], src_bitmaps[plane],
+                                  cc::ExactPixelComparator()))
+        << "plane_index=" << plane;
+  }
+}
+
 std::string TestParamToString(
     const testing::TestParamInfo<viz::SharedImageFormat>& param_info) {
   return param_info.param.ToTestParamString();
diff --git a/gpu/command_buffer/service/shared_image/shared_image_factory.cc b/gpu/command_buffer/service/shared_image/shared_image_factory.cc
index e33b70e3e..b138a07 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_factory.cc
+++ b/gpu/command_buffer/service/shared_image/shared_image_factory.cc
@@ -730,6 +730,26 @@
   return true;
 }
 
+bool SharedImageFactory::CreateSharedImagePool(
+    const SharedImagePoolId& pool_id,
+    mojo::PendingRemote<mojom::SharedImagePoolClientInterface> client_remote) {
+  auto it = shared_image_pool_map_.find(pool_id);
+  // Ensure that there is no pool already corresponding to the |pool_id|.
+  if (it != shared_image_pool_map_.end()) {
+    return false;
+  }
+  auto pool = std::make_unique<SharedImagePoolService>(
+      pool_id, std::move(client_remote), this);
+  shared_image_pool_map_.emplace(pool_id, std::move(pool));
+  return true;
+}
+
+bool SharedImageFactory::DestroySharedImagePool(
+    const SharedImagePoolId& pool_id) {
+  // Ensure that there is a pool corresponding to the |pool_id|.
+  return shared_image_pool_map_.erase(pool_id);
+}
+
 void SharedImageFactory::RegisterSharedImageBackingFactoryForTesting(
     SharedImageBackingFactory* factory) {
   backing_factory_for_testing_ = factory;
diff --git a/gpu/command_buffer/service/shared_image/shared_image_factory.h b/gpu/command_buffer/service/shared_image/shared_image_factory.h
index c66367d7..e3097afb 100644
--- a/gpu/command_buffer/service/shared_image/shared_image_factory.h
+++ b/gpu/command_buffer/service/shared_image/shared_image_factory.h
@@ -10,6 +10,7 @@
 #include <unordered_set>
 #include <vector>
 
+#include "base/containers/flat_map.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "build/build_config.h"
@@ -18,10 +19,12 @@
 #include "gpu/command_buffer/common/shared_image_capabilities.h"
 #include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
+#include "gpu/command_buffer/service/shared_image/shared_image_pool_service.h"
 #include "gpu/config/gpu_driver_bug_workarounds.h"
 #include "gpu/config/gpu_preferences.h"
 #include "gpu/gpu_gles2_export.h"
 #include "gpu/ipc/common/gpu_memory_buffer_support.h"
+#include "gpu/ipc/common/shared_image_pool_client_interface.mojom.h"
 #include "gpu/ipc/common/surface_handle.h"
 #include "ui/gfx/buffer_types.h"
 #include "ui/gfx/gpu_extra_info.h"
@@ -141,6 +144,11 @@
                                     gfx::Size& size,
                                     gfx::BufferUsage& buffer_usage);
 
+  bool CreateSharedImagePool(
+      const SharedImagePoolId& pool_id,
+      mojo::PendingRemote<mojom::SharedImagePoolClientInterface> client_remote);
+  bool DestroySharedImagePool(const SharedImagePoolId& pool_id);
+
   void RegisterSharedImageBackingFactoryForTesting(
       SharedImageBackingFactory* factory);
 
@@ -203,6 +211,11 @@
                      SharedImageRepresentationFactoryRefKeyEqual>
       shared_images_;
 
+  // Map of all the SharedImagePoolService objects corresponding to its unique
+  // pool id.
+  base::flat_map<SharedImagePoolId, std::unique_ptr<SharedImagePoolService>>
+      shared_image_pool_map_;
+
   // Array of all the backing factories to choose from for creating shared
   // images.
   std::vector<std::unique_ptr<SharedImageBackingFactory>> factories_;
diff --git a/gpu/command_buffer/service/shared_image/texture_holder_vk.cc b/gpu/command_buffer/service/shared_image/texture_holder_vk.cc
index 43c52d0..6157b7c 100644
--- a/gpu/command_buffer/service/shared_image/texture_holder_vk.cc
+++ b/gpu/command_buffer/service/shared_image/texture_holder_vk.cc
@@ -7,6 +7,9 @@
 #include "base/check.h"
 #include "gpu/command_buffer/service/skia_utils.h"
 #include "gpu/vulkan/vulkan_image.h"
+#include "third_party/skia/include/core/SkColorSpace.h"
+#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
+#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
 
 namespace gpu {
@@ -34,4 +37,18 @@
   return info;
 }
 
+bool TextureHolderVk::Readback(GrDirectContext* context,
+                               const SkPixmap& destination) {
+  CHECK(context);
+  auto sk_image = SkImages::BorrowTextureFrom(
+      context, backend_texture, kTopLeft_GrSurfaceOrigin,
+      destination.colorType(), SkAlphaType::kOpaque_SkAlphaType,
+      /*sk_color_space=*/nullptr);
+  if (!sk_image) {
+    return false;
+  }
+
+  return sk_image->readPixels(context, destination, 0, 0);
+}
+
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image/texture_holder_vk.h b/gpu/command_buffer/service/shared_image/texture_holder_vk.h
index c38a3d59..0bfc8aa 100644
--- a/gpu/command_buffer/service/shared_image/texture_holder_vk.h
+++ b/gpu/command_buffer/service/shared_image/texture_holder_vk.h
@@ -7,10 +7,13 @@
 
 #include <memory>
 
+#include "third_party/skia/include/core/SkPixmap.h"
 #include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
 #include "third_party/skia/include/gpu/ganesh/vk/GrVkTypes.h"
 #include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
 
+class GrDirectContext;
+
 namespace gfx {
 class ColorSpace;
 }  // namespace gfx
@@ -34,6 +37,8 @@
 
   GrVkImageInfo GetGrVkImageInfo() const;
 
+  bool Readback(GrDirectContext* context, const SkPixmap& destination);
+
   std::unique_ptr<VulkanImage> vulkan_image;
   GrBackendTexture backend_texture;
   sk_sp<GrPromiseImageTexture> promise_texture;
diff --git a/gpu/ipc/service/image_transport_surface_overlay_mac.mm b/gpu/ipc/service/image_transport_surface_overlay_mac.mm
index 08f7955..6bff6b1 100644
--- a/gpu/ipc/service/image_transport_surface_overlay_mac.mm
+++ b/gpu/ipc/service/image_transport_surface_overlay_mac.mm
@@ -52,6 +52,10 @@
              "NewPresentationFeedbackTimeStamps",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
+BASE_FEATURE(kPresentationDelayForInteractiveFrames,
+             "PresentationDelayForInteractiveFrames",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 // Record the delay from the system CVDisplayLink or CADisplaylink source to
 // CrGpuMain OnVSyncPresentation().
 void RecordVSyncCallbackDelay(base::TimeDelta delay) {
@@ -162,6 +166,12 @@
   bool delay_presenetation_until_next_vsync =
       features::IsVSyncAlignedPresentEnabled();
 
+  if (base::FeatureList::IsEnabled(kPresentationDelayForInteractiveFrames) &&
+      !ca_layer_tree_coordinator_->NumPendingSwaps() &&
+      !data.is_handling_interaction_or_animation) {
+    delay_presenetation_until_next_vsync = false;
+  }
+
   if (vsync_callback_mac_) {
     vsync_callback_mac_keep_alive_counter_ = kMaxKeepAliveCounter;
     if (delay_presenetation_until_next_vsync) {
@@ -260,8 +270,8 @@
 void ImageTransportSurfaceOverlayMacEGL::SetMaxPendingSwaps(
     int max_pending_swaps) {
 #if BUILDFLAG(IS_MAC)
-  cap_max_pending_swaps_ =
-      std::min(max_pending_swaps, features::NumPendingFrameSupported());
+  cap_max_pending_swaps_ = max_pending_swaps;
+
   // MaxCALayerTrees is equal to the number of max_pending_swaps + one
   // that has been displayed.
   ca_layer_tree_coordinator_->SetMaxCALayerTrees(cap_max_pending_swaps_ + 1);
diff --git a/gpu/ipc/service/shared_image_stub.cc b/gpu/ipc/service/shared_image_stub.cc
index 76ffd5e..91caf7f 100644
--- a/gpu/ipc/service/shared_image_stub.cc
+++ b/gpu/ipc/service/shared_image_stub.cc
@@ -151,13 +151,13 @@
     }
 
     case mojom::DeferredSharedImageRequest::Tag::kCreateSharedImagePool:
-      // Future CLs will add implementation.
-      NOTIMPLEMENTED();
+      OnCreateSharedImagePool(
+          std::move(request->get_create_shared_image_pool()));
       break;
 
     case mojom::DeferredSharedImageRequest::Tag::kDestroySharedImagePool:
-      // Future CLs will add implementation.
-      NOTIMPLEMENTED();
+      OnDestroySharedImagePool(
+          std::move(request->get_destroy_shared_image_pool()));
       break;
 
 #if BUILDFLAG(IS_WIN)
@@ -291,6 +291,31 @@
   }
 }
 
+void SharedImageStub::OnCreateSharedImagePool(
+    mojom::CreateSharedImagePoolParamsPtr params) {
+  TRACE_EVENT1("gpu", "SharedImageStub::OnCreateSharedImagePool", "pool_id",
+               params->pool_id.ToString());
+
+  if (!factory_->CreateSharedImagePool(params->pool_id,
+                                       std::move(params->client_remote))) {
+    LOG(ERROR) << "Unable to create SharedImagePool.";
+    OnError();
+    return;
+  }
+}
+
+void SharedImageStub::OnDestroySharedImagePool(
+    mojom::DestroySharedImagePoolParamsPtr params) {
+  TRACE_EVENT1("gpu", "SharedImageStub::OnDestroySharedImagePool", "pool_id",
+               params->pool_id.ToString());
+
+  if (!factory_->DestroySharedImagePool(params->pool_id)) {
+    LOG(ERROR) << "Unable to destroy SharedImagePool.";
+    OnError();
+    return;
+  }
+}
+
 void SharedImageStub::OnCreateSharedImageWithData(
     mojom::CreateSharedImageWithDataParamsPtr params) {
   TRACE_EVENT2("gpu", "SharedImageStub::OnCreateSharedImageWithData", "width",
diff --git a/gpu/ipc/service/shared_image_stub.h b/gpu/ipc/service/shared_image_stub.h
index ac10b9d..6b0e287 100644
--- a/gpu/ipc/service/shared_image_stub.h
+++ b/gpu/ipc/service/shared_image_stub.h
@@ -130,6 +130,9 @@
                              gfx::DXGIHandleToken dxgi_token);
 #endif  // BUILDFLAG(IS_WIN)
 
+  void OnCreateSharedImagePool(mojom::CreateSharedImagePoolParamsPtr params);
+  void OnDestroySharedImagePool(mojom::DestroySharedImagePoolParamsPtr params);
+
   ContextResult Initialize();
   void OnError();
 
diff --git a/infra/config/generated/builder-owners/chrome-sanitizer-builder-owners@google.com.txt b/infra/config/generated/builder-owners/chrome-sanitizer-builder-owners@google.com.txt
index cb372159..06f3371e 100644
--- a/infra/config/generated/builder-owners/chrome-sanitizer-builder-owners@google.com.txt
+++ b/infra/config/generated/builder-owners/chrome-sanitizer-builder-owners@google.com.txt
@@ -28,7 +28,6 @@
 ci/WebKit Linux MSAN
 ci/Win ASan Release
 ci/Win ASan Release Media
-ci/android-asan
 ci/chromeos-amd64-generic-asan-rel
 ci/ios-asan
 ci/win-asan
diff --git a/infra/config/generated/builders/ci/android-10-x86-fyi-rel/targets/chromium.android.fyi.json b/infra/config/generated/builders/ci/android-10-x86-fyi-rel/targets/chromium.android.fyi.json
index 68bfe4b..3ed70b5 100644
--- a/infra/config/generated/builders/ci/android-10-x86-fyi-rel/targets/chromium.android.fyi.json
+++ b/infra/config/generated/builders/ci/android-10-x86-fyi-rel/targets/chromium.android.fyi.json
@@ -1017,6 +1017,7 @@
         "args": [
           "--use-persistent-shell",
           "--avd-config=../../tools/android/avd/proto/android_29_google_apis_x86.textpb",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_10.content_unittests.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -2244,6 +2245,7 @@
         "args": [
           "--use-persistent-shell",
           "--avd-config=../../tools/android/avd/proto/android_29_google_apis_x86.textpb",
+          "--gtest_filter=-ScopedDirTest.CloseOutOfScope",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -2574,6 +2576,7 @@
         "args": [
           "--use-persistent-shell",
           "--avd-config=../../tools/android/avd/proto/android_29_google_apis_x86.textpb",
+          "--gtest_filter=-org.chromium.webview_shell.test.WebViewLayoutTest.*",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -3863,7 +3866,8 @@
         "args": [
           "--extra-browser-args=--enable-crashpad",
           "--use-persistent-shell",
-          "--avd-config=../../tools/android/avd/proto/android_29_google_apis_x86.textpb"
+          "--avd-config=../../tools/android/avd/proto/android_29_google_apis_x86.textpb",
+          "--browser=android-chromium"
         ],
         "isolate_profile_data": true,
         "merge": {
diff --git a/infra/config/generated/builders/ci/android-asan/gn-args.json b/infra/config/generated/builders/ci/android-asan/gn-args.json
deleted file mode 100644
index df5c21d..0000000
--- a/infra/config/generated/builders/ci/android-asan/gn-args.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "gn_args": {
-    "dcheck_always_on": false,
-    "debuggable_apks": false,
-    "ffmpeg_branding": "Chrome",
-    "is_asan": true,
-    "is_clang": true,
-    "is_component_build": false,
-    "is_debug": false,
-    "proprietary_codecs": true,
-    "strip_debug_info": true,
-    "symbol_level": 1,
-    "target_cpu": "arm",
-    "target_os": "android",
-    "use_remoteexec": true,
-    "use_siso": true
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-asan/properties.json b/infra/config/generated/builders/ci/android-asan/properties.json
deleted file mode 100644
index 25f9b5a..0000000
--- a/infra/config/generated/builders/ci/android-asan/properties.json
+++ /dev/null
@@ -1,76 +0,0 @@
-{
-  "$build/chromium_tests_builder_config": {
-    "builder_config": {
-      "additional_exclusions": [
-        "infra/config/generated/builders/ci/android-asan/gn-args.json"
-      ],
-      "builder_db": {
-        "entries": [
-          {
-            "builder_id": {
-              "bucket": "ci",
-              "builder": "android-asan",
-              "project": "chromium"
-            },
-            "builder_spec": {
-              "build_gs_bucket": "chromium-memory-archive",
-              "builder_group": "chromium.memory",
-              "execution_mode": "COMPILE_AND_TEST",
-              "legacy_android_config": {
-                "config": "main_builder"
-              },
-              "legacy_chromium_config": {
-                "apply_configs": [
-                  "mb"
-                ],
-                "build_config": "Release",
-                "config": "android_asan",
-                "target_bits": 64,
-                "target_platform": "android"
-              },
-              "legacy_gclient_config": {
-                "apply_configs": [
-                  "android"
-                ],
-                "config": "chromium"
-              }
-            }
-          }
-        ]
-      },
-      "builder_ids": [
-        {
-          "bucket": "ci",
-          "builder": "android-asan",
-          "project": "chromium"
-        }
-      ],
-      "retry_failed_shards": true,
-      "targets_spec_directory": "src/infra/config/generated/builders/ci/android-asan/targets"
-    }
-  },
-  "$build/reclient": {
-    "instance": "rbe-chromium-trusted",
-    "metrics_project": "chromium-reclient-metrics",
-    "scandeps_server": true
-  },
-  "$build/siso": {
-    "configs": [
-      "builder"
-    ],
-    "enable_cloud_profiler": true,
-    "enable_cloud_trace": true,
-    "experiments": [],
-    "project": "rbe-chromium-trusted",
-    "remote_jobs": 500
-  },
-  "$recipe_engine/resultdb/test_presentation": {
-    "column_keys": [],
-    "grouping_keys": [
-      "status",
-      "v.test_suite"
-    ]
-  },
-  "builder_group": "chromium.memory",
-  "recipe": "chromium"
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-asan/shadow-properties.json b/infra/config/generated/builders/ci/android-asan/shadow-properties.json
deleted file mode 100644
index 4325ef4..0000000
--- a/infra/config/generated/builders/ci/android-asan/shadow-properties.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "$build/reclient": {
-    "instance": "rbe-chromium-untrusted",
-    "metrics_project": "chromium-reclient-metrics",
-    "scandeps_server": true
-  },
-  "$build/siso": {
-    "configs": [
-      "builder"
-    ],
-    "enable_cloud_profiler": true,
-    "enable_cloud_trace": true,
-    "experiments": [],
-    "project": "rbe-chromium-untrusted",
-    "remote_jobs": 500
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-asan/targets/chromium.memory.json b/infra/config/generated/builders/ci/android-asan/targets/chromium.memory.json
deleted file mode 100644
index 8901822..0000000
--- a/infra/config/generated/builders/ci/android-asan/targets/chromium.memory.json
+++ /dev/null
@@ -1,1891 +0,0 @@
-{
-  "android-asan": {
-    "gtest_tests": [
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "absl_hardening_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "absl_hardening_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "absl_hardening_tests",
-        "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "android_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "android_browsertests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "android_browsertests",
-        "test_id_prefix": "ninja://chrome/test:android_browsertests/"
-      },
-      {
-        "args": [
-          "--test-launcher-batch-limit=1",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "android_sync_integration_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "android_sync_integration_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "android_sync_integration_tests",
-        "test_id_prefix": "ninja://chrome/test:android_sync_integration_tests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "android_webview_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "android_webview_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "android_webview_unittests",
-        "test_id_prefix": "ninja://android_webview/test:android_webview_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "base_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "base_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "base_unittests",
-        "test_id_prefix": "ninja://base:base_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "blink_common_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "blink_common_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_common_unittests",
-        "test_id_prefix": "ninja://third_party/blink/common:blink_common_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "blink_heap_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "blink_heap_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_heap_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform/heap:blink_heap_unittests/"
-      },
-      {
-        "args": [
-          "--git-revision=${got_revision}",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "blink_platform_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "blink_platform_unittests",
-        "precommit_args": [
-          "--gerrit-issue=${patch_issue}",
-          "--gerrit-patchset=${patch_set}",
-          "--buildbucket-id=${buildbucket_build_id}"
-        ],
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "blink_platform_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_platform_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "boringssl_crypto_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "boringssl_crypto_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "boringssl_crypto_tests",
-        "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "boringssl_ssl_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "boringssl_ssl_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "boringssl_ssl_tests",
-        "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/"
-      },
-      {
-        "args": [
-          "--gtest_filter=-*UsingRealWebcam*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "capture_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "capture_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "capture_unittests",
-        "test_id_prefix": "ninja://media/capture:capture_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "cast_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "cast_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "cast_unittests",
-        "test_id_prefix": "ninja://media/cast:cast_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "cc_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "cc_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "cc_unittests",
-        "test_id_prefix": "ninja://cc:cc_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "chrome_public_smoke_test"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "chrome_public_smoke_test",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "chrome_public_smoke_test",
-        "test_id_prefix": "ninja://chrome/android:chrome_public_smoke_test/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "components_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "components_browsertests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "components_browsertests",
-        "test_id_prefix": "ninja://components:components_browsertests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "components_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "components_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 6
-        },
-        "test": "components_unittests",
-        "test_id_prefix": "ninja://components:components_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/android.asan.content_browsertests.filter",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "content_browsertests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 25
-        },
-        "test": "content_browsertests",
-        "test_id_prefix": "ninja://content/test:content_browsertests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "content_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "content_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "content_unittests",
-        "test_id_prefix": "ninja://content/test:content_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "crashpad_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "crashpad_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "crashpad_tests",
-        "test_id_prefix": "ninja://third_party/crashpad/crashpad:crashpad_tests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "crypto_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "crypto_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "crypto_unittests",
-        "test_id_prefix": "ninja://crypto:crypto_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "device_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "device_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "device_unittests",
-        "test_id_prefix": "ninja://device:device_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "display_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "display_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "display_unittests",
-        "test_id_prefix": "ninja://ui/display:display_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "ci_only": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "env_chromium_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "env_chromium_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "env_chromium_unittests",
-        "test_id_prefix": "ninja://third_party/leveldatabase:env_chromium_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "events_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "events_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "events_unittests",
-        "test_id_prefix": "ninja://ui/events:events_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "fuzzing_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "fuzzing_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "fuzzing_unittests",
-        "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "gcm_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "gcm_unit_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gcm_unit_tests",
-        "test_id_prefix": "ninja://google_apis/gcm:gcm_unit_tests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "gfx_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "gfx_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gfx_unittests",
-        "test_id_prefix": "ninja://ui/gfx:gfx_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "gin_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "gin_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gin_unittests",
-        "test_id_prefix": "ninja://gin:gin_unittests/"
-      },
-      {
-        "args": [
-          "--use-cmd-decoder=validating",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "gl_tests_validating"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "gl_tests_validating",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gl_tests",
-        "test_id_prefix": "ninja://gpu:gl_tests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "gl_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "gl_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gl_unittests",
-        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "google_apis_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "google_apis_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "google_apis_unittests",
-        "test_id_prefix": "ninja://google_apis:google_apis_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "gpu_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "gpu_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gpu_unittests",
-        "test_id_prefix": "ninja://gpu:gpu_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "gwp_asan_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "gwp_asan_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "gwp_asan_unittests",
-        "test_id_prefix": "ninja://components/gwp_asan:gwp_asan_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "ipc_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "ipc_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "ipc_tests",
-        "test_id_prefix": "ninja://ipc:ipc_tests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "latency_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "latency_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "latency_unittests",
-        "test_id_prefix": "ninja://ui/latency:latency_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "ci_only": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "leveldb_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "leveldb_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "leveldb_unittests",
-        "test_id_prefix": "ninja://third_party/leveldatabase:leveldb_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "libjingle_xmpp_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "libjingle_xmpp_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "libjingle_xmpp_unittests",
-        "test_id_prefix": "ninja://third_party/libjingle_xmpp:libjingle_xmpp_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "liburlpattern_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "liburlpattern_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "liburlpattern_unittests",
-        "test_id_prefix": "ninja://third_party/liburlpattern:liburlpattern_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "media_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "media_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "media_unittests",
-        "test_id_prefix": "ninja://media:media_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "midi_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "midi_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "midi_unittests",
-        "test_id_prefix": "ninja://media/midi:midi_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "mojo_test_apk"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "mojo_test_apk",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "mojo_test_apk",
-        "test_id_prefix": "ninja://mojo/public/java/system:mojo_test_apk/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "mojo_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "mojo_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 5
-        },
-        "test": "mojo_unittests",
-        "test_id_prefix": "ninja://mojo:mojo_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "net_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "net_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "net_unittests",
-        "test_id_prefix": "ninja://net:net_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "services_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "services_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "services_unittests",
-        "test_id_prefix": "ninja://services:services_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "shell_dialogs_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "shell_dialogs_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "shell_dialogs_unittests",
-        "test_id_prefix": "ninja://ui/shell_dialogs:shell_dialogs_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "skia_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "skia_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "skia_unittests",
-        "test_id_prefix": "ninja://skia:skia_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "sql_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "sql_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "sql_unittests",
-        "test_id_prefix": "ninja://sql:sql_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "storage_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "storage_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "storage_unittests",
-        "test_id_prefix": "ninja://storage:storage_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "ui_android_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "ui_android_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ui_android_unittests",
-        "test_id_prefix": "ninja://ui/android:ui_android_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "ui_base_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "ui_base_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ui_base_unittests",
-        "test_id_prefix": "ninja://ui/base:ui_base_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "ui_touch_selection_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "ui_touch_selection_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "ui_touch_selection_unittests",
-        "test_id_prefix": "ninja://ui/touch_selection:ui_touch_selection_unittests/"
-      },
-      {
-        "args": [
-          "--test-launcher-filter-file=../../testing/buildbot/filters/android.asan.unit_tests.filter",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "unit_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 2
-        },
-        "test": "unit_tests",
-        "test_id_prefix": "ninja://chrome/test:unit_tests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "url_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "url_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "url_unittests",
-        "test_id_prefix": "ninja://url:url_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "viz_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "viz_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "viz_unittests",
-        "test_id_prefix": "ninja://components/viz:viz_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "vr_android_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "vr_android_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "vr_android_unittests",
-        "test_id_prefix": "ninja://chrome/browser/android/vr:vr_android_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "vr_common_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "vr_common_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "vr_common_unittests",
-        "test_id_prefix": "ninja://chrome/browser/vr:vr_common_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "webkit_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "webkit_unit_tests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 6
-        },
-        "test": "blink_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/controller:blink_unittests/"
-      },
-      {
-        "args": [
-          "--webview-process-mode=single",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "webview_instrumentation_test_apk_single_process_mode"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "webview_instrumentation_test_apk_single_process_mode",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 3
-        },
-        "test": "webview_instrumentation_test_apk",
-        "test_id_prefix": "ninja://android_webview/test:webview_instrumentation_test_apk/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "wtf_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "wtf_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "wtf_unittests",
-        "test_id_prefix": "ninja://third_party/blink/renderer/platform/wtf:wtf_unittests/"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "zlib_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "zlib_unittests",
-        "resultdb": {
-          "enable": true,
-          "has_native_resultdb_integration": true
-        },
-        "swarming": {
-          "dimensions": {
-            "device_os": "N2G48C",
-            "device_os_type": "userdebug",
-            "device_type": "bullhead",
-            "os": "Android"
-          },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
-        },
-        "test": "zlib_unittests",
-        "test_id_prefix": "ninja://third_party/zlib:zlib_unittests/"
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/infra/config/generated/builders/gn_args_locations.json b/infra/config/generated/builders/gn_args_locations.json
index e6ce5cd1..7f65fe5 100644
--- a/infra/config/generated/builders/gn_args_locations.json
+++ b/infra/config/generated/builders/gn_args_locations.json
@@ -418,7 +418,6 @@
     "WebKit Linux ASAN": "ci/WebKit Linux ASAN/gn-args.json",
     "WebKit Linux Leak": "ci/WebKit Linux Leak/gn-args.json",
     "WebKit Linux MSAN": "ci/WebKit Linux MSAN/gn-args.json",
-    "android-asan": "ci/android-asan/gn-args.json",
     "ios-asan": "ci/ios-asan/gn-args.json",
     "win-asan": "ci/win-asan/gn-args.json"
   },
diff --git a/infra/config/generated/builders/try/android-10-x86-fyi-rel/targets/chromium.android.fyi.json b/infra/config/generated/builders/try/android-10-x86-fyi-rel/targets/chromium.android.fyi.json
index 68bfe4b..3ed70b5 100644
--- a/infra/config/generated/builders/try/android-10-x86-fyi-rel/targets/chromium.android.fyi.json
+++ b/infra/config/generated/builders/try/android-10-x86-fyi-rel/targets/chromium.android.fyi.json
@@ -1017,6 +1017,7 @@
         "args": [
           "--use-persistent-shell",
           "--avd-config=../../tools/android/avd/proto/android_29_google_apis_x86.textpb",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_10.content_unittests.filter",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -2244,6 +2245,7 @@
         "args": [
           "--use-persistent-shell",
           "--avd-config=../../tools/android/avd/proto/android_29_google_apis_x86.textpb",
+          "--gtest_filter=-ScopedDirTest.CloseOutOfScope",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -2574,6 +2576,7 @@
         "args": [
           "--use-persistent-shell",
           "--avd-config=../../tools/android/avd/proto/android_29_google_apis_x86.textpb",
+          "--gtest_filter=-org.chromium.webview_shell.test.WebViewLayoutTest.*",
           "--gs-results-bucket=chromium-result-details",
           "--recover-devices"
         ],
@@ -3863,7 +3866,8 @@
         "args": [
           "--extra-browser-args=--enable-crashpad",
           "--use-persistent-shell",
-          "--avd-config=../../tools/android/avd/proto/android_29_google_apis_x86.textpb"
+          "--avd-config=../../tools/android/avd/proto/android_29_google_apis_x86.textpb",
+          "--browser=android-chromium"
         ],
         "isolate_profile_data": true,
         "merge": {
diff --git a/infra/config/generated/health-specs/health-specs.json b/infra/config/generated/health-specs/health-specs.json
index 90c7620..01649866 100644
--- a/infra/config/generated/health-specs/health-specs.json
+++ b/infra/config/generated/health-specs/health-specs.json
@@ -6974,27 +6974,6 @@
           }
         ]
       },
-      "android-asan": {
-        "contact_team_email": "chrome-sanitizer-builder-owners@google.com",
-        "problem_specs": [
-          {
-            "name": "Unhealthy",
-            "period_days": 7,
-            "score": 5,
-            "thresholds": {
-              "_default": "_default"
-            }
-          },
-          {
-            "name": "Low Value",
-            "period_days": 90,
-            "score": 1,
-            "thresholds": {
-              "_default": "_default"
-            }
-          }
-        ]
-      },
       "android-avd-packager": {
         "problem_specs": [
           {
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg
index b72a993..e4fb002 100644
--- a/infra/config/generated/luci/cr-buildbucket.cfg
+++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -39916,116 +39916,6 @@
       }
     }
     builders {
-      name: "android-asan"
-      swarming_host: "chromium-swarm.appspot.com"
-      dimensions: "builderless:1"
-      dimensions: "cores:8"
-      dimensions: "cpu:x86-64"
-      dimensions: "free_space:standard"
-      dimensions: "os:Ubuntu-22.04"
-      dimensions: "pool:luci.chromium.ci"
-      dimensions: "ssd:0"
-      exe {
-        cipd_package: "infra/chromium/bootstrapper/${platform}"
-        cipd_version: "latest"
-        cmd: "bootstrapper"
-      }
-      properties:
-        '{'
-        '  "$bootstrap/exe": {'
-        '    "exe": {'
-        '      "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",'
-        '      "cipd_version": "refs/heads/main",'
-        '      "cmd": ['
-        '        "luciexe"'
-        '      ]'
-        '    }'
-        '  },'
-        '  "$bootstrap/properties": {'
-        '    "properties_file": "infra/config/generated/builders/ci/android-asan/properties.json",'
-        '    "shadow_properties_file": "infra/config/generated/builders/ci/android-asan/shadow-properties.json",'
-        '    "top_level_project": {'
-        '      "ref": "refs/heads/main",'
-        '      "repo": {'
-        '        "host": "chromium.googlesource.com",'
-        '        "project": "chromium/src"'
-        '      }'
-        '    }'
-        '  },'
-        '  "builder_group": "chromium.memory",'
-        '  "led_builder_is_bootstrapped": true,'
-        '  "recipe": "chromium"'
-        '}'
-      execution_timeout_secs: 10800
-      build_numbers: YES
-      service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
-      experiments {
-        key: "chromium.use_per_builder_build_dir_name"
-        value: 100
-      }
-      experiments {
-        key: "luci.recipes.use_python3"
-        value: 100
-      }
-      resultdb {
-        enable: true
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "ci_test_results"
-          test_results {}
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "gpu_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+"
-            }
-          }
-        }
-        bq_exports {
-          project: "chrome-luci-data"
-          dataset: "chromium"
-          table: "blink_web_tests_ci_test_results"
-          test_results {
-            predicate {
-              test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)|(ninja://[^/]*headless_shell_wpt/.+)"
-            }
-          }
-        }
-        history_options {
-          use_invocation_timestamp: true
-        }
-      }
-      shadow_builder_adjustments {
-        service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com"
-        pool: "luci.chromium.try"
-        dimensions: "free_space:"
-        dimensions: "pool:luci.chromium.try"
-      }
-      contact_team_email: "chrome-sanitizer-builder-owners@google.com"
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/cached_count"
-        predicates: "has(build.output.properties.is_cached)"
-        predicates: "string(build.output.properties.is_cached) == \"true\""
-      }
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count"
-        predicates: "has(build.output.properties.ran_tests_retry_shard)"
-      }
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/ran_tests_without_patch_count"
-        predicates: "has(build.output.properties.ran_tests_without_patch)"
-      }
-      custom_metric_definitions {
-        name: "/chrome/infra/browser/builds/uncached_count"
-        predicates: "has(build.output.properties.is_cached)"
-        predicates: "string(build.output.properties.is_cached) == \"false\""
-      }
-    }
-    builders {
       name: "android-avd-packager"
       swarming_host: "chromium-swarm.appspot.com"
       dimensions: "builderless:1"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 48e7493..f3d2987 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -988,11 +988,6 @@
     short_name: "tst"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/android-asan"
-    category: "chromium.memory|android"
-    short_name: "asn"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/Linux CFI"
     category: "chromium.memory|cfi"
     short_name: "lnx"
@@ -13137,11 +13132,6 @@
     short_name: "tst"
   }
   builders {
-    name: "buildbucket/luci.chromium.ci/android-asan"
-    category: "android"
-    short_name: "asn"
-  }
-  builders {
     name: "buildbucket/luci.chromium.ci/Linux CFI"
     category: "cfi"
     short_name: "lnx"
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg
index 5ed3972a..17c96766 100644
--- a/infra/config/generated/luci/luci-scheduler.cfg
+++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -3492,15 +3492,6 @@
   }
 }
 job {
-  id: "android-asan"
-  realm: "ci"
-  buildbucket {
-    server: "cr-buildbucket.appspot.com"
-    bucket: "ci"
-    builder: "android-asan"
-  }
-}
-job {
   id: "android-avd-packager"
   realm: "ci"
   schedule: "triggered"
@@ -6733,7 +6724,6 @@
   triggers: "android-archive-rel"
   triggers: "android-arm64-archive-rel"
   triggers: "android-arm64-proguard-rel"
-  triggers: "android-asan"
   triggers: "android-bfcache-rel"
   triggers: "android-binary-size-generator"
   triggers: "android-cast-arm-dbg"
diff --git a/infra/config/lib/builder_exemptions.star b/infra/config/lib/builder_exemptions.star
index 129633c..8d102c6 100644
--- a/infra/config/lib/builder_exemptions.star
+++ b/infra/config/lib/builder_exemptions.star
@@ -275,7 +275,6 @@
         "android-archive-rel",
         "android-arm64-archive-rel",
         "android-arm64-proguard-rel",
-        "android-asan",
         "android-avd-packager",
         "android-bfcache-rel",
         "android-binary-size-generator",
@@ -519,7 +518,6 @@
         "android-arm-compile-dbg",
         "android-arm64-all-targets-dbg",
         "android-arm64-rel-compilator",
-        "android-asan-compile-dbg",
         "android-bfcache-rel",
         "android-binary-size",
         "android-chrome-pie-x86-wpt-fyi-rel",
@@ -1067,7 +1065,6 @@
         "android-arm64-all-targets-dbg",
         "android-arm64-rel",
         "android-arm64-rel-compilator",
-        "android-asan-compile-dbg",
         "android-bfcache-rel",
         "android-binary-size",
         "android-chrome-pie-x86-wpt-fyi-rel",
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
index 3f11d58..e4f67d3c 100644
--- a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
@@ -555,11 +555,35 @@
                     shards = 6,
                 ),
             ),
+            "content_unittests": targets.mixin(
+                args = [
+                    "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_10.content_unittests.filter",
+                ],
+            ),
+            "perfetto_unittests": targets.mixin(
+                args = [
+                    # TODO(crbug.com/40201873): Fix the failed test
+                    "--gtest_filter=-ScopedDirTest.CloseOutOfScope",
+                ],
+            ),
             "services_unittests": targets.mixin(
                 swarming = targets.swarming(
                     shards = 3,
                 ),
             ),
+            "system_webview_shell_layout_test_apk": targets.mixin(
+                args = [
+                    # TODO(crbug.com/390676579): Fix the failed test
+                    "--gtest_filter=-org.chromium.webview_shell.test.WebViewLayoutTest.*",
+                ],
+            ),
+            "telemetry_perf_unittests_android_chrome": targets.mixin(
+                # For whatever reason, automatic browser selection on this bot chooses
+                # webview instead of the full browser, so explicitly specify it here.
+                args = [
+                    "--browser=android-chromium",
+                ],
+            ),
         },
     ),
     targets_settings = targets.settings(
diff --git a/infra/config/subprojects/chromium/ci/chromium.memory.star b/infra/config/subprojects/chromium/ci/chromium.memory.star
index 02858cb2..1a9ebc5 100644
--- a/infra/config/subprojects/chromium/ci/chromium.memory.star
+++ b/infra/config/subprojects/chromium/ci/chromium.memory.star
@@ -1266,116 +1266,6 @@
 )
 
 ci.builder(
-    name = "android-asan",
-    builder_spec = builder_config.builder_spec(
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = ["android"],
-        ),
-        chromium_config = builder_config.chromium_config(
-            config = "android_asan",
-            apply_configs = ["mb"],
-            build_config = builder_config.build_config.RELEASE,
-            target_bits = 64,
-            target_platform = builder_config.target_platform.ANDROID,
-        ),
-        android_config = builder_config.android_config(config = "main_builder"),
-        build_gs_bucket = "chromium-memory-archive",
-    ),
-    gn_args = gn_args.config(
-        configs = [
-            "android_builder",
-            "clang",
-            "asan",
-            "release_builder",
-            "remoteexec",
-            "strip_debug_info",
-            "minimal_symbols",
-            "arm",
-        ],
-    ),
-    targets = targets.bundle(
-        targets = [
-            "chromium_android_gtests",
-        ],
-        mixins = [
-            "has_native_resultdb_integration",
-            "bullhead",
-            "nougat",
-        ],
-        per_test_modifications = {
-            "android_browsertests": targets.mixin(
-                swarming = targets.swarming(
-                    shards = 2,
-                ),
-            ),
-            "angle_unittests": targets.remove(
-                reason = "Times out listing tests crbug.com/1167314",
-            ),
-            "chrome_public_test_apk": targets.remove(
-                reason = "https://crbug.com/964562",
-            ),
-            "chrome_public_test_vr_apk": targets.remove(
-                reason = "https://crbug.com/964562",
-            ),
-            "chrome_public_unit_test_apk": targets.remove(
-                reason = "https://crbug.com/964562",
-            ),
-            "components_browsertests": targets.mixin(
-                swarming = targets.swarming(
-                    shards = 3,
-                ),
-            ),
-            "content_browsertests": targets.mixin(
-                args = [
-                    "--test-launcher-filter-file=../../testing/buildbot/filters/android.asan.content_browsertests.filter",
-                ],
-                swarming = targets.swarming(
-                    shards = 25,
-                ),
-            ),
-            "content_shell_test_apk": targets.remove(
-                reason = "https://crbug.com/964562",
-            ),
-            "ipc_tests": targets.mixin(
-                swarming = targets.swarming(
-                    shards = 2,
-                ),
-            ),
-            "mojo_unittests": targets.mixin(
-                swarming = targets.swarming(
-                    shards = 5,
-                ),
-            ),
-            "perfetto_unittests": targets.remove(
-                reason = "TODO(crbug.com/41440830): Fix permission issue when creating tmp files",
-            ),
-            "sandbox_linux_unittests": targets.remove(
-                reason = "https://crbug.com/962650",
-            ),
-            "unit_tests": targets.mixin(
-                args = [
-                    "--test-launcher-filter-file=../../testing/buildbot/filters/android.asan.unit_tests.filter",
-                ],
-            ),
-            "webview_instrumentation_test_apk_multiple_process_mode": targets.remove(
-                reason = "https://crbug.com/964562",
-            ),
-        },
-    ),
-    targets_settings = targets.settings(
-        os_type = targets.os_type.ANDROID,
-    ),
-    os = os.LINUX_DEFAULT,
-    gardener_rotations = args.ignore_default(None),
-    tree_closing = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "android",
-        short_name = "asn",
-    ),
-)
-
-ci.builder(
     name = "win-asan",
     builder_spec = builder_config.builder_spec(
         gclient_config = builder_config.gclient_config(
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
index 3797aa8..1e93334a 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -421,20 +421,6 @@
     siso_remote_jobs = siso.remote_jobs.LOW_JOBS_FOR_CQ,
 )
 
-# TODO(crbug.com/40240078): Reenable this builder once the reboot issue is resolved.
-# try_.builder(
-#     name = "android-asan",
-#     mirrors = ["ci/android-asan"],
-#     gn_args = gn_args.config(
-#         configs = [
-#             "ci/android-asan",
-#             "release_try_builder",
-#             "minimal_symbols",
-#         ],
-#     ),
-#     siso_remote_jobs = siso.remote_jobs.LOW_JOBS_FOR_CQ,
-# )
-
 try_.builder(
     name = "android-bfcache-rel",
     mirrors = [
diff --git a/internal b/internal
index 7f38ae2..014be7b 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 7f38ae2436442ada9c5c9c059547f64e6961de04
+Subproject commit 014be7b57c088d653120c5997fea33b52107a768
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index d3f7bf10..fdf06e4 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1201,8 +1201,14 @@
       <message name="IDS_IOS_CONTENT_SUGGESTIONS_HISTORY" desc="The History title on the new tab page [Length: 10em]">
         History
       </message>
+      <message name="IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_CONTEXT_MENU_DESCRIPTION" desc="Descripiton for the long-press context menu of the module that presents the most visite sites.">
+        This card shows your most visited sites.
+      </message>
+      <message name="IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_HIDE_CARD" desc="Button text indicating most visited sites module can be removed">
+        Hide "Your Top Sites"
+      </message>
       <message name="IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_TITLE" desc="The Most Visited module title on the new tab page [Length: 10em]">
-        Most visited sites
+        Your Top Sites
       </message>
       <message name="IDS_IOS_CONTENT_SUGGESTIONS_PARCEL_TRACKING_MODULE_PACKAGE_ARRIVING_STATUS" desc="The Parcel Tracking module package arriving status description on the Home surface [Length: 10em]">
         Arriving <ph name="Date"><ex>Monday, September 13</ex>$1</ph>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_CONTEXT_MENU_DESCRIPTION.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_CONTEXT_MENU_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..2e77b10
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_CONTEXT_MENU_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+470616ac06e7e3c8729c85a70a61c8804dd0942c
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_HIDE_CARD.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_HIDE_CARD.png.sha1
new file mode 100644
index 0000000..2e77b10
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_HIDE_CARD.png.sha1
@@ -0,0 +1 @@
+470616ac06e7e3c8729c85a70a61c8804dd0942c
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_TITLE.png.sha1
index 99e5595..eea4f44 100644
--- a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_TITLE.png.sha1
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_TITLE.png.sha1
@@ -1 +1 @@
-1d167d888ef24a35629dcbf6599466ccd9b8fcd6
\ No newline at end of file
+dee0b4d80f93e3c3cbdb4d8610503e388e942f89
\ No newline at end of file
diff --git a/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.mm b/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.mm
index 88b23a04..d6e609e 100644
--- a/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.mm
+++ b/ios/chrome/browser/autofill/model/bottom_sheet/autofill_bottom_sheet_tab_helper.mm
@@ -539,7 +539,10 @@
     autofill::FormGlobalId form_id,
     bool only_new) {
   autofill::FormStructure* form_structure = manager.FindCachedFormById(form_id);
-  if (!form_structure || !form_structure->IsCompleteCreditCardForm()) {
+  if (!form_structure ||
+      !form_structure->IsCompleteCreditCardForm(
+          autofill::FormStructure::CreditCardFormCompleteness::
+              kCompleteCreditCardForm)) {
     return;
   }
   if (manager.client()
diff --git a/ios/chrome/browser/credential_provider/model/BUILD.gn b/ios/chrome/browser/credential_provider/model/BUILD.gn
index ab051cc3..903b1ac 100644
--- a/ios/chrome/browser/credential_provider/model/BUILD.gn
+++ b/ios/chrome/browser/credential_provider/model/BUILD.gn
@@ -55,6 +55,7 @@
       "//ios/chrome/browser/shared/model/profile:profile_keyed_service_factory",
       "//ios/chrome/browser/shared/model/web_state_list",
       "//ios/chrome/browser/shared/public/commands",
+      "//ios/chrome/browser/shared/public/features",
       "//ios/chrome/browser/shared/ui/symbols",
       "//ios/chrome/browser/signin/model",
       "//ios/chrome/browser/signin/model:system_identity",
@@ -107,6 +108,7 @@
       "//ios/chrome/browser/shared/model/profile/test",
       "//ios/chrome/browser/shared/model/web_state_list",
       "//ios/chrome/browser/shared/public/commands",
+      "//ios/chrome/browser/shared/public/features",
       "//ios/chrome/browser/signin/model:fake_system_identity",
       "//ios/chrome/browser/signin/model:fake_system_identity_manager",
       "//ios/chrome/browser/signin/model:test_support",
diff --git a/ios/chrome/browser/credential_provider/model/credential_provider_service.h b/ios/chrome/browser/credential_provider/model/credential_provider_service.h
index a573d0d..8217f804 100644
--- a/ios/chrome/browser/credential_provider/model/credential_provider_service.h
+++ b/ios/chrome/browser/credential_provider/model/credential_provider_service.h
@@ -141,6 +141,9 @@
   // Syncs whether or not PRF is enabled.
   void UpdatePasskeyPRFSetting();
 
+  // Syncs whether or not the Passkeys M2 feature is enabled.
+  void UpdatePasskeysM2Availability();
+
   // PasswordStoreConsumer:
   void OnGetPasswordStoreResultsOrErrorFrom(
       password_manager::PasswordStoreInterface* store,
@@ -217,7 +220,7 @@
 
   // The preference associated with
   // password_manager::prefs::kCredentialsEnablePasskeys. See
-  // `AppGroupUserDefaulsCredentialProviderSavingPasskeysEnabled` documentation
+  // `AppGroupUserDefaultsCredentialProviderSavingPasskeysEnabled` documentation
   // for important caveats.
   BooleanPrefMember saving_passkeys_enabled_;
 
diff --git a/ios/chrome/browser/credential_provider/model/credential_provider_service.mm b/ios/chrome/browser/credential_provider/model/credential_provider_service.mm
index dcb5122..128ee7a 100644
--- a/ios/chrome/browser/credential_provider/model/credential_provider_service.mm
+++ b/ios/chrome/browser/credential_provider/model/credential_provider_service.mm
@@ -30,6 +30,7 @@
 #import "ios/chrome/browser/credential_provider/model/archivable_credential+password_form.h"
 #import "ios/chrome/browser/credential_provider/model/credential_provider_util.h"
 #import "ios/chrome/browser/credential_provider/model/features.h"
+#import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/signin/model/system_identity.h"
 #import "ios/chrome/common/app_group/app_group_constants.h"
 #import "ios/chrome/common/credential_provider/ASPasskeyCredentialIdentity+credential.h"
@@ -216,6 +217,7 @@
   OnPrefOrPolicyStatusChanged();
   UpdatePasswordSyncSetting();
   UpdatePasskeyPRFSetting();
+  UpdatePasskeysM2Availability();
 }
 
 CredentialProviderService::~CredentialProviderService() {}
@@ -543,6 +545,12 @@
          forKey:AppGroupUserDefaulsCredentialProviderPasskeyPRFEnabled()];
 }
 
+void CredentialProviderService::UpdatePasskeysM2Availability() {
+  [app_group::GetGroupUserDefaults()
+      setObject:[NSNumber numberWithBool:IOSPasskeysM2Enabled()]
+         forKey:AppGroupUserDefaultsCredentialProviderPasskeysM2Enabled()];
+}
+
 void CredentialProviderService::OnGetPasswordStoreResultsOrErrorFrom(
     password_manager::PasswordStoreInterface* store,
     password_manager::LoginsResultOrError results) {
@@ -644,13 +652,13 @@
 void CredentialProviderService::OnPrefOrPolicyStatusChanged() {
   [app_group::GetGroupUserDefaults()
       setObject:[NSNumber numberWithBool:saving_passwords_enabled_.GetValue()]
-         forKey:AppGroupUserDefaulsCredentialProviderSavingPasswordsEnabled()];
+         forKey:AppGroupUserDefaultsCredentialProviderSavingPasswordsEnabled()];
   [app_group::GetGroupUserDefaults()
       setObject:[NSNumber numberWithBool:saving_passwords_enabled_.IsManaged()]
-         forKey:AppGroupUserDefaulsCredentialProviderSavingPasswordsManaged()];
+         forKey:AppGroupUserDefaultsCredentialProviderSavingPasswordsManaged()];
   [app_group::GetGroupUserDefaults()
       setObject:[NSNumber numberWithBool:saving_passkeys_enabled_.GetValue()]
-         forKey:AppGroupUserDefaulsCredentialProviderSavingPasskeysEnabled()];
+         forKey:AppGroupUserDefaultsCredentialProviderSavingPasskeysEnabled()];
 }
 
 MemoryCredentialStore* CredentialProviderService::GetCredentialStore(
diff --git a/ios/chrome/browser/credential_provider/model/credential_provider_service_unittest.mm b/ios/chrome/browser/credential_provider/model/credential_provider_service_unittest.mm
index 38cb750..a74ba0f 100644
--- a/ios/chrome/browser/credential_provider/model/credential_provider_service_unittest.mm
+++ b/ios/chrome/browser/credential_provider/model/credential_provider_service_unittest.mm
@@ -34,6 +34,7 @@
 #import "ios/chrome/browser/credential_provider/model/features.h"
 #import "ios/chrome/browser/favicon/model/favicon_loader.h"
 #import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
+#import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/common/app_group/app_group_constants.h"
 #import "ios/chrome/common/credential_provider/constants.h"
 #import "ios/chrome/common/credential_provider/credential.h"
@@ -170,6 +171,11 @@
   }
 
   void CreateCredentialProviderService(bool with_account_store = false) {
+    // Make sure to shut down the previous instance before creating a new one.
+    if (credential_provider_service_) {
+      credential_provider_service_->Shutdown();
+    }
+
     credential_provider_service_ = std::make_unique<CredentialProviderService>(
         &testing_pref_service_, password_store_,
         with_account_store ? account_password_store_ : nullptr,
@@ -355,7 +361,7 @@
   // NSUserDefaults value is also true.
   EXPECT_TRUE([[app_group::GetGroupUserDefaults()
       objectForKey:
-          AppGroupUserDefaulsCredentialProviderSavingPasswordsEnabled()]
+          AppGroupUserDefaultsCredentialProviderSavingPasswordsEnabled()]
       boolValue]);
 
   // Change the pref value to false.
@@ -365,7 +371,7 @@
   // Make sure the NSUserDefaults value is now false.
   EXPECT_FALSE([[app_group::GetGroupUserDefaults()
       objectForKey:
-          AppGroupUserDefaulsCredentialProviderSavingPasswordsEnabled()]
+          AppGroupUserDefaultsCredentialProviderSavingPasswordsEnabled()]
       boolValue]);
 }
 
@@ -429,6 +435,32 @@
       stringForKey:AppGroupUserDefaultsCredentialProviderUserEmail()]);
 }
 
+// Tests that the CredentialProviderService correctly stores the enabled state
+// of the Passkeys M2 feature.
+TEST_F(CredentialProviderServiceTest, PasskeysM2Availability) {
+  {
+    // Enable the `kIOSPasskeysM2` feature.
+    base::test::ScopedFeatureList feature_list(kIOSPasskeysM2);
+
+    CreateCredentialProviderService();
+
+    EXPECT_TRUE([[app_group::GetGroupUserDefaults()
+        objectForKey:AppGroupUserDefaultsCredentialProviderPasskeysM2Enabled()]
+        boolValue]);
+  }
+  {
+    // Disable the `kIOSPasskeysM2` feature.
+    base::test::ScopedFeatureList feature_list;
+    feature_list.InitAndDisableFeature(kIOSPasskeysM2);
+
+    CreateCredentialProviderService();
+
+    EXPECT_FALSE([[app_group::GetGroupUserDefaults()
+        objectForKey:AppGroupUserDefaultsCredentialProviderPasskeysM2Enabled()]
+        boolValue]);
+  }
+}
+
 TEST_F(CredentialProviderServiceTest, AddCredentialsWithValidURL) {
   CreateCredentialProviderService();
 
diff --git a/ios/chrome/browser/iph_for_new_chrome_user/model/BUILD.gn b/ios/chrome/browser/iph_for_new_chrome_user/model/BUILD.gn
index 57d6b419..9c3074d 100644
--- a/ios/chrome/browser/iph_for_new_chrome_user/model/BUILD.gn
+++ b/ios/chrome/browser/iph_for_new_chrome_user/model/BUILD.gn
@@ -14,11 +14,13 @@
     "//base",
     "//components/bookmarks/browser",
     "//components/feature_engagement/public",
+    "//components/reading_list/core",
     "//components/segmentation_platform/embedder/default_model",
     "//components/segmentation_platform/public",
     "//components/send_tab_to_self",
     "//ios/chrome/browser/bookmarks/model",
     "//ios/chrome/browser/feature_engagement/model",
+    "//ios/chrome/browser/reading_list/model",
     "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/url:constants",
     "//ios/chrome/browser/shared/model/utils",
diff --git a/ios/chrome/browser/iph_for_new_chrome_user/model/DEPS b/ios/chrome/browser/iph_for_new_chrome_user/model/DEPS
index 7258d58c..86db2e2 100644
--- a/ios/chrome/browser/iph_for_new_chrome_user/model/DEPS
+++ b/ios/chrome/browser/iph_for_new_chrome_user/model/DEPS
@@ -2,5 +2,6 @@
   "+ios/chrome/browser/bookmarks/model",
   "+ios/chrome/browser/first_run/model",
   "+ios/chrome/browser/feature_engagement/model",
+  "+ios/chrome/browser/reading_list/model",
   "+ios/chrome/browser/url_loading/model",
 ]
diff --git a/ios/chrome/browser/iph_for_new_chrome_user/model/tab_based_iph_browser_agent.h b/ios/chrome/browser/iph_for_new_chrome_user/model/tab_based_iph_browser_agent.h
index 09eab1b..2014108 100644
--- a/ios/chrome/browser/iph_for_new_chrome_user/model/tab_based_iph_browser_agent.h
+++ b/ios/chrome/browser/iph_for_new_chrome_user/model/tab_based_iph_browser_agent.h
@@ -8,6 +8,7 @@
 #import "base/memory/raw_ptr.h"
 #import "base/scoped_observation.h"
 #import "components/bookmarks/browser/base_bookmark_model_observer.h"
+#import "components/reading_list/core/reading_list_model_observer.h"
 #import "ios/chrome/browser/shared/model/browser/browser_observer.h"
 #import "ios/chrome/browser/shared/model/browser/browser_user_data.h"
 #import "ios/chrome/browser/shared/model/web_state_list/active_web_state_observation_forwarder.h"
@@ -18,9 +19,14 @@
 class BookmarkNode;
 }  // namespace bookmarks
 
+namespace reading_list {
+enum EntrySource;
+}  // namespace reading_list
+
 class Browser;
 @class CommandDispatcher;
 @protocol HelpCommands;
+class ReadingListModel;
 class UrlLoadingNotifierBrowserAgent;
 class WebStateList;
 
@@ -33,6 +39,7 @@
 class TabBasedIPHBrowserAgent : public bookmarks::BaseBookmarkModelObserver,
                                 public BrowserUserData<TabBasedIPHBrowserAgent>,
                                 public BrowserObserver,
+                                public ReadingListModelObserver,
                                 public UrlLoadingObserver,
                                 public web::WebStateObserver {
  public:
@@ -81,6 +88,13 @@
   // BrowserObserver
   void BrowserDestroyed(Browser* browser) override;
 
+  // ReadingListModelObserver
+  void ReadingListModelLoaded(const ReadingListModel* model) override;
+  void ReadingListModelBeingShutdown(const ReadingListModel* model) override;
+  void ReadingListDidAddEntry(const ReadingListModel* model,
+                              const GURL& url,
+                              reading_list::EntrySource source) override;
+
   // UrlLoadingObserver
   void TabDidLoadUrl(const GURL& url,
                      ui::PageTransition transition_type) override;
@@ -109,6 +123,12 @@
   // longer react to bookmark modifications.
   void StopObservingBookmarkModel();
 
+  // Stops observing changes to the reading list model. This is typically
+  // called when `TabBasedIPHBrowserAgent` is being destroyed, when reactions
+  // to reading list modifications are no longer needed, or when the reading
+  // list model itself is being destroyed.
+  void StopObservingReadingListModel();
+
   // For all IPH features managed by this class, resets their tracker variables
   // to `false`, and remove currently displaying IPH views from the view.
   void ResetFeatureStatesAndRemoveIPHViews();
@@ -128,6 +148,16 @@
   base::ScopedObservation<bookmarks::BookmarkModel,
                           bookmarks::BaseBookmarkModelObserver>
       bookmark_model_observation_{this};
+  // `ReadingListModel` instance providing access to the reading list. May be
+  // `nullptr`.
+  raw_ptr<ReadingListModel> reading_list_model_ = nullptr;
+  // Tracks whether the reading list model has finished loading. Until the model
+  // is fully loaded, it is unsafe to use or interact with it.
+  bool reading_list_model_loaded_ = false;
+  // Automatically removes this observer from the reading list model when
+  // destroyed.
+  base::ScopedObservation<ReadingListModel, ReadingListModelObserver>
+      reading_list_model_observation_{this};
   // Observer for URL loading.
   raw_ptr<UrlLoadingNotifierBrowserAgent> url_loading_notifier_;
   // Command dispatcher for the browser; used to retrieve help handler.
diff --git a/ios/chrome/browser/iph_for_new_chrome_user/model/tab_based_iph_browser_agent.mm b/ios/chrome/browser/iph_for_new_chrome_user/model/tab_based_iph_browser_agent.mm
index 4dc522f..c5ff221 100644
--- a/ios/chrome/browser/iph_for_new_chrome_user/model/tab_based_iph_browser_agent.mm
+++ b/ios/chrome/browser/iph_for_new_chrome_user/model/tab_based_iph_browser_agent.mm
@@ -9,9 +9,12 @@
 #import "components/bookmarks/browser/bookmark_node.h"
 #import "components/feature_engagement/public/event_constants.h"
 #import "components/feature_engagement/public/tracker.h"
+#import "components/reading_list/core/reading_list_entry.h"
+#import "components/reading_list/core/reading_list_model.h"
 #import "components/send_tab_to_self/features.h"
 #import "ios/chrome/browser/bookmarks/model/bookmark_model_factory.h"
 #import "ios/chrome/browser/feature_engagement/model/tracker_factory.h"
+#import "ios/chrome/browser/reading_list/model/reading_list_model_factory.h"
 #import "ios/chrome/browser/shared/model/browser/browser.h"
 #import "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
 #import "ios/chrome/browser/shared/model/web_state_list/active_web_state_observation_forwarder.h"
@@ -30,6 +33,8 @@
                                                                this)),
       bookmark_model_(
           ios::BookmarkModelFactory::GetForProfile(browser->GetProfile())),
+      reading_list_model_(
+          ReadingListModelFactory::GetForProfile(browser->GetProfile())),
       url_loading_notifier_(
           UrlLoadingNotifierBrowserAgent::FromBrowser(browser)),
       command_dispatcher_(browser->GetCommandDispatcher()),
@@ -40,6 +45,7 @@
           IsSendTabIOSPushNotificationsEnabledWithTabReminders() &&
       bookmark_model_) {
     bookmark_model_observation_.Observe(bookmark_model_.get());
+    reading_list_model_observation_.Observe(reading_list_model_.get());
   }
   url_loading_notifier_->AddObserver(this);
 }
@@ -132,6 +138,7 @@
   if (send_tab_to_self::
           IsSendTabIOSPushNotificationsEnabledWithTabReminders()) {
     StopObservingBookmarkModel();
+    StopObservingReadingListModel();
   }
 
   web_state_list_ = nil;
@@ -140,6 +147,48 @@
   engagement_tracker_ = nil;
 }
 
+#pragma mark - ReadingListModelObserver
+
+void TabBasedIPHBrowserAgent::ReadingListModelLoaded(
+    const ReadingListModel* model) {
+  CHECK(
+      send_tab_to_self::IsSendTabIOSPushNotificationsEnabledWithTabReminders());
+
+  reading_list_model_loaded_ = true;
+}
+
+void TabBasedIPHBrowserAgent::ReadingListModelBeingShutdown(
+    const ReadingListModel* model) {
+  CHECK(
+      send_tab_to_self::IsSendTabIOSPushNotificationsEnabledWithTabReminders());
+  CHECK(reading_list_model_loaded_);
+
+  // The model passed is `const`, which makes it impossible to call
+  // `model->RemoveObserver(...)`. Other `ReadingListModelObserver`
+  // clients address this by removing themselves as observers using a reference
+  // to the model maintained in their class, so a similar approach is followed
+  // in `StopObservingReadingListModel()`. Fortunately,
+  // `reading_list_model_observation_` will gracefully handle removing
+  // `TabBasedIPHBrowserAgent` as an observer if all else fails.
+  StopObservingReadingListModel();
+}
+
+void TabBasedIPHBrowserAgent::ReadingListDidAddEntry(
+    const ReadingListModel* model,
+    const GURL& url,
+    reading_list::EntrySource source) {
+  CHECK(
+      send_tab_to_self::IsSendTabIOSPushNotificationsEnabledWithTabReminders());
+  CHECK(reading_list_model_loaded_);
+
+  if (source == reading_list::EntrySource::ADDED_VIA_CURRENT_APP) {
+    // A reading list entry was manually added by the user.
+
+    // TODO(crbug.com/389911609): Fire a UI command to display the relevant
+    // Reminder Notifications Bubble IPH.
+  }
+}
+
 #pragma mark - UrlLoadingObserver
 
 void TabBasedIPHBrowserAgent::TabDidLoadUrl(
@@ -252,6 +301,19 @@
   bookmark_model_observation_.Reset();
 }
 
+void TabBasedIPHBrowserAgent::StopObservingReadingListModel() {
+  CHECK(
+      send_tab_to_self::IsSendTabIOSPushNotificationsEnabledWithTabReminders());
+
+  if (reading_list_model_) {
+    reading_list_model_->RemoveObserver(this);
+  }
+
+  reading_list_model_ = nullptr;
+  reading_list_model_observation_.Reset();
+  reading_list_model_loaded_ = false;
+}
+
 void TabBasedIPHBrowserAgent::ResetFeatureStatesAndRemoveIPHViews() {
   multi_gesture_refresh_ = false;
   back_forward_button_tapped_ = false;
diff --git a/ios/chrome/browser/parcel_tracking/features.mm b/ios/chrome/browser/parcel_tracking/features.mm
index a394bf8..0195151 100644
--- a/ios/chrome/browser/parcel_tracking/features.mm
+++ b/ios/chrome/browser/parcel_tracking/features.mm
@@ -11,7 +11,7 @@
 
 BASE_FEATURE(kIOSDisableParcelTracking,
              "IOSDisableParcelTracking",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 bool IsIOSParcelTrackingEnabled() {
   variations::VariationsService* variations_service =
diff --git a/ios/chrome/browser/parcel_tracking/parcel_tracking_util_unittest.mm b/ios/chrome/browser/parcel_tracking/parcel_tracking_util_unittest.mm
index aed1ab7..037f21c 100644
--- a/ios/chrome/browser/parcel_tracking/parcel_tracking_util_unittest.mm
+++ b/ios/chrome/browser/parcel_tracking/parcel_tracking_util_unittest.mm
@@ -72,6 +72,9 @@
 // Tests that IsUserEligibleParcelTrackingOptInPrompt returns true when the user
 // is eligible.
 TEST_F(ParcelTrackingUtilTest, UserIsEligibleForPrompt) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(kIOSDisableParcelTracking);
+
   SignIn();
   SetPromptDisplayedStatus(false);
   IOSChromeScopedTestingVariationsService scoped_variations_service;
@@ -83,6 +86,9 @@
 // Tests that IsUserEligibleParcelTrackingOptInPrompt returns false when the
 // user is not signed in.
 TEST_F(ParcelTrackingUtilTest, NotSignedIn) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(kIOSDisableParcelTracking);
+
   SignOut();
   SetPromptDisplayedStatus(false);
   shopping_service_->SetIsParcelTrackingEligible(false);
@@ -93,9 +99,9 @@
 }
 
 // Tests that IsUserEligibleParcelTrackingOptInPrompt returns false when the
-// feature is disabled.
+// feature is disabled. This is now the default behavior, so doesn't require a
+// specific feature override.
 TEST_F(ParcelTrackingUtilTest, FeatureDisabled) {
-  base::test::ScopedFeatureList scoped_feature_list(kIOSDisableParcelTracking);
   SignIn();
   SetPromptDisplayedStatus(false);
   EXPECT_FALSE(IsUserEligibleParcelTrackingOptInPrompt(
@@ -105,6 +111,9 @@
 // Tests that IsUserEligibleParcelTrackingOptInPrompt returns false when the
 // user has seen the prompt.
 TEST_F(ParcelTrackingUtilTest, UserHasSeenPrompt) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(kIOSDisableParcelTracking);
+
   SignIn();
   SetPromptDisplayedStatus(true);
   IOSChromeScopedTestingVariationsService scoped_variations_service;
@@ -116,6 +125,9 @@
 // Tests that IsUserEligibleParcelTrackingOptInPrompt returns false when the
 // permanent country is not set to US.
 TEST_F(ParcelTrackingUtilTest, CountryNotUS) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(kIOSDisableParcelTracking);
+
   SignIn();
   SetPromptDisplayedStatus(true);
   EXPECT_FALSE(IsUserEligibleParcelTrackingOptInPrompt(
diff --git a/ios/chrome/browser/settings/ui_bundled/google_services/google_services_settings_egtest.mm b/ios/chrome/browser/settings/ui_bundled/google_services/google_services_settings_egtest.mm
index 1b9c48a..8522a0b 100644
--- a/ios/chrome/browser/settings/ui_bundled/google_services/google_services_settings_egtest.mm
+++ b/ios/chrome/browser/settings/ui_bundled/google_services/google_services_settings_egtest.mm
@@ -122,7 +122,7 @@
     // the feature is not enabled.
     config.features_enabled.push_back(kIdentityDiscAccountMenu);
   }
-  if ([self isRunningTest:@selector(DISABLED_testParcelTrackingSetting)]) {
+  if ([self isRunningTest:@selector(testParcelTrackingSetting)]) {
     config.features_disabled.push_back(kIOSDisableParcelTracking);
   }
   return config;
@@ -343,12 +343,9 @@
 }
 
 // Tests the parcel tracking settings row is properly shown.
-// TODO(crbug.com/389954360): Restore this test.
-- (void)DISABLED_testParcelTrackingSetting {
+- (void)testParcelTrackingSetting {
   // Parcel tracking is only enabled in the US.
-  [ChromeEarlGrey setStringValue:"us"
-               forLocalStatePref:variations::prefs::
-                                     kVariationsPermanentOverriddenCountry];
+  [ChromeEarlGrey overrideVariationsServiceStoredPermanentCountry:@"us"];
 
   [self openGoogleServicesSettings];
 
@@ -365,9 +362,7 @@
 // Tests the parcel tracking settings row is not shown for non-US countries.
 - (void)testParcelTrackingSetting_notShownOutsideUS {
   // Set permanent country to somthing other than the US.
-  [ChromeEarlGrey setStringValue:"fr"
-               forLocalStatePref:variations::prefs::
-                                     kVariationsPermanentOverriddenCountry];
+  [ChromeEarlGrey overrideVariationsServiceStoredPermanentCountry:@"fr"];
 
   [self openGoogleServicesSettings];
 
diff --git a/ios/chrome/browser/settings/ui_bundled/password/BUILD.gn b/ios/chrome/browser/settings/ui_bundled/password/BUILD.gn
index 019af96f..221274c 100644
--- a/ios/chrome/browser/settings/ui_bundled/password/BUILD.gn
+++ b/ios/chrome/browser/settings/ui_bundled/password/BUILD.gn
@@ -84,7 +84,6 @@
   ]
   deps = [
     ":common",
-    ":features",
     ":password_constants",
     ":title_view",
     "//base",
@@ -97,7 +96,6 @@
     "//components/prefs",
     "//components/signin/public/identity_manager/objc",
     "//components/strings",
-    "//components/sync/base",
     "//components/sync/service",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/net/model:crurl",
@@ -171,7 +169,6 @@
     "password_manager_ui_features.h",
     "password_manager_ui_features.mm",
   ]
-  deps = [ "//components/sync/base:features" ]
   public_deps = [ "//base" ]
 }
 
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_manager_ui_features.h b/ios/chrome/browser/settings/ui_bundled/password/password_manager_ui_features.h
index 456614e..a975397d 100644
--- a/ios/chrome/browser/settings/ui_bundled/password/password_manager_ui_features.h
+++ b/ios/chrome/browser/settings/ui_bundled/password/password_manager_ui_features.h
@@ -11,19 +11,18 @@
 // components.
 namespace password_manager::features {
 
+// Kill switch for the logic that allows the user to open the native Password
+// Settings page. Used when the user wants to access the Password Manager UI
+// without a passcode set.
 BASE_DECLARE_FEATURE(kIOSEnablePasscodeSettings);
 
+// Feature switch for the logic that allows the user to delete all saved
+// credentials in PWM.
 BASE_DECLARE_FEATURE(kIOSEnableDeleteAllSavedCredentials);
 
-BASE_DECLARE_FEATURE(kIOSPasskeysM2);
-
-// Helper function returning the status of `kIOSEnablePasscodeShortcut`.
+// Helper function returning the status of `kIOSEnablePasscodeSettings`.
 bool IsPasscodeSettingsEnabled();
 
-// Helper function returning the status of `kIOSPasskeysM2` and the M1
-// prerequisite.
-bool IOSPasskeysM2Enabled();
-
 }  // namespace password_manager::features
 
 #endif  // IOS_CHROME_BROWSER_SETTINGS_UI_BUNDLED_PASSWORD_PASSWORD_MANAGER_UI_FEATURES_H_
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_manager_ui_features.mm b/ios/chrome/browser/settings/ui_bundled/password/password_manager_ui_features.mm
index 6bf7764..796f7963 100644
--- a/ios/chrome/browser/settings/ui_bundled/password/password_manager_ui_features.mm
+++ b/ios/chrome/browser/settings/ui_bundled/password/password_manager_ui_features.mm
@@ -4,37 +4,18 @@
 
 #import "ios/chrome/browser/settings/ui_bundled/password/password_manager_ui_features.h"
 
-#import "components/sync/base/features.h"
-
 namespace password_manager::features {
 
-// Kill switch for the logic that allows the user to open the native Password
-// Settings page. Used when the user wants to access the Password Manager UI
-// without a passcode set.
 BASE_FEATURE(kIOSEnablePasscodeSettings,
              "IOSEnablePasscodeSettings",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-// Feature switch for the logic that allows the user to delete all saved
-// credentials in PWM.
 BASE_FEATURE(kIOSEnableDeleteAllSavedCredentials,
              "IOSEnableDeleteAllSavedCredentials",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Enables passkey syncing follow-up features.
-BASE_FEATURE(kIOSPasskeysM2,
-             "IOSPasskeysM2",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
-// Helper function returning the status of `kIOSEnablePasscodeSettings`.
 bool IsPasscodeSettingsEnabled() {
   return base::FeatureList::IsEnabled(kIOSEnablePasscodeSettings);
 }
 
-bool IOSPasskeysM2Enabled() {
-  return syncer::IsWebauthnCredentialSyncEnabled() &&
-         base::FeatureList::IsEnabled(
-             password_manager::features::kIOSPasskeysM2);
-}
-
 }  // namespace password_manager::features
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller.mm b/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller.mm
index 197692c..059ee9c 100644
--- a/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller.mm
+++ b/ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller.mm
@@ -39,7 +39,6 @@
 #import "ios/chrome/browser/settings/ui_bundled/cells/settings_check_item.h"
 #import "ios/chrome/browser/settings/ui_bundled/elements/enterprise_info_popover_view_controller.h"
 #import "ios/chrome/browser/settings/ui_bundled/password/create_password_manager_title_view.h"
-#import "ios/chrome/browser/settings/ui_bundled/password/password_manager_ui_features.h"
 #import "ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller+Testing.h"
 #import "ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller_delegate.h"
 #import "ios/chrome/browser/settings/ui_bundled/password/password_manager_view_controller_items.h"
@@ -53,6 +52,7 @@
 #import "ios/chrome/browser/settings/ui_bundled/utils/password_utils.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/url/chrome_url_constants.h"
+#import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/elements/home_waiting_view.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_detail_icon_item.h"
@@ -87,7 +87,6 @@
 #import "url/gurl.h"
 
 using base::UmaHistogramEnumeration;
-using password_manager::features::IOSPasskeysM2Enabled;
 using password_manager::metrics_util::PasswordCheckInteraction;
 
 namespace {
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_settings/BUILD.gn b/ios/chrome/browser/settings/ui_bundled/password/password_settings/BUILD.gn
index 3591881..8e1246f 100644
--- a/ios/chrome/browser/settings/ui_bundled/password/password_settings/BUILD.gn
+++ b/ios/chrome/browser/settings/ui_bundled/password/password_settings/BUILD.gn
@@ -70,10 +70,10 @@
   deps = [
     ":password_settings_constants",
     "//components/strings",
-    "//components/sync/base",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/settings/ui_bundled/password:features",
     "//ios/chrome/browser/shared/public/commands",
+    "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/shared/ui/symbols",
     "//ios/chrome/browser/shared/ui/table_view",
     "//ios/chrome/browser/shared/ui/table_view:utils",
@@ -127,7 +127,6 @@
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/passwords/model:store_factory",
     "//ios/chrome/browser/passwords/model/metrics",
-    "//ios/chrome/browser/settings/ui_bundled/password:features",
     "//ios/chrome/browser/settings/ui_bundled/password/reauthentication:reauthentication_ui",
     "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
     "//ios/chrome/browser/shared/model/application_context",
@@ -135,6 +134,7 @@
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile/test",
     "//ios/chrome/browser/shared/public/commands",
+    "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/browser/shared/ui/table_view",
     "//ios/chrome/browser/shared/ui/table_view:test_support",
     "//ios/chrome/browser/signin/model",
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_view_controller.mm b/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_view_controller.mm
index cd5f7e6c..7592596 100644
--- a/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_view_controller.mm
+++ b/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_view_controller.mm
@@ -18,6 +18,7 @@
 #import "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/settings/ui_bundled/password/password_manager_ui_features.h"
 #import "ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_constants.h"
+#import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_detail_icon_item.h"
 #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_image_item.h"
@@ -38,8 +39,6 @@
 
 namespace {
 
-using ::password_manager::features::IOSPasskeysM2Enabled;
-
 // Sections of the password settings UI.
 typedef NS_ENUM(NSInteger, SectionIdentifier) {
   SectionIdentifierSavePasswordsSwitch = kSectionIdentifierEnumZero,
diff --git a/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_view_controller_unittest.mm b/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_view_controller_unittest.mm
index 709f298..d184ff2 100644
--- a/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_view_controller_unittest.mm
+++ b/ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_view_controller_unittest.mm
@@ -7,8 +7,8 @@
 #import "base/apple/foundation_util.h"
 #import "base/test/scoped_feature_list.h"
 #import "components/sync/base/features.h"
-#import "ios/chrome/browser/settings/ui_bundled/password/password_manager_ui_features.h"
 #import "ios/chrome/browser/settings/ui_bundled/password/password_settings/password_settings_consumer.h"
+#import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_detail_icon_item.h"
 #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_image_item.h"
 #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_info_button_item.h"
@@ -28,11 +28,7 @@
 // displayed on top. This differs based on the addition of the automatic passkey
 // upgrades toggle. Should be cleaned up after the feature is launched.
 int ExpectedSectionAfterAlwaysVisibleTopSections() {
-  return syncer::IsWebauthnCredentialSyncEnabled() &&
-                 base::FeatureList::IsEnabled(
-                     password_manager::features::kIOSPasskeysM2)
-             ? 3
-             : 2;
+  return IOSPasskeysM2Enabled() ? 3 : 2;
 }
 
 }  // namespace
@@ -137,8 +133,7 @@
   }
 
   base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      password_manager::features::kIOSPasskeysM2);
+  scoped_feature_list.InitAndEnableFeature(kIOSPasskeysM2);
 
   // Re-create the controller so that the enabled flag is picked up.
   CreateController();
diff --git a/ios/chrome/browser/settings/ui_bundled/password/passwords_in_other_apps/BUILD.gn b/ios/chrome/browser/settings/ui_bundled/password/passwords_in_other_apps/BUILD.gn
index abfa5fd..a7c8e34 100644
--- a/ios/chrome/browser/settings/ui_bundled/password/passwords_in_other_apps/BUILD.gn
+++ b/ios/chrome/browser/settings/ui_bundled/password/passwords_in_other_apps/BUILD.gn
@@ -34,7 +34,6 @@
     "//components/strings",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/settings/ui_bundled:settings_root",
-    "//ios/chrome/browser/settings/ui_bundled/password:features",
     "//ios/chrome/browser/settings/ui_bundled/resources",
     "//ios/chrome/browser/settings/ui_bundled/utils",
     "//ios/chrome/browser/shared/public/features",
@@ -69,7 +68,6 @@
     ":passwords_in_other_apps",
     ":passwords_in_other_apps_ui",
     "//base/test:test_support",
-    "//ios/chrome/browser/settings/ui_bundled/password:features",
     "//ios/chrome/browser/settings/ui_bundled/password/password_settings:common",
     "//ios/chrome/browser/settings/ui_bundled/password/reauthentication:reauthentication_ui",
     "//ios/chrome/browser/settings/ui_bundled/utils",
diff --git a/ios/chrome/browser/settings/ui_bundled/password/passwords_in_other_apps/passwords_in_other_apps_view_controller.mm b/ios/chrome/browser/settings/ui_bundled/password/passwords_in_other_apps/passwords_in_other_apps_view_controller.mm
index 63eaadf9..f98b7f4 100644
--- a/ios/chrome/browser/settings/ui_bundled/password/passwords_in_other_apps/passwords_in_other_apps_view_controller.mm
+++ b/ios/chrome/browser/settings/ui_bundled/password/passwords_in_other_apps/passwords_in_other_apps_view_controller.mm
@@ -5,7 +5,6 @@
 #import "ios/chrome/browser/settings/ui_bundled/password/passwords_in_other_apps/passwords_in_other_apps_view_controller.h"
 
 #import "base/ios/ios_util.h"
-#import "ios/chrome/browser/settings/ui_bundled/password/password_manager_ui_features.h"
 #import "ios/chrome/browser/settings/ui_bundled/password/passwords_in_other_apps/constants.h"
 #import "ios/chrome/browser/settings/ui_bundled/password/passwords_in_other_apps/passwords_in_other_apps_view_controller_delegate.h"
 #import "ios/chrome/browser/settings/ui_bundled/settings_navigation_controller.h"
@@ -29,8 +28,6 @@
 
 namespace {
 
-using ::password_manager::features::IOSPasskeysM2Enabled;
-
 CGFloat const kCaptionTextViewOffset = 16;
 CGFloat const kDefaultMargin = 16;
 CGFloat const kTitleTopMinimumMargin = 48;
@@ -543,7 +540,6 @@
 - (UIView*)turnOffInstructionView {
   if (!_turnOffInstructionView) {
     UITextView* captionTextView = [self drawCaptionTextView];
-    NSLog(@"%@", captionTextView.text);
     UIImage* checkmark = [[UIImage imageNamed:@"settings_safe_state"]
         imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
     UIImageView* checkmarkView = [[UIImageView alloc] initWithImage:checkmark];
@@ -622,9 +618,7 @@
 }
 
 - (BOOL)useShortInstruction {
-  return ios::provider::SupportShortenedInstructionForPasswordAutoFill() &&
-         base::FeatureList::IsEnabled(
-             kEnableShortenedPasswordAutoFillInstruction);
+  return ios::provider::SupportShortenedInstructionForPasswordAutoFill();
 }
 
 - (NSArray<NSString*>*)steps {
diff --git a/ios/chrome/browser/shared/public/features/BUILD.gn b/ios/chrome/browser/shared/public/features/BUILD.gn
index e454581..6c384f5 100644
--- a/ios/chrome/browser/shared/public/features/BUILD.gn
+++ b/ios/chrome/browser/shared/public/features/BUILD.gn
@@ -11,6 +11,7 @@
     "//components/country_codes",
     "//components/segmentation_platform/embedder/home_modules:constants",
     "//components/segmentation_platform/public",
+    "//components/sync/base:features",
     "//components/version_info:channel",
     "//ios/chrome/app:background_mode_buildflags",
     "//ios/chrome/browser/ntp/shared/metrics:constants",
diff --git a/ios/chrome/browser/shared/public/features/features.h b/ios/chrome/browser/shared/public/features/features.h
index acba01a..d11c2fd 100644
--- a/ios/chrome/browser/shared/public/features/features.h
+++ b/ios/chrome/browser/shared/public/features/features.h
@@ -357,10 +357,6 @@
 // Feature flag to enable duplicate NTP cleanup.
 BASE_DECLARE_FEATURE(kRemoveExcessNTPs);
 
-// Feature flag to enable shortened instruction to turn on Password AutoFill for
-// Chrome.
-BASE_DECLARE_FEATURE(kEnableShortenedPasswordAutoFillInstruction);
-
 // Feature flag / Kill Switch for TCRex.
 BASE_DECLARE_FEATURE(kTCRexKillSwitch);
 
@@ -1000,4 +996,11 @@
 // Returns whether 'kFRESignInSecondaryActionLabelUpdate' is enabled
 bool FRESignInSecondaryActionLabelUpdate();
 
+// Enables passkey syncing follow-up features.
+BASE_DECLARE_FEATURE(kIOSPasskeysM2);
+
+// Helper function returning the status of `kIOSPasskeysM2` and the M1
+// prerequisite.
+bool IOSPasskeysM2Enabled();
+
 #endif  // IOS_CHROME_BROWSER_SHARED_PUBLIC_FEATURES_FEATURES_H_
diff --git a/ios/chrome/browser/shared/public/features/features.mm b/ios/chrome/browser/shared/public/features/features.mm
index d5e4d89..868a1a3 100644
--- a/ios/chrome/browser/shared/public/features/features.mm
+++ b/ios/chrome/browser/shared/public/features/features.mm
@@ -11,6 +11,7 @@
 #import "base/metrics/field_trial_params.h"
 #import "components/country_codes/country_codes.h"
 #import "components/segmentation_platform/public/features.h"
+#import "components/sync/base/features.h"
 #import "components/version_info/channel.h"
 #import "ios/chrome/app/background_mode_buildflags.h"
 #import "ios/chrome/browser/ntp/shared/metrics/feed_metrics_constants.h"
@@ -299,10 +300,6 @@
              "RemoveExcessNTPs",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kEnableShortenedPasswordAutoFillInstruction,
-             "EnableShortenedPasswordAutoFillInstruction",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kTCRexKillSwitch,
              "kTCRexKillSwitch",
              base::FEATURE_DISABLED_BY_DEFAULT);
@@ -1220,3 +1217,12 @@
 bool FRESignInSecondaryActionLabelUpdate() {
   return base::FeatureList::IsEnabled(kFRESignInSecondaryActionLabelUpdate);
 }
+
+BASE_FEATURE(kIOSPasskeysM2,
+             "IOSPasskeysM2",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+bool IOSPasskeysM2Enabled() {
+  return syncer::IsWebauthnCredentialSyncEnabled() &&
+         base::FeatureList::IsEnabled(kIOSPasskeysM2);
+}
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index 6570e3d..dd99125 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -364,6 +364,7 @@
     "//ios/chrome/browser/ntp/model:set_up_list_prefs",
     "//ios/chrome/browser/ntp/shared/metrics:home_metrics",
     "//ios/chrome/browser/ntp/ui_bundled",
+    "//ios/chrome/browser/parcel_tracking:features",
     "//ios/chrome/browser/reading_list/model",
     "//ios/chrome/browser/reading_list/model:test_support",
     "//ios/chrome/browser/safety_check/model:constants",
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn
index aee6ad43..f645116 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/cells/BUILD.gn
@@ -46,6 +46,7 @@
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_constant",
     "//ios/chrome/browser/ui/content_suggestions:public",
     "//ios/chrome/browser/ui/content_suggestions/cells/resources",
+    "//ios/chrome/browser/ui/content_suggestions/magic_stack:constants",
     "//ios/chrome/common:timing",
     "//ios/chrome/common/ui/colors",
     "//ios/chrome/common/ui/elements",
@@ -100,6 +101,7 @@
     "//ios/chrome/browser/ui/content_suggestions:metrics",
     "//ios/chrome/browser/ui/content_suggestions:public",
     "//ios/chrome/browser/ui/content_suggestions/magic_stack:public",
+    "//ios/chrome/browser/ui/content_suggestions/magic_stack:utils",
     "//ios/chrome/browser/url_loading/model",
     "//ios/chrome/browser/url_loading/model:url_loading_params_header",
     "//ios/chrome/browser/widget_kit/model:features",
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_view.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_view.mm
index df3240fd..7b7b42c 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_tile_view.mm
@@ -171,11 +171,13 @@
   UIContentSizeCategory category =
       self.traitCollection.preferredContentSizeCategory;
   NSComparisonResult result = UIContentSizeCategoryCompareToCategory(
-      category, UIContentSizeCategoryExtraLarge);
+      category, UIContentSizeCategoryExtraExtraExtraLarge);
   if (result == NSOrderedAscending) {
     self.titleLabel.numberOfLines = kLabelNumLines;
+    self.titleLabel.lineBreakMode = NSLineBreakByCharWrapping;
   } else {
     self.titleLabel.numberOfLines = 1;
+    self.titleLabel.lineBreakMode = NSLineBreakByTruncatingTail;
   }
 }
 
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/icon_detail_view.mm b/ios/chrome/browser/ui/content_suggestions/cells/icon_detail_view.mm
index 8e22518..5b921b1 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/icon_detail_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/icon_detail_view.mm
@@ -7,6 +7,7 @@
 #import "ios/chrome/browser/shared/ui/symbols/symbols.h"
 #import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/icon_view.h"
+#import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_constants.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
 #import "ios/chrome/common/ui/elements/gradient_view.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
@@ -711,10 +712,18 @@
   label.translatesAutoresizingMaskIntoConstraints = NO;
   label.numberOfLines = 0;
   label.lineBreakMode = NSLineBreakByWordWrapping;
-  label.font =
-      _layoutType == IconDetailViewLayoutType::kHero
-          ? CreateDynamicFont(UIFontTextStyleFootnote, UIFontWeightSemibold)
-          : [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+  if (_layoutType == IconDetailViewLayoutType::kHero) {
+    label.font = [[UIFontMetrics defaultMetrics]
+        scaledFontForFont:CreateDynamicFont(UIFontTextStyleFootnote,
+                                            UIFontWeightSemibold)
+         maximumPointSize:kMaxTextSizeForStyleFootnote];
+  } else {
+    label.font = [[UIFontMetrics defaultMetrics]
+        scaledFontForFont:[UIFont
+                              preferredFontForTextStyle:UIFontTextStyleFootnote]
+         maximumPointSize:kMaxTextSizeForStyleFootnote];
+  }
+
   label.adjustsFontForContentSizeCategory = YES;
   label.textColor = [UIColor colorNamed:kTextPrimaryColor];
 
@@ -729,7 +738,10 @@
   label.text = _description;
   label.numberOfLines = 2;
   label.lineBreakMode = NSLineBreakByTruncatingTail;
-  label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
+  label.font = [[UIFontMetrics defaultMetrics]
+      scaledFontForFont:[UIFont
+                            preferredFontForTextStyle:UIFontTextStyleFootnote]
+       maximumPointSize:kMaxTextSizeForStyleFootnote];
   label.adjustsFontForContentSizeCategory = YES;
   label.textColor = [UIColor colorNamed:kTextSecondaryColor];
 
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_mediator.h b/ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_mediator.h
index 206d411..faee516 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_mediator.h
+++ b/ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_mediator.h
@@ -104,6 +104,9 @@
 // Trigger a refresh of the Most Visited tiles.
 - (void)refreshMostVisitedTiles;
 
+// Disable the most visited sites module.
+- (void)disableModule;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_CELLS_MOST_VISITED_TILES_MEDIATOR_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_mediator.mm b/ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_mediator.mm
index 687d8da..4d0e256 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_mediator.mm
@@ -146,6 +146,10 @@
   _mostVisitedSites->Refresh();
 }
 
+- (void)disableModule {
+  _prefService->SetBoolean(prefs::kHomeCustomizationMostVisitedEnabled, false);
+}
+
 - (MostVisitedTilesConfig*)mostVisitedTilesConfig {
   return _mostVisitedConfig;
 }
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_stack_view.mm b/ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_stack_view.mm
index b8da76c4..072c4a3 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_stack_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_stack_view.mm
@@ -4,6 +4,7 @@
 
 #import "ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_stack_view.h"
 
+#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_item.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_most_visited_tile_view.h"
 #import "ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_commands.h"
@@ -11,17 +12,35 @@
 #import "ios/chrome/browser/ui/content_suggestions/cells/most_visited_tiles_stack_view_consumer_source.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_constants.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_image_data_source.h"
+#import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.h"
 #import "ios/chrome/common/ui/favicon/favicon_attributes.h"
 #import "ios/chrome/common/ui/favicon/favicon_view.h"
 #import "url/gurl.h"
 
-@implementation MostVisitedTilesStackView
+@implementation MostVisitedTilesStackView {
+  NSLayoutConstraint* _heightConstraint;
+  BOOL _inMagicStack;
+}
 
 - (instancetype)initWithConfig:(MostVisitedTilesConfig*)config
                        spacing:(CGFloat)spacing {
   if ((self = [super init])) {
     if (config.inMagicStack) {
+      _inMagicStack = YES;
       [config.consumerSource addConsumer:self];
+
+      _heightConstraint = [self.heightAnchor
+          constraintLessThanOrEqualToConstant:GetMagicStackHeight(self) - 10];
+      _heightConstraint.active = YES;
+
+      if (@available(iOS 17, *)) {
+        NSArray<UITrait>* traits = TraitCollectionSetForTraits(
+            @[ UITraitPreferredContentSizeCategory.class ]);
+        [self registerForTraitChanges:traits
+                           withAction:@selector(updateHeight)];
+      }
+    } else {
+      _inMagicStack = NO;
     }
     self.axis = UILayoutConstraintAxisHorizontal;
     self.distribution = UIStackViewDistributionFillEqually;
@@ -32,6 +51,21 @@
   return self;
 }
 
+#if !defined(__IPHONE_17_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_17_0
+- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+  if (@available(iOS 17, *)) {
+    return;
+  }
+
+  if (previousTraitCollection.preferredContentSizeCategory !=
+          self.traitCollection.preferredContentSizeCategory &&
+      _inMagicStack) {
+    [self updateHeight];
+  }
+}
+#endif
+
 #pragma mark - MostVisitedTilesStackViewConsumer
 
 - (void)updateWithConfig:(MostVisitedTilesConfig*)config {
@@ -80,4 +114,9 @@
   }
 }
 
+// Resizes the Most Visted card height.
+- (void)updateHeight {
+  _heightConstraint.constant = GetMagicStackHeight(self) - 10;
+}
+
 @end
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
index 6ebec48..a3a0b58 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -854,6 +854,9 @@
 
 - (void)neverShowModuleType:(ContentSuggestionsModuleType)type {
   switch (type) {
+    case ContentSuggestionsModuleType::kMostVisited:
+      [_mostVisitedTilesMediator disableModule];
+      break;
     case ContentSuggestionsModuleType::kTabResumption:
       [_tabResumptionMediator disableModule];
       break;
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/magic_stack/BUILD.gn
index e82c11c..d729d522 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/BUILD.gn
@@ -121,6 +121,7 @@
     ":constants",
     ":magic_stack_module_content_view_delegate",
     ":public",
+    ":utils",
     "//base",
     "//google_apis",
     "//ios/chrome/app/strings",
@@ -228,6 +229,7 @@
     "//ios/chrome/browser/first_run/ui_bundled:utils",
     "//ios/chrome/browser/ntp/model:set_up_list_prefs",
     "//ios/chrome/browser/ntp/shared/metrics:constants",
+    "//ios/chrome/browser/parcel_tracking:features",
     "//ios/chrome/browser/reading_list/model",
     "//ios/chrome/browser/reading_list/model:test_support",
     "//ios/chrome/browser/safety_check/model:factory",
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view.mm
index 0877d88c..04b98ff 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view.mm
@@ -55,6 +55,7 @@
   // The most recently selected MagicStack module's page index.
   NSUInteger _magicStackPage;
   BOOL _hasSeenEphemeralCard;
+  NSLayoutConstraint* _heightConstraint;
 }
 
 - (void)loadView {
@@ -63,9 +64,20 @@
   [self populateWithPlaceholders];
 
   self.view = _collectionView;
-  [NSLayoutConstraint
-      activateConstraints:@[ [_collectionView.heightAnchor
-                              constraintEqualToConstant:kModuleMaxHeight] ]];
+
+  if (@available(iOS 17, *)) {
+    NSArray<UITrait>* traits = TraitCollectionSetForTraits(
+        @[ UITraitPreferredContentSizeCategory.class ]);
+    [self registerForTraitChanges:traits
+                       withAction:@selector(updateCardHeightOnTraitChange)];
+  }
+}
+
+- (void)viewDidLoad {
+  _heightConstraint = [_collectionView.heightAnchor
+      constraintEqualToConstant:GetMagicStackHeight(self.view)];
+
+  [NSLayoutConstraint activateConstraints:@[ _heightConstraint ]];
 }
 
 - (void)viewWillLayoutSubviews {
@@ -91,6 +103,20 @@
                       completion:nil];
 }
 
+#if !defined(__IPHONE_17_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_17_0
+- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
+  [super traitCollectionDidChange:previousTraitCollection];
+  if (@available(iOS 17, *)) {
+    return;
+  }
+
+  if (previousTraitCollection.preferredContentSizeCategory !=
+      self.traitCollection.preferredContentSizeCategory) {
+    [self updateCardHeightOnTraitChange];
+  }
+}
+#endif
+
 #pragma mark - Public
 
 - (void)moduleWidthDidUpdate {
@@ -489,4 +515,12 @@
   }
 }
 
+// Resizes the Magic Stack card height.
+- (void)updateCardHeightOnTraitChange {
+  _heightConstraint.constant = GetMagicStackHeight(self.view);
+
+  [_magicStackCollectionViewLayoutConfigurator
+          .magicStackCompositionalLayout invalidateLayout];
+}
+
 @end
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view_unittest.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view_unittest.mm
index e4d1506c5..d472990 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_collection_view_unittest.mm
@@ -39,6 +39,7 @@
     audience_ = OCMStrictProtocolMock(
         @protocol(MagicStackCollectionViewControllerAudience));
     view_controller_.audience = audience_;
+    [view_controller_ loadViewIfNeeded];
     [view_controller_ viewDidLoad];
     [_window addSubview:[view_controller_ view]];
     AddSameConstraints(_window, [view_controller_ view]);
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_constants.h b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_constants.h
index 1cce039..d1d20f8 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_constants.h
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_constants.h
@@ -11,8 +11,8 @@
 extern NSString* const kMagicStackSectionIdentifier;
 extern NSString* const kMagicStackEditSectionIdentifier;
 
-// The max height of the modules.
-extern const int kModuleMaxHeight;
+// The max text size of text with the Footnote Text Style.
+extern const int kMaxTextSizeForStyleFootnote;
 
 // The spacing between modules in the Magic Stack.
 extern const CGFloat kMagicStackSpacing;
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_constants.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_constants.mm
index 3cfdd3b7b..2796635 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_constants.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_constants.mm
@@ -9,7 +9,7 @@
 NSString* const kMagicStackEditSectionIdentifier =
     @"MagicStackEditSectionIdentifier";
 
-const int kModuleMaxHeight = 150;
+const int kMaxTextSizeForStyleFootnote = 24;
 
 const CGFloat kMagicStackSpacing = 12.0f;
 
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.mm
index 9745294c..bcb2841 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_collection_view_cell.mm
@@ -196,6 +196,7 @@
     case ContentSuggestionsModuleType::kSendTabPromo:
     case ContentSuggestionsModuleType::kTipsWithProductImage:
     case ContentSuggestionsModuleType::kTips:
+    case ContentSuggestionsModuleType::kMostVisited:
       return YES;
     default:
       return NO;
@@ -327,6 +328,9 @@
     case ContentSuggestionsModuleType::kTips:
       return l10n_util::GetNSString(
           IDS_IOS_MAGIC_STACK_TIP_CONTEXT_MENU_DESCRIPTION);
+    case ContentSuggestionsModuleType::kMostVisited:
+      return l10n_util::GetNSString(
+          IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_CONTEXT_MENU_DESCRIPTION);
     default:
       NOTREACHED();
   }
@@ -371,6 +375,9 @@
           IDS_IOS_MAGIC_STACK_TIP_CONTEXT_MENU_HIDE_CHROME_TIPS,
           base::SysNSStringToUTF16(
               l10n_util::GetNSString(IDS_IOS_MAGIC_STACK_TIP_TITLE)));
+    case ContentSuggestionsModuleType::kMostVisited:
+      return l10n_util::GetNSString(
+          IDS_IOS_CONTENT_SUGGESTIONS_MOST_VISITED_MODULE_HIDE_CARD);
     default:
       NOTREACHED();
   }
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container.mm
index ce435f6..a31acae9 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container.mm
@@ -18,6 +18,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_container_delegate.h"
 #import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_content_view_delegate.h"
 #import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_module_contents_factory.h"
+#import "ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.h"
 #import "ios/chrome/browser/ui/content_suggestions/safety_check/safety_check_state.h"
 #import "ios/chrome/browser/ui/content_suggestions/safety_check/utils.h"
 #import "ios/chrome/common/ui/colors/semantic_color_names.h"
@@ -40,6 +41,7 @@
 // The bottom inset for the content within this container.
 const CGFloat kContentBottomInset = 24.0f;
 const CGFloat kReducedContentBottomInset = 10.0f;
+const CGFloat kOversizedReducedContentBottomInset = 20.0f;
 
 // Vertical spacing between the content views.
 const CGFloat kContentVerticalSpacing = 16.0f;
@@ -69,6 +71,7 @@
   NSLayoutConstraint* _containerHeightAnchor;
   NSLayoutConstraint* _contentStackViewBottomMarginAnchor;
   ContentSuggestionsModuleType _type;
+  BOOL _reducedBottomMargin;
 }
 
 - (instancetype)initWithFrame:(CGRect)frame {
@@ -91,6 +94,7 @@
     _title.textColor = [UIColor colorNamed:kTextPrimaryColor];
     _title.numberOfLines = 1;
     _title.lineBreakMode = NSLineBreakByTruncatingTail;
+    _title.adjustsFontForContentSizeCategory = YES;
     _title.accessibilityTraits |= UIAccessibilityTraitHeader;
     [_title setContentHuggingPriority:UILayoutPriorityDefaultLow
                               forAxis:UILayoutConstraintAxisHorizontal];
@@ -110,6 +114,9 @@
       [_title.bottomAnchor constraintEqualToAnchor:_titleStackView.bottomAnchor]
     ]];
 
+    _containerHeightAnchor = [self.heightAnchor
+        constraintLessThanOrEqualToConstant:GetMagicStackHeight(_stackView)];
+
     _seeMoreButton = [self
         actionButton:l10n_util::GetNSString(IDS_IOS_MAGIC_STACK_SEE_MORE)];
     _seeMoreButton.hidden = YES;
@@ -130,11 +137,14 @@
 
     _subtitle = [[UILabel alloc] init];
     _subtitle.hidden = YES;
-    _subtitle.font = [MagicStackModuleContainer fontForSubtitle];
+    _subtitle.font = [[UIFontMetrics defaultMetrics]
+        scaledFontForFont:[MagicStackModuleContainer fontForSubtitle]
+         maximumPointSize:kMaxTextSizeForStyleFootnote];
     _subtitle.textColor = [UIColor colorNamed:kTextSecondaryColor];
     _subtitle.numberOfLines = 0;
     _subtitle.lineBreakMode = NSLineBreakByWordWrapping;
     _subtitle.accessibilityTraits |= UIAccessibilityTraitHeader;
+    _subtitle.maximumContentSizeCategory = UIContentSizeCategoryMedium;
     [_subtitle setContentHuggingPriority:UILayoutPriorityRequired
                                  forAxis:UILayoutConstraintAxisHorizontal];
     [_subtitle
@@ -168,10 +178,6 @@
           constraintEqualToAnchor:_stackView.trailingAnchor],
     ]];
 
-    _containerHeightAnchor =
-        [self.heightAnchor constraintEqualToConstant:kModuleMaxHeight];
-    [NSLayoutConstraint activateConstraints:@[ _containerHeightAnchor ]];
-
     [self addSubview:_stackView];
     AddSameConstraintsToSidesWithInsets(
         _stackView, self,
@@ -194,9 +200,11 @@
         if (!strongSelf) {
           return;
         }
-        strongSelf->_title.font = [strongSelf fontForTitle];
+        [weakSelf updateTitleFont];
       };
       [self registerForTraitChanges:traits withHandler:handler];
+      [self registerForTraitChanges:traits
+                         withAction:@selector(updateCardSizing)];
     }
   }
   return self;
@@ -250,7 +258,7 @@
   [self resetView];
   // By default, the container is in the magic stack.
   BOOL inMagicStack = YES;
-  // Ensures that the modules conforms to a height of kModuleMaxHeight. For
+  // Ensures that the modules conforms to the dynamic MS height. For
   // the MVT when it lives outside of the Magic Stack to stay as close to its
   // intrinsic size as possible, the constraint is configured to be less than
   // or equal to.
@@ -263,8 +271,6 @@
       self.layer.cornerRadius = kCornerRadius;
       self.clipsToBounds = YES;
       _containerHeightAnchor.active = NO;
-      _containerHeightAnchor = [self.heightAnchor
-          constraintLessThanOrEqualToConstant:kModuleMaxHeight];
       [NSLayoutConstraint activateConstraints:@[ _containerHeightAnchor ]];
     }
   }
@@ -421,6 +427,11 @@
   return CreateDynamicFont(UIFontTextStyleFootnote, UIFontWeightRegular);
 }
 
+// Updates the title font.
+- (void)updateTitleFont {
+  _title.font = [self fontForTitle];
+}
+
 // Updates the bottom content margins if the module contents need it.
 - (void)updateBottomContentMarginsForConfig:(MagicStackModule*)config {
   switch (config.type) {
@@ -428,19 +439,25 @@
     case ContentSuggestionsModuleType::kShortcuts:
     case ContentSuggestionsModuleType::kCompactedSetUpList:
       _contentStackViewBottomMarginAnchor.constant =
-          -kReducedContentBottomInset;
+          isContentOversized(_stackView) ? -kOversizedReducedContentBottomInset
+                                         : -kReducedContentBottomInset;
+      _reducedBottomMargin = true;
       break;
     case ContentSuggestionsModuleType::kSafetyCheck: {
       SafetyCheckState* safetyCheckConfig =
           static_cast<SafetyCheckState*>(config);
       if ([safetyCheckConfig numberOfIssues] > 1) {
         _contentStackViewBottomMarginAnchor.constant =
-            -kReducedContentBottomInset;
+            isContentOversized(_stackView)
+                ? -kOversizedReducedContentBottomInset
+                : -kReducedContentBottomInset;
+        _reducedBottomMargin = true;
       }
       break;
     }
 
     default:
+      _reducedBottomMargin = false;
       break;
   }
 }
@@ -457,6 +474,7 @@
   if (previousTraitCollection.preferredContentSizeCategory !=
       self.traitCollection.preferredContentSizeCategory) {
     _title.font = [self fontForTitle];
+    [self updateCardSizing];
   }
 }
 #endif
@@ -540,4 +558,14 @@
   }
 }
 
+// Updates the card sizing based on the dynamic Magic Stack Height.
+- (void)updateCardSizing {
+  _containerHeightAnchor.constant = GetMagicStackHeight(_stackView);
+  if (_reducedBottomMargin) {
+    _contentStackViewBottomMarginAnchor.constant =
+        isContentOversized(_stackView) ? -kOversizedReducedContentBottomInset
+                                       : -kReducedContentBottomInset;
+  }
+}
+
 @end
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model_unittest.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model_unittest.mm
index 0dee0d3..b45c192 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_ranking_model_unittest.mm
@@ -38,6 +38,7 @@
 #import "ios/chrome/browser/first_run/ui_bundled/first_run_util.h"
 #import "ios/chrome/browser/ntp/model/set_up_list_prefs.h"
 #import "ios/chrome/browser/ntp/shared/metrics/feed_metrics_constants.h"
+#import "ios/chrome/browser/parcel_tracking/features.h"
 #import "ios/chrome/browser/reading_list/model/reading_list_model_factory.h"
 #import "ios/chrome/browser/reading_list/model/reading_list_test_utils.h"
 #import "ios/chrome/browser/safety_check/model/ios_chrome_safety_check_manager_factory.h"
@@ -239,7 +240,8 @@
         segmentation_platform::kEphemeralModuleBackendRankerTestOverride,
         "price_tracking_notification_promo");
     scoped_feature_list_.InitWithFeaturesAndParameters(
-        {{kMagicStack, {{kMagicStackMostVisitedModuleParam, "true"}}}}, {});
+        {{kMagicStack, {{kMagicStackMostVisitedModuleParam, "true"}}}},
+        {kIOSDisableParcelTracking});
 
     TestProfileIOS::Builder builder;
     builder.AddTestingFactory(
@@ -734,7 +736,7 @@
         {{segmentation_platform::features::
               kEphemeralCardRankerForceShowCardParam,
           segmentation_platform::kPriceTrackingNotificationPromo}}}},
-      {});
+      {kIOSDisableParcelTracking});
   commerce::MockShoppingService* shopping_service =
       static_cast<commerce::MockShoppingService*>(
           commerce::ShoppingServiceFactory::GetForProfile(GetProfile()));
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.h b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.h
index c2e8524b..08532a0 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.h
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.h
@@ -24,4 +24,10 @@
                                      AuthenticationService* auth_service,
                                      PrefService* pref_service);
 
+/// True if the user's preferred content size is an accessibility size.
+bool isContentOversized(id<UITraitEnvironment> trait_environment);
+
+/// Returns the dynamic height of the Magic Stack modules.
+CGFloat GetMagicStackHeight(id<UITraitEnvironment> trait_environment);
+
 #endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_MAGIC_STACK_MAGIC_STACK_UTILS_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.mm
index 9d608fe..694138d1 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack/magic_stack_utils.mm
@@ -59,3 +59,32 @@
            base::ToLowerASCII(GetCurrentCountryCode(
                GetApplicationContext()->GetVariationsService())) == "us"));
 }
+
+bool isContentOversized(id<UITraitEnvironment> trait_environment) {
+  // The preferred content size of the user's device.
+  NSString* preferred_content_size =
+      trait_environment.traitCollection.preferredContentSizeCategory;
+  NSComparisonResult result = UIContentSizeCategoryCompareToCategory(
+      preferred_content_size, UIContentSizeCategoryAccessibilityMedium);
+  return result != NSOrderedAscending;
+}
+
+CGFloat GetMagicStackHeight(id<UITraitEnvironment> trait_environment) {
+  // The preferred content size of the user's device.
+  NSString* preferred_content_size =
+      trait_environment.traitCollection.preferredContentSizeCategory;
+  if (isContentOversized(trait_environment)) {
+    // The maximum Magic Stack height in px.
+    return 190;
+  } else if (preferred_content_size ==
+             UIContentSizeCategoryExtraExtraExtraLarge) {
+    return 180;
+  } else if (preferred_content_size == UIContentSizeCategoryExtraExtraLarge) {
+    return 170;
+  } else if (preferred_content_size == UIContentSizeCategoryExtraLarge) {
+    return 160;
+  } else {
+    // The minimum Magic Stack height in px.
+    return 150;
+  }
+}
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_mediator_unittest.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_mediator_unittest.mm
index 1cef203..7fd1f88e 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_mediator_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_mediator_unittest.mm
@@ -12,6 +12,7 @@
 #import "ios/chrome/browser/first_run/ui_bundled/first_run_util.h"
 #import "ios/chrome/browser/metrics/model/constants.h"
 #import "ios/chrome/browser/ntp/model/set_up_list_prefs.h"
+#import "ios/chrome/browser/parcel_tracking/features.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
 #import "ios/chrome/browser/shared/model/prefs/pref_names.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
@@ -30,8 +31,8 @@
 class MagicStackHalfSheetMediatorTest : public PlatformTest {
  public:
   void SetUp() override {
-    scoped_feature_list_.InitWithFeatures({kTabResumption, kNewFeedPositioning},
-                                          {});
+    scoped_feature_list_.InitWithFeatures(
+        {kTabResumption, kNewFeedPositioning}, {kIOSDisableParcelTracking});
     pref_service_ = std::make_unique<TestingPrefServiceSimple>();
 
     // Necessary set up for kIOSSetUpList.
diff --git a/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_table_view_controller_unittest.mm b/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_table_view_controller_unittest.mm
index f525b17..2e2cd76 100644
--- a/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_table_view_controller_unittest.mm
+++ b/ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_table_view_controller_unittest.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/ui/content_suggestions/magic_stack_half_sheet_table_view_controller.h"
 
 #import "base/test/scoped_feature_list.h"
+#import "ios/chrome/browser/parcel_tracking/features.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_switch_item.h"
 #import "ios/chrome/browser/shared/ui/table_view/table_view_utils.h"
@@ -18,7 +19,8 @@
 class MagicStackHalfSheetTableViewControllerUnittest : public PlatformTest {
  public:
   void SetUp() override {
-    scoped_feature_list_.InitWithFeatures({kTabResumption}, {});
+    scoped_feature_list_.InitWithFeatures(
+        {kTabResumption}, {kIOSDisableParcelTracking});
 
     view_controller_ = [[MagicStackHalfSheetTableViewController alloc] init];
   }
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 01758e82..a3b7b59 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
@@ -35,7 +35,7 @@
 constexpr CGFloat kPadding = 15;
 
 // The spacing between the title and description labels.
-constexpr CGFloat kTextSpacing = 5;
+constexpr CGFloat kTextSpacing = 2;
 constexpr CGFloat kCompactTextSpacing = 0;
 
 // The duration of the icon / label crossfade animation.
@@ -152,12 +152,6 @@
           kTextSpacing,
       };
     }
-    if (@available(iOS 17, *)) {
-      NSArray<UITrait>* traits = TraitCollectionSetForTraits(
-          @[ UITraitPreferredContentSizeCategory.class ]);
-      [self registerForTraitChanges:traits
-                         withAction:@selector(hideDescriptionOnTraitChange)];
-    }
   }
   return self;
 }
@@ -183,11 +177,6 @@
   if (@available(iOS 17, *)) {
     return;
   }
-
-  if (previousTraitCollection.preferredContentSizeCategory !=
-      self.traitCollection.preferredContentSizeCategory) {
-    [self hideDescriptionOnTraitChange];
-  }
 }
 #endif
 
@@ -298,6 +287,8 @@
   textStack.axis = UILayoutConstraintAxisVertical;
   textStack.translatesAutoresizingMaskIntoConstraints = NO;
   textStack.spacing = _config.text_spacing;
+  textStack.maximumContentSizeCategory =
+      UIContentSizeCategoryAccessibilityMedium;
 
   // Add a horizontal stack to contain the icon(s) and the text stack.
   NSArray* arrangedSubviews = putIconInSquareBackground
@@ -358,6 +349,10 @@
   if (_complete) {
     label.attributedText = Strikethrough(label.text);
   }
+  // Set height constraint. Applicable for larger text.
+  [NSLayoutConstraint activateConstraints:@[ [label.heightAnchor
+                                              constraintEqualToConstant:26] ]];
+
   [label
       setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
                                       forAxis:UILayoutConstraintAxisVertical];
@@ -442,16 +437,6 @@
   }
 }
 
-// Hides `_description` if the font size category is larger than
-// extra-extra-large.
-- (void)hideDescriptionOnTraitChange {
-  _description.hidden = self.traitCollection.preferredContentSizeCategory >
-                        UIContentSizeCategoryExtraExtraLarge;
-  // Force a layout since the size of text components may have changed.
-  [self setNeedsLayout];
-  [self layoutIfNeeded];
-}
-
 #pragma mark - Private methods (animation helpers)
 
 // Sets the various subview properties that should be animated.
diff --git a/ios/chrome/common/credential_provider/constants.h b/ios/chrome/common/credential_provider/constants.h
index 4fefa4a7..2681ed3 100644
--- a/ios/chrome/common/credential_provider/constants.h
+++ b/ios/chrome/common/credential_provider/constants.h
@@ -23,18 +23,18 @@
 
 // Key for the app group user defaults containing whether saving passwords and
 // passkeys is currently enabled.
-NSString* AppGroupUserDefaulsCredentialProviderSavingPasswordsEnabled();
+NSString* AppGroupUserDefaultsCredentialProviderSavingPasswordsEnabled();
 
 // Key for the app group user defaults containing whether saving passwords is
 // currently managed by an enterprise policy.
-NSString* AppGroupUserDefaulsCredentialProviderSavingPasswordsManaged();
+NSString* AppGroupUserDefaultsCredentialProviderSavingPasswordsManaged();
 
 // Key for the app group user defaults indicating whether saving passkeys is
 // allowed by enterprise policy. Even if this is set to `YES`, passkey creation
 // could still be blocked by `...CredentialProviderSavingPasswordsEnabled`
 // above. This pref is only configurable by enterprise policy, not by users, so
 // if this is set to `NO` then it is because the behavior is managed.
-NSString* AppGroupUserDefaulsCredentialProviderSavingPasskeysEnabled();
+NSString* AppGroupUserDefaultsCredentialProviderSavingPasskeysEnabled();
 
 // Key for the app group user defaults containing whether syncing passwords is
 // currently enabled.
@@ -44,6 +44,10 @@
 // currently enabled.
 NSString* AppGroupUserDefaulsCredentialProviderPasskeyPRFEnabled();
 
+// Key for the app group user defaults containing whether the passkeys M2
+// feature is currently enabled.
+NSString* AppGroupUserDefaultsCredentialProviderPasskeysM2Enabled();
+
 // Key for the app group user defaults indicating if the credentials have been
 // synced with iOS via AuthenticationServices.
 extern NSString* const
diff --git a/ios/chrome/common/credential_provider/constants.mm b/ios/chrome/common/credential_provider/constants.mm
index e606f7b..e736ca4 100644
--- a/ios/chrome/common/credential_provider/constants.mm
+++ b/ios/chrome/common/credential_provider/constants.mm
@@ -61,6 +61,11 @@
 NSString* const kUserDefaultsCredentialProviderPasskeyPRFSetting =
     @"kUserDefaultsCredentialProviderPasskeyPRFSetting";
 
+// Used to generate the key for the app group user defaults containing whether
+// the passkeys M2 feature is currently enabled.
+NSString* const kUserDefaultsCredentialProviderPasskeysM2Enabled =
+    @"kUserDefaultsCredentialProviderPasskeysM2Enabled";
+
 // Used to generate a unique AppGroupPrefix to differentiate between different
 // versions of Chrome running in the same device.
 NSString* AppGroupPrefix() {
@@ -115,19 +120,19 @@
       stringByAppendingString:kUserDefaultsCredentialProviderNewCredentials];
 }
 
-NSString* AppGroupUserDefaulsCredentialProviderSavingPasswordsEnabled() {
+NSString* AppGroupUserDefaultsCredentialProviderSavingPasswordsEnabled() {
   return [AppGroupPrefix()
       stringByAppendingString:
           kUserDefaulsCredentialProviderSavingPasswordsEnabled];
 }
 
-NSString* AppGroupUserDefaulsCredentialProviderSavingPasswordsManaged() {
+NSString* AppGroupUserDefaultsCredentialProviderSavingPasswordsManaged() {
   return [AppGroupPrefix()
       stringByAppendingString:
           kUserDefaultsCredentialProviderSavingPasswordsManaged];
 }
 
-NSString* AppGroupUserDefaulsCredentialProviderSavingPasskeysEnabled() {
+NSString* AppGroupUserDefaultsCredentialProviderSavingPasskeysEnabled() {
   return [AppGroupPrefix()
       stringByAppendingString:
           kUserDefaulsCredentialProviderSavingPasskeysEnabled];
@@ -143,3 +148,8 @@
   return [AppGroupPrefix()
       stringByAppendingString:kUserDefaultsCredentialProviderPasskeyPRFSetting];
 }
+
+NSString* AppGroupUserDefaultsCredentialProviderPasskeysM2Enabled() {
+  return [AppGroupPrefix()
+      stringByAppendingString:kUserDefaultsCredentialProviderPasskeysM2Enabled];
+}
diff --git a/ios/chrome/credential_provider_extension/ui/feature_flags.h b/ios/chrome/credential_provider_extension/ui/feature_flags.h
index d72b056..fd4112a 100644
--- a/ios/chrome/credential_provider_extension/ui/feature_flags.h
+++ b/ios/chrome/credential_provider_extension/ui/feature_flags.h
@@ -27,4 +27,7 @@
 // policy.
 BOOL IsPasskeyCreationAllowedByPolicy();
 
+// Whether the passkeys M2 feature is currently enabled.
+BOOL IsPasskeysM2Enabled();
+
 #endif  // IOS_CHROME_CREDENTIAL_PROVIDER_EXTENSION_UI_FEATURE_FLAGS_H_
diff --git a/ios/chrome/credential_provider_extension/ui/feature_flags.mm b/ios/chrome/credential_provider_extension/ui/feature_flags.mm
index 02d63af..e7cee2cb 100644
--- a/ios/chrome/credential_provider_extension/ui/feature_flags.mm
+++ b/ios/chrome/credential_provider_extension/ui/feature_flags.mm
@@ -17,14 +17,14 @@
 BOOL IsPasswordCreationUserEnabled() {
   return [[app_group::GetGroupUserDefaults()
       objectForKey:
-          AppGroupUserDefaulsCredentialProviderSavingPasswordsEnabled()]
+          AppGroupUserDefaultsCredentialProviderSavingPasswordsEnabled()]
       boolValue];
 }
 
 BOOL IsPasswordCreationManaged() {
   return [[app_group::GetGroupUserDefaults()
       objectForKey:
-          AppGroupUserDefaulsCredentialProviderSavingPasswordsManaged()]
+          AppGroupUserDefaultsCredentialProviderSavingPasswordsManaged()]
       boolValue];
 }
 
@@ -36,6 +36,13 @@
 
 BOOL IsPasskeyCreationAllowedByPolicy() {
   return [[app_group::GetGroupUserDefaults()
-      objectForKey:AppGroupUserDefaulsCredentialProviderSavingPasskeysEnabled()]
+      objectForKey:
+          AppGroupUserDefaultsCredentialProviderSavingPasskeysEnabled()]
+      boolValue];
+}
+
+BOOL IsPasskeysM2Enabled() {
+  return [[app_group::GetGroupUserDefaults()
+      objectForKey:AppGroupUserDefaultsCredentialProviderPasskeysM2Enabled()]
       boolValue];
 }
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index beb6b036f..6c80aaf0 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -101,6 +101,7 @@
     "//components/translate/core/browser",
     "//components/unified_consent",
     "//components/variations",
+    "//components/variations/service",
     "//ios/chrome/app:app_internal",
     "//ios/chrome/app/application_delegate:app_state",
     "//ios/chrome/app/strings",
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.h b/ios/chrome/test/earl_grey/chrome_earl_grey.h
index 8f820f7..5a8f57c 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.h
@@ -993,6 +993,11 @@
 
 - (void)requestTipsNotification:(TipsNotificationType)type;
 
+#pragma mark - Variations Utilities
+
+// Forces an override of the variations stored permanent country.
+- (void)overrideVariationsServiceStoredPermanentCountry:(NSString*)country;
+
 @end
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_EARL_GREY_H_
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey.mm b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
index 0375cb8..285bae50 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey.mm
@@ -1902,8 +1902,17 @@
   return [ChromeEarlGreyAppInterface hasFirstRunSentinel];
 }
 
+#pragma mark - Notification Utilities
+
 - (void)requestTipsNotification:(TipsNotificationType)type {
   return [ChromeEarlGreyAppInterface requestTipsNotification:type];
 }
 
+#pragma mark - Variations Utilities
+
+- (void)overrideVariationsServiceStoredPermanentCountry:(NSString*)country {
+  return [ChromeEarlGreyAppInterface
+      overrideVariationsServiceStoredPermanentCountry:country];
+}
+
 @end
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
index eddae7c6a..25640ab 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.h
@@ -744,6 +744,11 @@
 
 + (void)requestTipsNotification:(TipsNotificationType)type;
 
+#pragma mark - Variations Utilities
+
+// Forces an override of the variations stored permanent country.
++ (void)overrideVariationsServiceStoredPermanentCountry:(NSString*)country;
+
 @end
 
 #endif  // IOS_CHROME_TEST_EARL_GREY_CHROME_EARL_GREY_APP_INTERFACE_H_
diff --git a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
index 51999e6e..e34cdff 100644
--- a/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
+++ b/ios/chrome/test/earl_grey/chrome_earl_grey_app_interface.mm
@@ -33,6 +33,7 @@
 #import "components/sync/service/sync_user_settings.h"
 #import "components/sync/test/fake_server_http_post_provider.h"
 #import "components/unified_consent/unified_consent_service.h"
+#import "components/variations/service/variations_service.h"
 #import "components/variations/variations_associated_data.h"
 #import "components/variations/variations_ids_provider.h"
 #import "ios/chrome/app/application_delegate/app_state.h"
@@ -1568,6 +1569,8 @@
   return HasFirstRunSentinel();
 }
 
+#pragma mark - Notification Utilities
+
 + (void)requestTipsNotification:(TipsNotificationType)type {
   UNUserNotificationCenter* center =
       UNUserNotificationCenter.currentNotificationCenter;
@@ -1580,4 +1583,13 @@
   [center addNotificationRequest:request withCompletionHandler:nil];
 }
 
+#pragma mark - Variations Utilities
+
++ (void)overrideVariationsServiceStoredPermanentCountry:(NSString*)country {
+  std::string UTF8Country = base::SysNSStringToUTF8(country);
+  variations::VariationsService* variationsService =
+      GetApplicationContext()->GetVariationsService();
+  variationsService->OverrideStoredPermanentCountry(UTF8Country);
+}
+
 @end
diff --git a/ios/components/security_interstitials/safe_browsing/OWNERS b/ios/components/security_interstitials/safe_browsing/OWNERS
new file mode 100644
index 0000000..4db0888
--- /dev/null
+++ b/ios/components/security_interstitials/safe_browsing/OWNERS
@@ -0,0 +1 @@
+danieltwhite@google.com
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
index 62a3c003..39711a4 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-a5e717b5d9fe678a8a06950824a2a6f6395067aa
\ No newline at end of file
+76e0304f3c7eb54da7758cefe02c7ecabbb9f69e
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
index 920f468..8fd3b64 100644
--- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-2800e223789d188c394f99ff6b471e9f3fbecac5
\ No newline at end of file
+532e168f087d8ad075ba0c7551aea0dde5096c22
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
index e404a8c..150dc0fd 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-961c51dc2c57cc9d88a98fd4d34fa6fb8298d88d
\ No newline at end of file
+6206a84499863947808c3871c4932c7361153511
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
index 4f7acfad..0c261619 100644
--- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-cce7637b65bdb339f2766d212060283e44c07975
\ No newline at end of file
+10b2e19eb1abc053496245e25efe717546b849e8
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
index 29a757f..9e7276ec 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@
-9049c1bfdc4a50fc1a9fb21c43a9bcd26c0d33d5
\ No newline at end of file
+ed25f56d9cafd447debe0262db98203e8032f16d
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
index 0c9b88d2..e44618f 100644
--- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@
-9f0d93ec5443360075bc72873bb8dd34f3903715
\ No newline at end of file
+11e061b6a7c3d0d3a3fdd18ec143b58690df31d9
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
index 704c59f..b1e1b8d 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-a8a5fcbd6421119f00a7d00b38f1866b2bdac8d1
\ No newline at end of file
+1f39d1e56905252e310f062087c77230aab860a1
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
index 42ee2b79..57273719 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-7255f07c433e3e21c75ce11823280277337829c5
\ No newline at end of file
+2d0f5bd377e4064f59214f258133bd4499b06505
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
index 0a8a3883..c7f91719 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-783e9e5d44efe6c0f6aa6b576891f9ff501c5cb5
\ No newline at end of file
+dcba949bedb460b8e214f8aed332657843740c4c
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
index f93593c8..7d97f43d 100644
--- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-491450aeae4618be9a377289eeda240120fb0afc
\ No newline at end of file
+ae436559871bd938b8e5638c56ea1044463615d8
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
index 7a9eeca..15e1d2d 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-9af7806bb62c900c0a79400d2a1c456c004ba147
\ No newline at end of file
+6757346754c96c56fe45f5a98667b98269c2ac33
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
index 98fa7a0..a9f4fe2 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-5ab6e9eecdf7523c45b8ff8bd5783b136ea97013
\ No newline at end of file
+b47722f65ee17ef3e22bd3e282fb1b8ffdcba974
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
index d2b4480..4114bac8 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-4e230f53f8790e654608aac45150e2301621464e
\ No newline at end of file
+843a5e0c466fe3a2ddf4836bb2381099baa603cc
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
index f6ae8ead..9838fde 100644
--- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-1c665c7894774e6661a78666515a5680ad4a9a09
\ No newline at end of file
+a91e07a6dc2c84ebb6fd59e70b3fce5435ba238a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
index abeb4b4..031a1f91 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-653662a2d00744062a57010a0d1cb889aad94777
\ No newline at end of file
+b6cc3af165fca4418cf4bdb3b03d05a24cd2a602
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
index 984c65b..0ba44d72 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-d7389179d2efedc9497d72a7a8e8d3f316893041
\ No newline at end of file
+4ecfcad9aa77c3410767d90c8da5dd6140ed7291
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
index 37d9a869..61d05fa0 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-328ce723fb81728e175e92d8ba557f983fade509
\ No newline at end of file
+f108fa143b64e64baaf79e51916a8f5dd4f4324a
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 184f1962..b5f0504 100644
--- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-00f228b0cde3aacdfe4345c895f8717fa488f8a3
\ No newline at end of file
+852457ea05e2b15d809500d5346b0450d7c7a205
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
index a8f1383..cf9dc4d0 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-64c923742881c016b12ebfd1058781d9ba7d6fda
\ No newline at end of file
+e65f82af4d169a9c7dd6fa7de9bd5c459843d927
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
index 8fdeb92..80c3015 100644
--- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-4be27ec6957a30b889b0deb554d2fc6593c68369
\ No newline at end of file
+780c83d6c3786b8ef78c770ef11a246ccc8f05a5
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
index f413614..a4dbb50 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-11e90efb75c21f121dd818bff3cac0b18f478f69
\ No newline at end of file
+cd0fb916d552c46c352177e9e4ddfc63bd0123eb
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
index 53e05c5..91748bc 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-51f074d9e251145b4cb6bbaca2ecde1aceb7631f
\ No newline at end of file
+afc549af00f08509354b7b5298b2fe9bc90cc7cf
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
index ad39524..18bd54a 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-dc304d6d69b3e4dda0417c5888f6c6a9ccaa5d93
\ No newline at end of file
+2a37ae8e058999ee85c191d8a2ddf422189f28a2
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 47ee08b..f825f2e 100644
--- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-cab653359c1e8180398bfab476c9bac66b7882a6
\ No newline at end of file
+13fae50fffe710fb0790f93457b47d3c0dbd5883
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
index 61660b0..bcc0dc6b 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@
-4b286483aaa0f1c6a7f29320f0ec69e104883304
\ No newline at end of file
+f24c423da6b26b884cc3e77826e1bc21c015aebd
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
index 805be703..288ea38 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@
-8f80106257c79a626dfb214dd2d5ff7dc16ad826
\ No newline at end of file
+f769d2c3d3ffb1cdb1deb97171472319632f9934
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
index 63e8e8b..bf624ae 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@
-b579b34f8a078931ab70044e702f2dd4db5e7c52
\ No newline at end of file
+19e435d22d03f140ff358aac55cced87bb8c1d20
\ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
index 8583f49..2fbbdf30 100644
--- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
+++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@
-fd223a2b5fab49f82015a503ecc5e1ffb6f2a205
\ No newline at end of file
+2b1790089ab632fd3b53c9da5554493f68175e90
\ No newline at end of file
diff --git a/ios_internal b/ios_internal
index 46e2e65..63647fd 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 46e2e655940ecae3d363c4f438c1aabeaa5669e8
+Subproject commit 63647fd54313ac65206bb1093278b8e2e73bfcf8
diff --git a/media/gpu/chromeos/frame_resource.h b/media/gpu/chromeos/frame_resource.h
index cbe25d4..50dd11ef 100644
--- a/media/gpu/chromeos/frame_resource.h
+++ b/media/gpu/chromeos/frame_resource.h
@@ -98,11 +98,6 @@
   virtual std::unique_ptr<VideoFrame::ScopedMapping> MapGMBOrSharedImage()
       const = 0;
 
-  // Returns an identifier based on the frame data's underlying storage. This
-  // returns consistent results even if the frame gets wrapped. Returns an
-  // invalid GenericSharedMemoryId if an identifier cannot be determined.
-  virtual gfx::GenericSharedMemoryId GetSharedMemoryId() const = 0;
-
   virtual const VideoFrameLayout& layout() const = 0;
 
   virtual VideoPixelFormat format() const = 0;
diff --git a/media/gpu/chromeos/native_pixmap_frame_resource.cc b/media/gpu/chromeos/native_pixmap_frame_resource.cc
index cd9c02b7..528e4864 100644
--- a/media/gpu/chromeos/native_pixmap_frame_resource.cc
+++ b/media/gpu/chromeos/native_pixmap_frame_resource.cc
@@ -305,7 +305,7 @@
   gmb_handle.type = gfx::GpuMemoryBufferType::NATIVE_PIXMAP;
   // |gmb_handle.id| is set to the GenericSharedMemoryId from |this|. This
   // allows for more predictable caching when converting to a VideoFrame.
-  gmb_handle.id = GetSharedMemoryId();
+  gmb_handle.id = id_;
   gmb_handle.native_pixmap_handle = std::move(native_pixmap_handle);
   return gmb_handle;
 }
@@ -317,11 +317,6 @@
   return nullptr;
 }
 
-gfx::GenericSharedMemoryId NativePixmapFrameResource::GetSharedMemoryId()
-    const {
-  return id_;
-}
-
 const VideoFrameLayout& NativePixmapFrameResource::layout() const {
   return layout_;
 }
@@ -430,8 +425,8 @@
   // constructor.
   auto wrapping_frame = base::WrapRefCounted<NativePixmapFrameResource>(
       new NativePixmapFrameResource(layout(), visible_rect, natural_size,
-                                    timestamp(), GetSharedMemoryId(),
-                                    tracking_token(), buffer_usage_, pixmap_));
+                                    timestamp(), id_, tracking_token(),
+                                    buffer_usage_, pixmap_));
 
   // All other metadata is copied to the "wrapping" frame.
   wrapping_frame->metadata().MergeMetadataFrom(metadata());
diff --git a/media/gpu/chromeos/native_pixmap_frame_resource.h b/media/gpu/chromeos/native_pixmap_frame_resource.h
index 3d942f74..a02275f5 100644
--- a/media/gpu/chromeos/native_pixmap_frame_resource.h
+++ b/media/gpu/chromeos/native_pixmap_frame_resource.h
@@ -74,14 +74,13 @@
   scoped_refptr<const gfx::NativePixmapDmaBuf> GetNativePixmapDmaBuf()
       const override;
   // CreateGpuMemoryBufferHandle() will duplicate file descriptors to make a
-  // gfx::GpuMemoryBufferHandle. The GpuMemoryBufferId will match
-  // GetSharedMemoryId(). Doing this helps with identification of original
-  // FrameResource from a VideoFrame produced by CreateVideoFrame().
+  // gfx::GpuMemoryBufferHandle. The GpuMemoryBufferId will be set to a
+  // consistent value in subsequent calls for |this| or for any wrapping frame
+  // of |this|.
   gfx::GpuMemoryBufferHandle CreateGpuMemoryBufferHandle() const override;
   // Always returns nullptr.
   std::unique_ptr<VideoFrame::ScopedMapping> MapGMBOrSharedImage()
       const override;
-  gfx::GenericSharedMemoryId GetSharedMemoryId() const override;
   const VideoFrameLayout& layout() const override;
   VideoPixelFormat format() const override;
   int stride(size_t plane) const override;
@@ -148,7 +147,9 @@
   // is wrapped, |id_| is copied to the wrapping frame. The ID's will be unique
   // per underlying NativePixmapDmaBuf object, per process. The ID is generated
   // by and stored in NativePixmapFrameResource because the ID returned by
-  // gxf::NativePixmapDmaBuf::GetUniqueId() is currently always zero.
+  // gxf::NativePixmapDmaBuf::GetUniqueId() is currently always zero. It is used
+  // by CreateGpuMemoryBufferHandle to create GpuMemoryBufferHandle's with
+  // consistent ID's.
   const gfx::GenericSharedMemoryId id_;
 
   // |buffer_usage_| affects how a buffer can be used. It is only set if it was
diff --git a/media/gpu/chromeos/video_frame_resource.cc b/media/gpu/chromeos/video_frame_resource.cc
index 225954b..ad11f6f 100644
--- a/media/gpu/chromeos/video_frame_resource.cc
+++ b/media/gpu/chromeos/video_frame_resource.cc
@@ -86,10 +86,6 @@
   return frame_->MapGMBOrSharedImage();
 }
 
-gfx::GenericSharedMemoryId VideoFrameResource::GetSharedMemoryId() const {
-  return media::GetSharedMemoryId(*frame_);
-}
-
 const VideoFrameLayout& VideoFrameResource::layout() const {
   return frame_->layout();
 }
diff --git a/media/gpu/chromeos/video_frame_resource.h b/media/gpu/chromeos/video_frame_resource.h
index 87e529d..51827af 100644
--- a/media/gpu/chromeos/video_frame_resource.h
+++ b/media/gpu/chromeos/video_frame_resource.h
@@ -40,7 +40,6 @@
   gfx::GpuMemoryBufferHandle CreateGpuMemoryBufferHandle() const override;
   std::unique_ptr<VideoFrame::ScopedMapping> MapGMBOrSharedImage()
       const override;
-  gfx::GenericSharedMemoryId GetSharedMemoryId() const override;
   const VideoFrameLayout& layout() const override;
   VideoPixelFormat format() const override;
   int stride(size_t plane) const override;
diff --git a/media/mojo/clients/mojo_stable_video_decoder.cc b/media/mojo/clients/mojo_stable_video_decoder.cc
index f0dc61c1..039e01f 100644
--- a/media/mojo/clients/mojo_stable_video_decoder.cc
+++ b/media/mojo/clients/mojo_stable_video_decoder.cc
@@ -129,16 +129,14 @@
       return nullptr;
     }
 
-    return base::WrapRefCounted(new SharedImageHolder(
-        frame_resource->GetSharedMemoryId(), std::move(new_client_shared_image),
-        frame_resource->ColorSpace(), std::move(sii)));
+    return base::WrapRefCounted(
+        new SharedImageHolder(std::move(new_client_shared_image),
+                              frame_resource->ColorSpace(), std::move(sii)));
   }
 
   SharedImageHolder(const SharedImageHolder&) = delete;
   SharedImageHolder& operator=(const SharedImageHolder&) = delete;
 
-  gfx::GenericSharedMemoryId id() const { return id_; }
-
   const scoped_refptr<gpu::ClientSharedImage> client_shared_image() const {
     return client_shared_image_;
   }
@@ -165,18 +163,15 @@
  private:
   friend class base::RefCountedThreadSafe<SharedImageHolder>;
 
-  SharedImageHolder(gfx::GenericSharedMemoryId id,
-                    scoped_refptr<gpu::ClientSharedImage> client_shared_image,
+  SharedImageHolder(scoped_refptr<gpu::ClientSharedImage> client_shared_image,
                     const gfx::ColorSpace& color_space,
                     scoped_refptr<gpu::SharedImageInterface> sii)
-      : id_(id),
-        color_space_(color_space),
+      : color_space_(color_space),
         sii_(std::move(sii)),
         client_shared_image_(std::move(client_shared_image)) {}
 
   ~SharedImageHolder() { CHECK(client_shared_image_->HasOneRef()); }
 
-  const gfx::GenericSharedMemoryId id_;
   const gfx::ColorSpace color_space_;
   const scoped_refptr<gpu::SharedImageInterface> sii_;
 
@@ -341,13 +336,14 @@
     const scoped_refptr<FrameResource>& frame_resource) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  const gfx::GenericSharedMemoryId frame_id =
-      frame_resource->GetSharedMemoryId();
-  CHECK(frame_id.is_valid());
+  const base::UnguessableToken frame_token = frame_resource->tracking_token();
+  CHECK(!frame_token.is_empty());
 
   // First, let's see if the buffer for this frame already has a SharedImage
   // that can be re-used.
-  SharedImageHolder* shared_image_holder = shared_images_.Lookup(frame_id);
+  const auto iter = shared_images_.find(frame_token);
+  SharedImageHolder* shared_image_holder =
+      iter != shared_images_.end() ? iter->second.get() : nullptr;
   if (shared_image_holder &&
       shared_image_holder->IsCompatibleWith(frame_resource)) {
     shared_image_holder->Update();
@@ -370,7 +366,7 @@
     // SharedImage if the user of the decoded frames still hasn't released all
     // frames that use that SharedImage.
     shared_image_holder = nullptr;
-    shared_images_.Replace(frame_id, new_shared_image);
+    shared_images_.insert_or_assign(frame_token, new_shared_image);
   } else {
     // In this case, the buffer does not have a SharedImage associated with it.
     // Therefore, we need to ask the containing FrameResource to notify us when
@@ -379,22 +375,21 @@
     FrameResource* original_frame_resource =
         oop_video_decoder()->GetOriginalFrame(frame_resource->tracking_token());
     CHECK(original_frame_resource);
-    shared_images_.AddWithID(new_shared_image, frame_id);
+    shared_images_.insert_or_assign(frame_token, new_shared_image);
     original_frame_resource->AddDestructionObserver(
         base::BindPostTaskToCurrentDefault(
             base::BindOnce(&MojoStableVideoDecoder::UnregisterSharedImage,
-                           weak_this_factory_.GetWeakPtr(), frame_id)));
+                           weak_this_factory_.GetWeakPtr(), frame_token)));
   }
 
   return new_shared_image;
 }
 
 void MojoStableVideoDecoder::UnregisterSharedImage(
-    gfx::GenericSharedMemoryId frame_id) {
+    base::UnguessableToken frame_token) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  CHECK(frame_id.is_valid());
-  CHECK(shared_images_.Lookup(frame_id));
-  shared_images_.Remove(frame_id);
+  CHECK(!frame_token.is_empty());
+  CHECK_EQ(1u, shared_images_.erase(frame_token));
 }
 
 void MojoStableVideoDecoder::OnFrameResourceDecoded(
diff --git a/media/mojo/clients/mojo_stable_video_decoder.h b/media/mojo/clients/mojo_stable_video_decoder.h
index fbefdecf..45d683bb 100644
--- a/media/mojo/clients/mojo_stable_video_decoder.h
+++ b/media/mojo/clients/mojo_stable_video_decoder.h
@@ -5,7 +5,7 @@
 #ifndef MEDIA_MOJO_CLIENTS_MOJO_STABLE_VIDEO_DECODER_H_
 #define MEDIA_MOJO_CLIENTS_MOJO_STABLE_VIDEO_DECODER_H_
 
-#include "base/containers/id_map.h"
+#include "base/containers/flat_map.h"
 #include "base/containers/lru_cache.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
@@ -95,7 +95,7 @@
   scoped_refptr<SharedImageHolder> CreateOrUpdateSharedImageForFrame(
       const scoped_refptr<FrameResource>& frame_resource);
 
-  void UnregisterSharedImage(gfx::GenericSharedMemoryId frame_id);
+  void UnregisterSharedImage(base::UnguessableToken frame_token);
 
   void OnFrameResourceDecoded(scoped_refptr<FrameResource> frame_resource);
 
@@ -136,8 +136,7 @@
   // possible. The fact that we keep a reference to a SharedImageHolder
   // guarantees that the corresponding SharedImage lives for at least as long as
   // the OOPVideoDecoder knows about the corresponding buffer.
-  base::IDMap</*V=*/scoped_refptr<SharedImageHolder>,
-              /*K=*/gfx::GenericSharedMemoryId>
+  base::flat_map<base::UnguessableToken, scoped_refptr<SharedImageHolder>>
       shared_images_ GUARDED_BY_CONTEXT(sequence_checker_);
 
   OutputCB output_cb_ GUARDED_BY_CONTEXT(sequence_checker_);
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index d14df39f..7e64f9a 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -730,7 +730,7 @@
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   std::optional<base::ElapsedTimer> timer;
-  if (get_cookie_list_timing_subsampler_.ShouldSample(0.001)) {
+  if (metrics_subsampler_.ShouldSample(0.001)) {
     timer.emplace();
   }
 
@@ -1409,8 +1409,11 @@
     CookieAccessResult& access_result = cookie_result.second;
 
     // We want to collect these metrics for cookies that would be included
-    // without considering shadowing domain cookies.
-    if (access_result.status.IsInclude()) {
+    // without considering shadowing domain cookies. Recording them on every
+    // resource sequest results in unnecessarily large amounts of samples
+    // and has a non-zero runtime cost, so only collect 1/1000 times.
+    if (metrics_subsampler_.ShouldSample(0.001) &&
+        access_result.status.IsInclude()) {
       int destination_port = url.EffectiveIntPort();
 
       if (IsLocalhost(url)) {
diff --git a/net/cookies/cookie_monster.h b/net/cookies/cookie_monster.h
index e0ded49..da8bf6df 100644
--- a/net/cookies/cookie_monster.h
+++ b/net/cookies/cookie_monster.h
@@ -813,7 +813,7 @@
 
   bool persist_session_cookies_ = false;
 
-  base::MetricsSubSampler get_cookie_list_timing_subsampler_;
+  base::MetricsSubSampler metrics_subsampler_;
 
   THREAD_CHECKER(thread_checker_);
 
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index f3ebb52..da0e755 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -24,6 +24,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/metrics/histogram.h"
 #include "base/metrics/histogram_samples.h"
+#include "base/rand_util.h"
 #include "base/ranges/algorithm.h"
 #include "base/run_loop.h"
 #include "base/strings/strcat.h"
@@ -6433,6 +6434,10 @@
 }
 
 TEST_F(CookieMonsterTest, CookiePortReadHistogram) {
+  // The per-resource cookie histograms are subsampled, simulate for this test
+  // that the dice roll makes them record.
+  base::MetricsSubSampler::ScopedAlwaysSampleForTesting no_subsampling;
+
   base::HistogramTester histograms;
   const char kHistogramName[] = "Cookie.Port.Read.RemoteHost";
   const char kHistogramNameLocal[] = "Cookie.Port.Read.Localhost";
@@ -6538,6 +6543,10 @@
 }
 
 TEST_F(CookieMonsterTest, CookiePortReadDiffersFromSetHistogram) {
+  // The per-resource cookie histograms are subsampled, simulate for this test
+  // that the dice roll makes them record.
+  base::MetricsSubSampler::ScopedAlwaysSampleForTesting no_subsampling;
+
   base::HistogramTester histograms;
   const char kHistogramName[] = "Cookie.Port.ReadDiffersFromSet.RemoteHost";
   const char kHistogramNameLocal[] = "Cookie.Port.ReadDiffersFromSet.Localhost";
diff --git a/net/log/trace_net_log_observer.cc b/net/log/trace_net_log_observer.cc
index 7e5f791..0faf0fe 100644
--- a/net/log/trace_net_log_observer.cc
+++ b/net/log/trace_net_log_observer.cc
@@ -55,6 +55,9 @@
 
 void TraceNetLogObserver::OnAddEntry(const NetLogEntry& entry) {
   base::Value::Dict params = entry.params.Clone();
+  // Add source's start time as a parameter. The net-log viewer requires it.
+  params.Set("source_start_time",
+             NetLog::TickCountToString(entry.source.start_time));
   switch (entry.phase) {
     case NetLogEventPhase::BEGIN:
       TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(
diff --git a/net/log/trace_net_log_observer_unittest.cc b/net/log/trace_net_log_observer_unittest.cc
index 051a610..0a77aa2 100644
--- a/net/log/trace_net_log_observer_unittest.cc
+++ b/net/log/trace_net_log_observer_unittest.cc
@@ -435,11 +435,17 @@
       item1->GetDict().FindStringByDottedPath("args.params.foo");
   ASSERT_TRUE(item1_params);
   EXPECT_EQ("bar", *item1_params);
+  const std::string* item1_param_source_start_time =
+      item1->GetDict().FindStringByDottedPath("args.params.source_start_time");
+  EXPECT_NE(item1_param_source_start_time, nullptr);
 
-  // Perfetto tracing backend skips empty args.
+  // Events emitted by TraceNetLogObserver always have params.
   const base::Value::Dict* item2_args =
       item2->GetDict().FindDictByDottedPath("args");
-  EXPECT_FALSE(item2_args->contains("params"));
+  EXPECT_TRUE(item2_args->contains("params"));
+  const std::string* item2_param_source_start_time =
+      item2->GetDict().FindStringByDottedPath("args.params.source_start_time");
+  EXPECT_NE(item2_param_source_start_time, nullptr);
 }
 
 TEST(TraceNetLogObserverCategoryTest, DisabledCategory) {
diff --git a/printing/backend/cups_helper.cc b/printing/backend/cups_helper.cc
index 2d63c3f..20860c5 100644
--- a/printing/backend/cups_helper.cc
+++ b/printing/backend/cups_helper.cc
@@ -163,21 +163,22 @@
   std::vector<gfx::Size> dpis;
   gfx::Size default_dpi;
   if (res) {
-    UNSAFE_TODO({
-      for (int i = 0; i < res->num_choices; i++) {
-        char* choice = res->choices[i].choice;
-        CHECK(choice);
-        std::optional<gfx::Size> parsed_size = ParseResolutionString(choice);
-        if (!parsed_size.has_value()) {
-          continue;
-        }
-
-        dpis.push_back(parsed_size.value());
-        if (!strcmp(choice, res->defchoice)) {
-          default_dpi = dpis.back();
-        }
+    // SAFETY: Required from CUPS.
+    auto choices = UNSAFE_BUFFERS(base::span<const ppd_choice_t>(
+        res->choices, static_cast<size_t>(res->num_choices)));
+    for (const auto& choice : choices) {
+      const char* choice_str = choice.choice;
+      CHECK(choice_str);
+      std::optional<gfx::Size> parsed_size = ParseResolutionString(choice_str);
+      if (!parsed_size.has_value()) {
+        continue;
       }
-    });
+
+      dpis.push_back(parsed_size.value());
+      if (!strcmp(choice_str, res->defchoice)) {
+        default_dpi = dpis.back();
+      }
+    }
   } else {
     // If there is no resolution option, then check for a standalone
     // DefaultResolution.
@@ -865,63 +866,62 @@
     VLOG(1) << "Paper list size - " << ppd->num_sizes;
     ppd_option_t* paper_option = ppdFindOption(ppd, kPageSize);
     bool is_default_found = false;
-    UNSAFE_TODO({
-      for (int i = 0; i < ppd->num_sizes; ++i) {
-        const gfx::Size paper_size_um(
-            ConvertUnit(ppd->sizes[i].width, kPointsPerInch, kMicronsPerInch),
-            ConvertUnit(ppd->sizes[i].length, kPointsPerInch, kMicronsPerInch));
-        if (!paper_size_um.IsEmpty()) {
-          std::string display_name;
-          if (paper_option) {
-            ppd_choice_t* paper_choice =
-                ppdFindChoice(paper_option, ppd->sizes[i].name);
-            // Human readable paper name should be UTF-8 encoded, but some PPDs
-            // do not follow this standard.
-            if (paper_choice && base::IsStringUTF8(paper_choice->text)) {
-              display_name = paper_choice->text;
-            }
-          }
-          int printable_area_left_um =
-              ConvertUnit(ppd->sizes[i].left, kPointsPerInch, kMicronsPerInch);
-          int printable_area_bottom_um = ConvertUnit(
-              ppd->sizes[i].bottom, kPointsPerInch, kMicronsPerInch);
-          // ppd->sizes[i].right is the horizontal distance from the left of the
-          // paper to the right of the printable area.
-          int printable_area_right_um =
-              ConvertUnit(ppd->sizes[i].right, kPointsPerInch, kMicronsPerInch);
-          // ppd->sizes[i].top is the vertical distance from the bottom of the
-          // paper to the top of the printable area.
-          int printable_area_top_um =
-              ConvertUnit(ppd->sizes[i].top, kPointsPerInch, kMicronsPerInch);
-
-          gfx::Rect printable_area_um(
-              printable_area_left_um, printable_area_bottom_um,
-              /*width=*/printable_area_right_um - printable_area_left_um,
-              /*height=*/printable_area_top_um - printable_area_bottom_um);
-
-          // Default to the paper size if printable area is empty.
-          // We've seen some drivers have a printable area that goes out of
-          // bounds of the paper size. In those cases, set the printable area to
-          // be the size. (See crbug.com/1412305.)
-          const gfx::Rect size_um_rect = gfx::Rect(paper_size_um);
-          if (printable_area_um.IsEmpty() ||
-              !size_um_rect.Contains(printable_area_um)) {
-            printable_area_um = size_um_rect;
-          }
-
-          PrinterSemanticCapsAndDefaults::Paper paper(
-              display_name,
-              /*vendor_id=*/ppd->sizes[i].name, paper_size_um,
-              printable_area_um);
-
-          caps.papers.push_back(paper);
-          if (ppd->sizes[i].marked) {
-            caps.default_paper = paper;
-            is_default_found = true;
+    // SAFETY: Required from CUPS.
+    auto sizes = UNSAFE_BUFFERS(base::span<const ppd_size_t>(
+        ppd->sizes, static_cast<size_t>(ppd->num_sizes)));
+    for (const auto& size : sizes) {
+      const gfx::Size paper_size_um(
+          ConvertUnit(size.width, kPointsPerInch, kMicronsPerInch),
+          ConvertUnit(size.length, kPointsPerInch, kMicronsPerInch));
+      if (!paper_size_um.IsEmpty()) {
+        std::string display_name;
+        if (paper_option) {
+          ppd_choice_t* paper_choice = ppdFindChoice(paper_option, size.name);
+          // Human readable paper name should be UTF-8 encoded, but some PPDs
+          // do not follow this standard.
+          if (paper_choice && base::IsStringUTF8(paper_choice->text)) {
+            display_name = paper_choice->text;
           }
         }
+        int printable_area_left_um =
+            ConvertUnit(size.left, kPointsPerInch, kMicronsPerInch);
+        int printable_area_bottom_um =
+            ConvertUnit(size.bottom, kPointsPerInch, kMicronsPerInch);
+        // `size.right` is the horizontal distance from the left of the paper to
+        // the right of the printable area.
+        int printable_area_right_um =
+            ConvertUnit(size.right, kPointsPerInch, kMicronsPerInch);
+        // `size.top` is the vertical distance from the bottom of the paper to
+        // the top of the printable area.
+        int printable_area_top_um =
+            ConvertUnit(size.top, kPointsPerInch, kMicronsPerInch);
+
+        gfx::Rect printable_area_um(
+            printable_area_left_um, printable_area_bottom_um,
+            /*width=*/printable_area_right_um - printable_area_left_um,
+            /*height=*/printable_area_top_um - printable_area_bottom_um);
+
+        // Default to the paper size if printable area is empty.
+        // We've seen some drivers have a printable area that goes out of
+        // bounds of the paper size. In those cases, set the printable area to
+        // be the size. (See crbug.com/1412305.)
+        const gfx::Rect size_um_rect = gfx::Rect(paper_size_um);
+        if (printable_area_um.IsEmpty() ||
+            !size_um_rect.Contains(printable_area_um)) {
+          printable_area_um = size_um_rect;
+        }
+
+        PrinterSemanticCapsAndDefaults::Paper paper(
+            display_name,
+            /*vendor_id=*/size.name, paper_size_um, printable_area_um);
+
+        caps.papers.push_back(paper);
+        if (size.marked) {
+          caps.default_paper = paper;
+          is_default_found = true;
+        }
       }
-    });
+    }
     if (!is_default_found) {
       gfx::Size locale_paper_um = GetDefaultPaperSizeFromLocaleMicrons(locale);
       for (const PrinterSemanticCapsAndDefaults::Paper& paper : caps.papers) {
diff --git a/printing/backend/cups_printer.cc b/printing/backend/cups_printer.cc
index 7c879e9..cfd8ae2 100644
--- a/printing/backend/cups_printer.cc
+++ b/printing/backend/cups_printer.cc
@@ -165,12 +165,14 @@
     printer_info->options[kDriverInfoTagName] = make_and_model;
 
     // Store printer options.
-    UNSAFE_TODO({
-      for (int opt_index = 0; opt_index < printer->num_options; ++opt_index) {
-        printer_info->options[printer->options[opt_index].name] =
-            printer->options[opt_index].value;
+    if (printer->num_options > 0) {
+      // SAFETY: Required from CUPS.
+      auto options = UNSAFE_BUFFERS(base::span<const cups_option_t>(
+          printer->options, static_cast<size_t>(printer->num_options)));
+      for (const auto& option : options) {
+        printer_info->options[option.name] = option.value;
       }
-    });
+    }
 
     return true;
   }
diff --git a/printing/backend/print_backend_cups.cc b/printing/backend/print_backend_cups.cc
index f5596b2..f3a75c2 100644
--- a/printing/backend/print_backend_cups.cc
+++ b/printing/backend/print_backend_cups.cc
@@ -111,12 +111,14 @@
     printer_info->options[kDriverInfoTagName] = drv_info;
 
   // Store printer options.
-  UNSAFE_TODO({
-    for (int opt_index = 0; opt_index < printer.num_options; ++opt_index) {
-      printer_info->options[printer.options[opt_index].name] =
-          printer.options[opt_index].value;
+  if (printer.num_options > 0) {
+    // SAFETY: Required from CUPS.
+    auto options = UNSAFE_BUFFERS(base::span<const cups_option_t>(
+        printer.options, static_cast<size_t>(printer.num_options)));
+    for (const auto& option : options) {
+      printer_info->options[option.name] = option.value;
     }
-  });
+  }
   std::string_view info =
       info_option ? std::string_view(info_option) : std::string_view();
   printer_info->display_name = GetDisplayName(printer_info->printer_name, info);
@@ -181,18 +183,16 @@
     return mojom::ResultCode::kSuccess;
   }
 
-  UNSAFE_TODO({
-    for (int printer_index = 0; printer_index < dests_data.num_dests;
-         ++printer_index) {
-      const cups_dest_t& printer = dests_data.dests[printer_index];
-
-      PrinterBasicInfo printer_info;
-      if (PrinterBasicInfoFromCUPS(printer, &printer_info) ==
-          mojom::ResultCode::kSuccess) {
-        printer_list.push_back(printer_info);
-      }
+  // SAFETY: Required from CUPS.
+  auto printers = UNSAFE_BUFFERS(base::span<const cups_dest_t>(
+      dests_data.dests, static_cast<size_t>(dests_data.num_dests)));
+  for (const auto& printer : printers) {
+    PrinterBasicInfo printer_info;
+    if (PrinterBasicInfoFromCUPS(printer, &printer_info) ==
+        mojom::ResultCode::kSuccess) {
+      printer_list.push_back(printer_info);
     }
-  });
+  }
 
   cupsFreeDests(dests_data.num_dests, dests_data.dests);
 
diff --git a/printing/common/metafile_utils.cc b/printing/common/metafile_utils.cc
index e28fb6e..9b901f3 100644
--- a/printing/common/metafile_utils.cc
+++ b/printing/common/metafile_utils.cc
@@ -10,6 +10,7 @@
 #include "base/check.h"
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
+#include "base/containers/span_reader.h"
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
@@ -234,6 +235,21 @@
   return valid;
 }
 
+sk_sp<SkData> GetImageData(SkImage* img) {
+  // Skip the encoding step if the image is already encoded
+  if (sk_sp<SkData> data = img->refEncodedData()) {
+    return data;
+  }
+
+  // TODO(crbug.com/40073326) Convert texture-backed images to raster
+  // *before* they get this far if possible.
+  if (img->isTextureBacked()) {
+    GrDirectContext* ctx = SkImages::GetContext(img);
+    return skia::EncodePngAsSkData(ctx, img);
+  }
+  return skia::EncodePngAsSkData(nullptr, img);
+}
+
 }  // namespace
 
 namespace printing {
@@ -363,43 +379,86 @@
   return typeface;
 }
 
-sk_sp<SkData> SerializeRasterImage(SkImage* img, void*) {
+sk_sp<SkData> SerializeRasterImage(SkImage* img, void* ctx) {
   if (!img) {
     return nullptr;
   }
-  // Skip the encoding step if the image is already encoded
-  if (sk_sp<SkData> data = img->refEncodedData()) {
-    return data;
+
+  auto* context = reinterpret_cast<ImageSerializationContext*>(ctx);
+
+  uint32_t img_id = img->uniqueID();
+  if (context->contains(img_id)) {
+    return SkData::MakeWithCopy(&img_id, sizeof(img_id));
   }
 
-  // TODO(crbug.com/40073326) Convert texture-backed images to raster
-  // *before* they get this far if possible.
-  if (img->isTextureBacked()) {
-    GrDirectContext* ctx = SkImages::GetContext(img);
-    return skia::EncodePngAsSkData(ctx, img);
+  sk_sp<SkData> img_data = GetImageData(img);
+  if (!img_data) {
+    return nullptr;
   }
-  return skia::EncodePngAsSkData(nullptr, img);
+
+  // Store image id followed by the image data on the first occurrence
+  // of an image.
+  auto data = SkData::MakeUninitialized(
+      base::CheckAdd(img_data->size(), sizeof(img_id)).ValueOrDie());
+
+  // SAFETY: The span is used as a view to avoid direct pointer access.
+  auto [id_span, data_span] =
+      UNSAFE_BUFFERS(base::span(static_cast<uint8_t*>(data->writable_data()),
+                                data->size()))
+          .split_at<sizeof(img_id)>();
+  id_span.copy_from(base::byte_span_from_ref(img_id));
+  data_span.copy_from(gfx::SkDataToSpan(img_data));
+
+  context->insert(img_id);
+
+  return data;
 }
 
-sk_sp<SkImage> DeserializeRasterImage(const void* bytes, size_t length, void*) {
+sk_sp<SkImage> DeserializeRasterImage(const void* bytes,
+                                      size_t length,
+                                      void* ctx) {
+  auto* context = reinterpret_cast<ImageDeserializationContext*>(ctx);
+
   // SAFETY: The caller must provide a valid pointer and length.
-  auto bytes_span =
-      UNSAFE_BUFFERS(base::span(static_cast<const uint8_t*>(bytes), length));
-  // Safe for `data` to be a span over `bytes_span` because `bytes_span` will
-  // outlive `data`.
-  auto data = gfx::MakeSkDataFromSpanWithoutCopy(bytes_span);
-  //TODO(b/40045064): Explicitly decode other supported codecs
-  auto codec = SkPngDecoder::Decode(data, nullptr);
-  if (codec) {
-    return std::get<0>(codec->getImage());
+  base::SpanReader reader{
+      UNSAFE_BUFFERS(base::span(static_cast<const uint8_t*>(bytes), length))};
+
+  uint32_t img_id;
+  if (!reader.ReadU32NativeEndian(img_id)) {
+    // If there is no room for id, there cannot be meaningful image data.
+    return nullptr;
   }
-  return nullptr;
+
+  auto iter = context->find(img_id);
+  if (iter != context->end() && iter->second) {
+    return iter->second;
+  }
+
+  if (!reader.remaining()) {
+    return nullptr;
+  }
+
+  // Copy the data to avoid `bytes` being freed before the image is decoded.
+  auto data_span = reader.remaining_span();
+  auto img_data = SkData::MakeWithCopy(data_span.data(), data_span.size());
+
+  // Need to explicitly decode here, as the data are prefixed with image id,
+  // invalidating the built-in Skia fallback.
+  auto image = SkImages::DeferredFromEncodedData(img_data);
+  if (!image) {
+    return nullptr;
+  }
+
+  (*context)[img_id] = image;
+  return image;
 }
 
 SkSerialProcs SerializationProcs(PictureSerializationContext* picture_ctx,
-                                 TypefaceSerializationContext* typeface_ctx) {
+                                 TypefaceSerializationContext* typeface_ctx,
+                                 ImageSerializationContext* image_ctx) {
   SkSerialProcs procs;
   procs.fImageProc = SerializeRasterImage;
+  procs.fImageCtx = image_ctx;
   procs.fPictureProc = SerializeOopPicture;
   procs.fPictureCtx = picture_ctx;
   procs.fTypefaceProc = SerializeOopTypeface;
@@ -409,9 +468,11 @@
 
 SkDeserialProcs DeserializationProcs(
     PictureDeserializationContext* picture_ctx,
-    TypefaceDeserializationContext* typeface_ctx) {
+    TypefaceDeserializationContext* typeface_ctx,
+    ImageDeserializationContext* image_ctx) {
   SkDeserialProcs procs;
   procs.fImageProc = DeserializeRasterImage;
+  procs.fImageCtx = image_ctx;
   procs.fPictureProc = DeserializeOopPicture;
   procs.fPictureCtx = picture_ctx;
   procs.fTypefaceProc = DeserializeOopTypeface;
diff --git a/printing/common/metafile_utils.h b/printing/common/metafile_utils.h
index dee4f240..f304a0e 100644
--- a/printing/common/metafile_utils.h
+++ b/printing/common/metafile_utils.h
@@ -31,6 +31,7 @@
     base::flat_map<uint32_t, sk_sp<SkPicture>>;
 using TypefaceDeserializationContext =
     base::flat_map<uint32_t, sk_sp<SkTypeface>>;
+using ImageDeserializationContext = base::flat_map<uint32_t, sk_sp<SkImage>>;
 
 // Stores the mapping between content's unique id and its corresponding frame
 // proxy token.
@@ -39,6 +40,9 @@
 // Stores the set of typeface unique ids used by the picture frame content.
 using TypefaceSerializationContext = ContentProxySet;
 
+// Stores the set of serialized image ids used by the content.
+using ImageSerializationContext = ContentProxySet;
+
 sk_sp<SkDocument> MakePdfDocument(
     std::string_view creator,
     std::string_view title,
@@ -51,11 +55,13 @@
 #endif
 
 SkSerialProcs SerializationProcs(PictureSerializationContext* picture_ctx,
-                                 TypefaceSerializationContext* typeface_ctx);
+                                 TypefaceSerializationContext* typeface_ctx,
+                                 ImageSerializationContext* image_ctx);
 
 SkDeserialProcs DeserializationProcs(
     PictureDeserializationContext* picture_ctx,
-    TypefaceDeserializationContext* typeface_ctx);
+    TypefaceDeserializationContext* typeface_ctx,
+    ImageDeserializationContext* image_ctx);
 
 }  // namespace printing
 
diff --git a/printing/metafile_skia.cc b/printing/metafile_skia.cc
index 41c74e26..0f8510e 100644
--- a/printing/metafile_skia.cc
+++ b/printing/metafile_skia.cc
@@ -79,6 +79,7 @@
   std::map<uint32_t, sk_sp<SkPicture>> subframe_pics;
   int document_cookie = 0;
   raw_ptr<ContentProxySet> typeface_content_info = nullptr;
+  raw_ptr<ContentProxySet> image_content_info = nullptr;
 
   // The scale factor is used because Blink occasionally calls
   // PaintCanvas::getTotalMatrix() even though the total matrix is not as
@@ -113,6 +114,10 @@
   data_->typeface_content_info = typeface_content_info;
 }
 
+void MetafileSkia::UtilizeImageContext(ContentProxySet* image_content_info) {
+  data_->image_content_info = image_content_info;
+}
+
 // TODO(halcanary): Create a Metafile class that only stores data.
 // Metafile::InitFromData is orthogonal to what the rest of
 // MetafileSkia does.
@@ -213,7 +218,8 @@
 #endif
     case mojom::SkiaDocumentType::kMSKP:
       SkSerialProcs procs = SerializationProcs(&data_->subframe_content_info,
-                                               data_->typeface_content_info);
+                                               data_->typeface_content_info,
+                                               data_->image_content_info);
       doc = SkMultiPictureDocument::Make(&stream, &procs);
       // It is safe to use base::Unretained(this) because the callback
       // is only used by `canvas` in the following loop which has shorter
@@ -249,7 +255,8 @@
   sk_sp<SkPicture> pic = data_->pages[0].content.ToSkPicture(
       SkRect::MakeSize(data_->pages[0].size), nullptr, callbacks);
   SkSerialProcs procs = SerializationProcs(&data_->subframe_content_info,
-                                           data_->typeface_content_info);
+                                           data_->typeface_content_info,
+                                           data_->image_content_info);
   SkDynamicMemoryWStream stream;
   pic->serialize(&stream, &procs);
   data_->data_stream = stream.detachAsStream();
@@ -404,6 +411,7 @@
   metafile->data_->subframe_content_info = data_->subframe_content_info;
   metafile->data_->subframe_pics = data_->subframe_pics;
   metafile->data_->typeface_content_info = data_->typeface_content_info;
+  metafile->data_->image_content_info = data_->image_content_info;
 
   if (!metafile->FinishDocument())  // Generate PDF.
     metafile.reset();
diff --git a/printing/metafile_skia.h b/printing/metafile_skia.h
index 097a4f134..d6afae9 100644
--- a/printing/metafile_skia.h
+++ b/printing/metafile_skia.h
@@ -121,6 +121,8 @@
 
   void UtilizeTypefaceContext(ContentProxySet* typeface_content_info);
 
+  void UtilizeImageContext(ContentProxySet* image_content_info);
+
   const ui::AXTreeUpdate& accessibility_tree() const {
     return accessibility_tree_;
   }
diff --git a/printing/metafile_skia_unittest.cc b/printing/metafile_skia_unittest.cc
index 1dbadb4e..0c2e113 100644
--- a/printing/metafile_skia_unittest.cc
+++ b/printing/metafile_skia_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/containers/span_reader.h"
 #include "build/build_config.h"
 #include "cc/paint/paint_op.h"
 #include "cc/paint/paint_record.h"
@@ -14,8 +15,8 @@
 #include "skia/ext/font_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/codec/SkCodec.h"
-#include "third_party/skia/include/codec/SkPngDecoder.h"
 #include "third_party/skia/include/codec/SkJpegDecoder.h"
+#include "third_party/skia/include/codec/SkPngDecoder.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkCanvas.h"
 #include "third_party/skia/include/core/SkFont.h"
@@ -33,6 +34,7 @@
 #include "third_party/skia/include/core/SkTextBlob.h"
 #include "third_party/skia/include/core/SkTypeface.h"
 #include "third_party/skia/include/encode/SkJpegEncoder.h"
+#include "ui/gfx/skia_span_util.h"
 
 namespace printing {
 
@@ -80,7 +82,7 @@
   // Get the complete picture by replacing the placeholder.
   PictureDeserializationContext subframes;
   subframes[content_id] = picture;
-  SkDeserialProcs procs = DeserializationProcs(&subframes, nullptr);
+  SkDeserialProcs procs = DeserializationProcs(&subframes, nullptr, nullptr);
   sk_sp<SkPicture> pic = SkPicture::MakeFromStream(metafile_stream, &procs);
   ASSERT_TRUE(pic);
 
@@ -153,7 +155,7 @@
   ContentProxySet serialize_typeface_ctx;
   PictureDeserializationContext subframes;
   TypefaceDeserializationContext typefaces;
-  SkDeserialProcs procs = DeserializationProcs(&subframes, &typefaces);
+  SkDeserialProcs procs = DeserializationProcs(&subframes, &typefaces, nullptr);
 
   // The typefaces which will be reused across the multiple (duplicate) pages.
   constexpr char kTypefaceName1[] = "sans-serif";
@@ -254,13 +256,25 @@
 
     // Use the image serialization proc and assert that we get encoded data back
     PictureSerializationContext subframes;
-    SkSerialProcs procs = SerializationProcs(&subframes, nullptr);
+    ImageSerializationContext images;
+    SkSerialProcs procs = SerializationProcs(&subframes, nullptr, &images);
 
-    sk_sp<SkData> encoded_data = (*procs.fImageProc)(image.get(), nullptr);
+    sk_sp<SkData> encoded_data =
+        (*procs.fImageProc)(image.get(), procs.fImageCtx);
     ASSERT_TRUE(encoded_data);
+    EXPECT_GT(encoded_data->size(), sizeof(uint32_t));
+
+    base::SpanReader reader{gfx::SkDataToSpan(encoded_data)};
+
+    uint32_t img_id;
+    ASSERT_TRUE(reader.ReadU32NativeEndian(img_id));
+    EXPECT_EQ(img_id, image->uniqueID());
+    ASSERT_TRUE(images.contains(img_id));
 
     // We expect unencoded images to be encoded as PNG.
-    ASSERT_TRUE(SkPngDecoder::IsPng(encoded_data->data(), encoded_data->size()));
+    auto encoded_image = reader.remaining_span();
+    ASSERT_TRUE(
+        SkPngDecoder::IsPng(encoded_image.data(), encoded_image.size()));
 }
 
 TEST(MetafileSkiaTest, SkipEncodingAsPngWhenImageIsAlreadyEncoded) {
@@ -288,12 +302,96 @@
 
     // Call serialization proc on the JPEG image
     PictureSerializationContext subframes;
-    SkSerialProcs procs = SerializationProcs(&subframes, nullptr);
-    sk_sp<SkData> encoded_data = (*procs.fImageProc)(jpeg_img.get(), nullptr);
+    ImageSerializationContext images;
+    SkSerialProcs procs = SerializationProcs(&subframes, nullptr, &images);
+    sk_sp<SkData> encoded_data =
+        (*procs.fImageProc)(jpeg_img.get(), procs.fImageCtx);
     ASSERT_TRUE(encoded_data);
+    EXPECT_GT(encoded_data->size(), sizeof(uint32_t));
+
+    base::SpanReader reader{gfx::SkDataToSpan(encoded_data)};
+
+    uint32_t img_id;
+    ASSERT_TRUE(reader.ReadU32NativeEndian(img_id));
+    EXPECT_EQ(img_id, jpeg_img->uniqueID());
+    ASSERT_TRUE(images.contains(img_id));
 
     // Make sure the data is still encoded as JPEG
-    ASSERT_TRUE(SkJpegDecoder::IsJpeg(encoded_data->data(), encoded_data->size()));
+    auto encoded_image = reader.remaining_span();
+    ASSERT_TRUE(
+        SkJpegDecoder::IsJpeg(encoded_image.data(), encoded_image.size()));
+}
+
+TEST(MetafileSkiaTest, SerializeUniqueImages) {
+  // Make raster surface
+  sk_sp<SkSurface> surface =
+      SkSurfaces::Raster(SkImageInfo::MakeN32(100, 50, kOpaque_SkAlphaType));
+  SkCanvas* canvas = surface->getCanvas();
+
+  // Draw to it
+  SkPaint paint;
+  paint.setColor(SK_ColorGREEN);
+  canvas->clear(SK_ColorYELLOW);
+  canvas->drawRect(SkRect::MakeSize(SkSize::Make(75, 25)), paint);
+
+  // Get an image that is not encoded
+  sk_sp<SkImage> unencoded_img = surface->makeImageSnapshot();
+  ASSERT_FALSE(unencoded_img->refEncodedData());
+
+  // Call serialization proc on the image for the first time.
+  PictureSerializationContext subframes;
+  ImageSerializationContext images;
+  SkSerialProcs procs = SerializationProcs(&subframes, nullptr, &images);
+
+  sk_sp<SkData> encoded_data1 =
+      (*procs.fImageProc)(unencoded_img.get(), procs.fImageCtx);
+  ASSERT_TRUE(encoded_data1);
+  EXPECT_GT(encoded_data1->size(), sizeof(uint32_t));
+
+  {
+    base::SpanReader reader{gfx::SkDataToSpan(encoded_data1)};
+    uint32_t img_id;
+    ASSERT_TRUE(reader.ReadU32NativeEndian(img_id));
+    EXPECT_EQ(img_id, unencoded_img->uniqueID());
+    ASSERT_TRUE(images.contains(img_id));
+
+    // The image is expected to be encoded as PNG.
+    auto encoded_image = reader.remaining_span();
+    ASSERT_FALSE(encoded_image.empty());
+    ASSERT_TRUE(
+        SkPngDecoder::IsPng(encoded_image.data(), encoded_image.size()));
+  }
+
+  // Call the serialization proc on the image for the second time.
+  sk_sp<SkData> encoded_data2 =
+      (*procs.fImageProc)(unencoded_img.get(), procs.fImageCtx);
+  ASSERT_TRUE(encoded_data2);
+  EXPECT_EQ(encoded_data2->size(), sizeof(uint32_t));
+
+  {
+    // The second serialization should only return image id.
+    base::SpanReader reader{gfx::SkDataToSpan(encoded_data2)};
+    uint32_t img_id;
+    ASSERT_TRUE(reader.ReadU32NativeEndian(img_id));
+    EXPECT_EQ(img_id, unencoded_img->uniqueID());
+    EXPECT_FALSE(reader.remaining());
+  }
+
+  // Deserialization.
+  SkCodecs::Register(SkPngDecoder::Decoder());
+  PictureDeserializationContext d_subframes;
+  ImageDeserializationContext d_images;
+  SkDeserialProcs d_procs =
+      DeserializationProcs(&d_subframes, nullptr, &d_images);
+
+  sk_sp<SkImage> decoded_image1 = (*d_procs.fImageProc)(
+      encoded_data1->data(), encoded_data1->size(), d_procs.fImageCtx);
+  ASSERT_TRUE(decoded_image1);
+
+  sk_sp<SkImage> decoded_image2 = (*d_procs.fImageProc)(
+      encoded_data2->data(), encoded_data2->size(), d_procs.fImageCtx);
+  ASSERT_TRUE(decoded_image2);
+  EXPECT_EQ(decoded_image1->uniqueID(), decoded_image2->uniqueID());
 }
 
 }  // namespace printing
diff --git a/remoting/base/cloud_session_authz_service_client_factory.cc b/remoting/base/cloud_session_authz_service_client_factory.cc
index 6515b38..7edd0d8 100644
--- a/remoting/base/cloud_session_authz_service_client_factory.cc
+++ b/remoting/base/cloud_session_authz_service_client_factory.cc
@@ -49,10 +49,10 @@
 
   // SessionAuthzServiceClient implementation.
   void GenerateHostToken(GenerateHostTokenCallback callback) override;
-  void VerifySessionToken(
-      const internal::VerifySessionTokenRequestStruct& request,
-      VerifySessionTokenCallback callback) override;
-  void ReauthorizeHost(const internal::ReauthorizeHostRequestStruct& request,
+  void VerifySessionToken(std::string_view session_token,
+                          VerifySessionTokenCallback callback) override;
+  void ReauthorizeHost(std::string_view session_reauth_token,
+                       std::string_view session_id,
                        ReauthorizeHostCallback callback) override;
 
  private:
@@ -89,20 +89,21 @@
 }
 
 void CloudSessionAuthzServiceClient::VerifySessionToken(
-    const internal::VerifySessionTokenRequestStruct& request,
+    std::string_view session_token,
     VerifySessionTokenCallback callback) {
   client_.VerifySessionToken(
-      request.session_token,
+      std::string(session_token),
       base::BindOnce(
           &CloudSessionAuthzServiceClient::OnVerifySessionTokenResponse,
           weak_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 void CloudSessionAuthzServiceClient::ReauthorizeHost(
-    const internal::ReauthorizeHostRequestStruct& request,
+    std::string_view session_reauth_token,
+    std::string_view session_id,
     ReauthorizeHostCallback callback) {
   client_.ReauthorizeHost(
-      request.session_reauth_token, request.session_id,
+      std::string(session_reauth_token), std::string(session_id),
       base::BindOnce(&CloudSessionAuthzServiceClient::OnReauthorizeHostResponse,
                      weak_factory_.GetWeakPtr(), std::move(callback)));
 }
diff --git a/remoting/base/corp_session_authz_service_client.cc b/remoting/base/corp_session_authz_service_client.cc
index c6432e6..bc346802 100644
--- a/remoting/base/corp_session_authz_service_client.cc
+++ b/remoting/base/corp_session_authz_service_client.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/functional/bind.h"
+#include "base/strings/strcat.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "remoting/base/internal_headers.h"
 #include "remoting/base/protobuf_http_request.h"
@@ -49,11 +50,17 @@
 
 CorpSessionAuthzServiceClient::CorpSessionAuthzServiceClient(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    std::unique_ptr<OAuthTokenGetter> oauth_token_getter)
+    std::unique_ptr<OAuthTokenGetter> oauth_token_getter,
+    std::string_view support_id)
     : oauth_token_getter_(std::move(oauth_token_getter)),
       http_client_(ServiceUrls::GetInstance()->remoting_corp_endpoint(),
                    oauth_token_getter_.get(),
-                   url_loader_factory) {}
+                   url_loader_factory),
+      support_id_(support_id) {
+  session_authz_path_ = support_id.empty()
+                            ? internal::GetRemoteAccessSessionAuthzPath()
+                            : internal::GetRemoteSupportSessionAuthzPath();
+}
 
 CorpSessionAuthzServiceClient::~CorpSessionAuthzServiceClient() = default;
 
@@ -94,14 +101,14 @@
             "Not implemented."
         })");
   ExecuteRequest(
-      traffic_annotation, internal::GetGenerateHostTokenRequestPath(),
-      internal::GetGenerateHostTokenRequest({}),
+      traffic_annotation, internal::GetGenerateHostTokenRequestVerb(),
+      internal::GetGenerateHostTokenRequest({.support_id = support_id_}),
       ConvertCallback(std::move(callback),
                       &internal::GetGenerateHostTokenResponseStruct));
 }
 
 void CorpSessionAuthzServiceClient::VerifySessionToken(
-    const internal::VerifySessionTokenRequestStruct& request,
+    std::string_view session_token,
     VerifySessionTokenCallback callback) {
   constexpr net::NetworkTrafficAnnotationTag traffic_annotation =
       net::DefineNetworkTrafficAnnotation(
@@ -139,15 +146,19 @@
           policy_exception_justification:
             "Not implemented."
         })");
+  internal::VerifySessionTokenRequestStruct request;
+  request.session_token = session_token;
+  request.support_id = support_id_;
   ExecuteRequest(
-      traffic_annotation, internal::GetVerifySessionTokenRequestPath(),
+      traffic_annotation, internal::GetVerifySessionTokenRequestVerb(),
       internal::GetVerifySessionTokenRequest(request),
       ConvertCallback(std::move(callback),
                       &internal::GetVerifySessionTokenResponseStruct));
 }
 
 void CorpSessionAuthzServiceClient::ReauthorizeHost(
-    const internal::ReauthorizeHostRequestStruct& request,
+    std::string_view session_reauth_token,
+    std::string_view session_id,
     ReauthorizeHostCallback callback) {
   constexpr net::NetworkTrafficAnnotationTag traffic_annotation =
       net::DefineNetworkTrafficAnnotation(
@@ -183,7 +194,11 @@
           policy_exception_justification:
             "Not implemented."
         })");
-  ExecuteRequest(traffic_annotation, internal::GetReauthorizeHostRequestPath(),
+  internal::ReauthorizeHostRequestStruct request;
+  request.session_reauth_token = session_reauth_token;
+  request.session_id = session_id;
+  request.support_id = support_id_;
+  ExecuteRequest(traffic_annotation, internal::GetReauthorizeHostRequestVerb(),
                  internal::GetReauthorizeHostRequest(request),
                  ConvertCallback(std::move(callback),
                                  &internal::GetReauthorizeHostResponseStruct));
@@ -192,12 +207,12 @@
 template <typename CallbackType>
 void CorpSessionAuthzServiceClient::ExecuteRequest(
     const net::NetworkTrafficAnnotationTag& traffic_annotation,
-    const std::string& path,
+    std::string_view verb,
     std::unique_ptr<google::protobuf::MessageLite> request_message,
     CallbackType callback) {
   auto request_config =
       std::make_unique<ProtobufHttpRequestConfig>(traffic_annotation);
-  request_config->path = path;
+  request_config->path = base::StrCat({session_authz_path_, ":", verb});
   request_config->api_key = internal::GetRemotingCorpApiKey();
   request_config->authenticated = true;
   request_config->provide_certificate = true;
diff --git a/remoting/base/corp_session_authz_service_client.h b/remoting/base/corp_session_authz_service_client.h
index e6f7a191..4e137e9c 100644
--- a/remoting/base/corp_session_authz_service_client.h
+++ b/remoting/base/corp_session_authz_service_client.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 #include <string>
+#include <string_view>
 
 #include "base/memory/scoped_refptr.h"
 #include "remoting/base/oauth_token_getter.h"
@@ -29,9 +30,12 @@
 // API. For internal details, see go/crd-sessionauthz.
 class CorpSessionAuthzServiceClient : public SessionAuthzServiceClient {
  public:
+  // |support_id|: The 7-digit support ID. Empty implies that the connection
+  //   mode is remote access.
   CorpSessionAuthzServiceClient(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      std::unique_ptr<OAuthTokenGetter> oauth_token_getter);
+      std::unique_ptr<OAuthTokenGetter> oauth_token_getter,
+      std::string_view support_id);
   ~CorpSessionAuthzServiceClient() override;
 
   CorpSessionAuthzServiceClient(const CorpSessionAuthzServiceClient&) = delete;
@@ -39,22 +43,24 @@
       const CorpSessionAuthzServiceClient&) = delete;
 
   void GenerateHostToken(GenerateHostTokenCallback callback) override;
-  void VerifySessionToken(
-      const internal::VerifySessionTokenRequestStruct& request,
-      VerifySessionTokenCallback callback) override;
-  void ReauthorizeHost(const internal::ReauthorizeHostRequestStruct& request,
+  void VerifySessionToken(std::string_view session_token,
+                          VerifySessionTokenCallback callback) override;
+  void ReauthorizeHost(std::string_view session_reauth_token,
+                       std::string_view session_id,
                        ReauthorizeHostCallback callback) override;
 
  private:
   template <typename CallbackType>
   void ExecuteRequest(
       const net::NetworkTrafficAnnotationTag& traffic_annotation,
-      const std::string& path,
+      std::string_view verb,
       std::unique_ptr<google::protobuf::MessageLite> request_message,
       CallbackType callback);
 
   std::unique_ptr<OAuthTokenGetter> oauth_token_getter_;
   ProtobufHttpClient http_client_;
+  std::string support_id_;
+  std::string_view session_authz_path_;
 };
 
 }  // namespace remoting
diff --git a/remoting/base/corp_session_authz_service_client_factory.cc b/remoting/base/corp_session_authz_service_client_factory.cc
index 10606891..122b49cd 100644
--- a/remoting/base/corp_session_authz_service_client_factory.cc
+++ b/remoting/base/corp_session_authz_service_client_factory.cc
@@ -19,7 +19,8 @@
 CorpSessionAuthzServiceClientFactory::CorpSessionAuthzServiceClientFactory(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     const std::string& service_account_email,
-    const std::string& refresh_token) {
+    const std::string& refresh_token,
+    std::string_view support_id) {
   DCHECK(url_loader_factory);
 
   url_loader_factory_ = url_loader_factory;
@@ -27,6 +28,7 @@
       url_loader_factory, service_account_email, refresh_token);
   oauth_token_getter_task_runner_ =
       base::SequencedTaskRunner::GetCurrentDefault();
+  support_id_ = support_id;
 }
 
 CorpSessionAuthzServiceClientFactory::~CorpSessionAuthzServiceClientFactory() =
@@ -37,7 +39,8 @@
   return std::make_unique<CorpSessionAuthzServiceClient>(
       url_loader_factory_,
       std::make_unique<OAuthTokenGetterProxy>(oauth_token_getter_->GetWeakPtr(),
-                                              oauth_token_getter_task_runner_));
+                                              oauth_token_getter_task_runner_),
+      support_id_);
 }
 
 AuthenticationMethod CorpSessionAuthzServiceClientFactory::method() {
diff --git a/remoting/base/corp_session_authz_service_client_factory.h b/remoting/base/corp_session_authz_service_client_factory.h
index 0835233..12c506f98 100644
--- a/remoting/base/corp_session_authz_service_client_factory.h
+++ b/remoting/base/corp_session_authz_service_client_factory.h
@@ -6,11 +6,12 @@
 #define REMOTING_BASE_CORP_SESSION_AUTHZ_SERVICE_CLIENT_FACTORY_H_
 
 #include <memory>
-#include <string>
+#include <string_view>
 
 #include "base/memory/scoped_refptr.h"
 #include "base/task/sequenced_task_runner.h"
 #include "remoting/base/authentication_method.h"
+#include "remoting/base/corp_session_authz_service_client.h"
 #include "remoting/base/session_authz_service_client_factory.h"
 
 namespace network {
@@ -27,10 +28,13 @@
 class CorpSessionAuthzServiceClientFactory
     : public SessionAuthzServiceClientFactory {
  public:
+  // |support_id|: The 7-digit support ID. Should only be provided for remote
+  //   support.
   CorpSessionAuthzServiceClientFactory(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       const std::string& service_account_email,
-      const std::string& refresh_token);
+      const std::string& refresh_token,
+      std::string_view support_id = {});
 
   CorpSessionAuthzServiceClientFactory(
       const CorpSessionAuthzServiceClientFactory&) = delete;
@@ -46,6 +50,7 @@
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
   std::unique_ptr<OAuthTokenGetterImpl> oauth_token_getter_;
   scoped_refptr<base::SequencedTaskRunner> oauth_token_getter_task_runner_;
+  std::string support_id_;
 };
 
 }  // namespace remoting
diff --git a/remoting/base/mock_session_authz_service_client.h b/remoting/base/mock_session_authz_service_client.h
index 71cfccc..70865461 100644
--- a/remoting/base/mock_session_authz_service_client.h
+++ b/remoting/base/mock_session_authz_service_client.h
@@ -20,11 +20,12 @@
   MOCK_METHOD(void, GenerateHostToken, (GenerateHostTokenCallback callback));
   MOCK_METHOD(void,
               VerifySessionToken,
-              (const internal::VerifySessionTokenRequestStruct& request,
+              (std::string_view session_token,
                VerifySessionTokenCallback callback));
   MOCK_METHOD(void,
               ReauthorizeHost,
-              (const internal::ReauthorizeHostRequestStruct& request,
+              (std::string_view session_reauth_token,
+               std::string_view session_id,
                ReauthorizeHostCallback callback));
 };
 
diff --git a/remoting/base/session_authz_service_client.h b/remoting/base/session_authz_service_client.h
index 67199226..8f8a863 100644
--- a/remoting/base/session_authz_service_client.h
+++ b/remoting/base/session_authz_service_client.h
@@ -6,6 +6,7 @@
 #define REMOTING_BASE_SESSION_AUTHZ_SERVICE_CLIENT_H_
 
 #include <memory>
+#include <string_view>
 
 #include "base/functional/callback_forward.h"
 #include "remoting/base/protobuf_http_status.h"
@@ -30,12 +31,11 @@
   virtual ~SessionAuthzServiceClient() = default;
 
   virtual void GenerateHostToken(GenerateHostTokenCallback callback) = 0;
-  virtual void VerifySessionToken(
-      const internal::VerifySessionTokenRequestStruct& request,
-      VerifySessionTokenCallback callback) = 0;
-  virtual void ReauthorizeHost(
-      const internal::ReauthorizeHostRequestStruct& request,
-      ReauthorizeHostCallback callback) = 0;
+  virtual void VerifySessionToken(std::string_view session_token,
+                                  VerifySessionTokenCallback callback) = 0;
+  virtual void ReauthorizeHost(std::string_view session_reauth_token,
+                               std::string_view session_id,
+                               ReauthorizeHostCallback callback) = 0;
 
  protected:
   SessionAuthzServiceClient() = default;
diff --git a/remoting/host/it2me/BUILD.gn b/remoting/host/it2me/BUILD.gn
index 81e56cc11..59bc09b 100644
--- a/remoting/host/it2me/BUILD.gn
+++ b/remoting/host/it2me/BUILD.gn
@@ -138,10 +138,7 @@
 
 if (is_chromeos) {
   source_set("chrome_os_host") {
-    sources = [
-      "it2me_native_messaging_host_allowed_origins.cc",
-      "it2me_native_messaging_host_allowed_origins.h",
-    ]
+    sources = [ "it2me_native_messaging_host_allowed_origins.h" ]
 
     if (is_chromeos_lacros) {
       sources += [
diff --git a/remoting/host/it2me/it2me_native_messaging_host_allowed_origins.cc b/remoting/host/it2me/it2me_native_messaging_host_allowed_origins.cc
deleted file mode 100644
index 321efa8..0000000
--- a/remoting/host/it2me/it2me_native_messaging_host_allowed_origins.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2021 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "remoting/host/it2me/it2me_native_messaging_host_allowed_origins.h"
-
-#include <iterator>
-
-namespace remoting {
-
-// If you modify the list of allowed_origins, don't forget to update
-// remoting/host/it2me/com.google.chrome.remote_assistance.json.jinja2
-// to keep the two lists in sync.
-const char* const kIt2MeOrigins[] = {
-    "chrome-extension://inomeogfingihgjfjlpeplalcfajhgai/",
-    "chrome-extension://hpodccmdligbeohchckkeajbfohibipg/"};
-
-const size_t kIt2MeOriginsSize = std::size(kIt2MeOrigins);
-
-const char kIt2MeNativeMessageHostName[] =
-    "com.google.chrome.remote_assistance";
-
-}  // namespace remoting
diff --git a/remoting/host/it2me/it2me_native_messaging_host_allowed_origins.h b/remoting/host/it2me/it2me_native_messaging_host_allowed_origins.h
index a037c1d2..d9c09c1 100644
--- a/remoting/host/it2me/it2me_native_messaging_host_allowed_origins.h
+++ b/remoting/host/it2me/it2me_native_messaging_host_allowed_origins.h
@@ -9,13 +9,16 @@
 namespace remoting {
 
 // The set of origins which are allowed to instantiate an It2Me host.
-extern const char* const kIt2MeOrigins[];
-
-// The number of entries defined in |kIt2MeOrigins|.
-extern const size_t kIt2MeOriginsSize;
+// If you modify the list of allowed_origins, don't forget to update
+// remoting/host/it2me/com.google.chrome.remote_assistance.json.jinja2
+// to keep the two lists in sync.
+inline constexpr const char* kIt2MeOrigins[] = {
+    "chrome-extension://inomeogfingihgjfjlpeplalcfajhgai/",
+    "chrome-extension://hpodccmdligbeohchckkeajbfohibipg/"};
 
 // The name used to register the It2Me native message host.
-extern const char kIt2MeNativeMessageHostName[];
+inline constexpr char kIt2MeNativeMessageHostName[] =
+    "com.google.chrome.remote_assistance";
 
 }  // namespace remoting
 
diff --git a/remoting/internal b/remoting/internal
index bc8e5db..f4b7da7f 160000
--- a/remoting/internal
+++ b/remoting/internal
@@ -1 +1 @@
-Subproject commit bc8e5dbe2ea801d77d77ed8400e752f631f352a8
+Subproject commit f4b7da7fe40499ad775d1b2a730f4cd1de4ac05c
diff --git a/remoting/proto/internal_stubs.cc b/remoting/proto/internal_stubs.cc
index 2c23af74..07ac238c 100644
--- a/remoting/proto/internal_stubs.cc
+++ b/remoting/proto/internal_stubs.cc
@@ -128,10 +128,18 @@
 // SessionAuthzService helpers
 // ===========================
 
+std::string_view GetRemoteAccessSessionAuthzPath() {
+  return {};
+}
+
+std::string_view GetRemoteSupportSessionAuthzPath() {
+  return {};
+}
+
 // GenerateHostToken
 
-std::string GetGenerateHostTokenRequestPath() {
-  return "";
+std::string_view GetGenerateHostTokenRequestVerb() {
+  return {};
 }
 
 std::unique_ptr<GenerateHostTokenRequest> GetGenerateHostTokenRequest(
@@ -146,8 +154,8 @@
 
 // VerifySessionToken
 
-std::string GetVerifySessionTokenRequestPath() {
-  return "";
+std::string_view GetVerifySessionTokenRequestVerb() {
+  return {};
 }
 
 std::unique_ptr<VerifySessionTokenRequest> GetVerifySessionTokenRequest(
@@ -162,8 +170,8 @@
 
 // ReauthorizeHost
 
-std::string GetReauthorizeHostRequestPath() {
-  return "";
+std::string_view GetReauthorizeHostRequestVerb() {
+  return {};
 }
 
 std::unique_ptr<ReauthorizeHostRequest> GetReauthorizeHostRequest(
diff --git a/remoting/proto/internal_stubs.h b/remoting/proto/internal_stubs.h
index e9eee2ed..0c65e413 100644
--- a/remoting/proto/internal_stubs.h
+++ b/remoting/proto/internal_stubs.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <string_view>
 
 #include "remoting/proto/logging_service.h"
 #include "remoting/proto/session_authz_service.h"
@@ -89,9 +90,12 @@
 // SessionAuthzService helpers
 // ===========================
 
+extern std::string_view GetRemoteAccessSessionAuthzPath();
+extern std::string_view GetRemoteSupportSessionAuthzPath();
+
 // GenerateHostToken
 using GenerateHostTokenRequest = DoNothingProto;
-extern std::string GetGenerateHostTokenRequestPath();
+extern std::string_view GetGenerateHostTokenRequestVerb();
 extern std::unique_ptr<GenerateHostTokenRequest> GetGenerateHostTokenRequest(
     const GenerateHostTokenRequestStruct&);
 
@@ -101,7 +105,7 @@
 
 // VerifySessionToken
 using VerifySessionTokenRequest = DoNothingProto;
-extern std::string GetVerifySessionTokenRequestPath();
+extern std::string_view GetVerifySessionTokenRequestVerb();
 extern std::unique_ptr<VerifySessionTokenRequest> GetVerifySessionTokenRequest(
     const VerifySessionTokenRequestStruct&);
 
@@ -111,7 +115,7 @@
 
 // ReauthorizeHost
 using ReauthorizeHostRequest = DoNothingProto;
-extern std::string GetReauthorizeHostRequestPath();
+extern std::string_view GetReauthorizeHostRequestVerb();
 extern std::unique_ptr<ReauthorizeHostRequest> GetReauthorizeHostRequest(
     const ReauthorizeHostRequestStruct&);
 
diff --git a/remoting/proto/session_authz_service.h b/remoting/proto/session_authz_service.h
index 779ba1b..757111c 100644
--- a/remoting/proto/session_authz_service.h
+++ b/remoting/proto/session_authz_service.h
@@ -16,7 +16,9 @@
 // builds, they are populated by code in internal_stubs.h.
 namespace remoting::internal {
 
-struct GenerateHostTokenRequestStruct {};
+struct GenerateHostTokenRequestStruct {
+  std::string support_id;
+};
 
 struct GenerateHostTokenResponseStruct {
   GenerateHostTokenResponseStruct();
@@ -33,6 +35,7 @@
   bool operator==(const VerifySessionTokenRequestStruct&) const;
 
   std::string session_token;
+  std::string support_id;
 };
 
 struct VerifySessionTokenResponseStruct {
@@ -53,6 +56,7 @@
 
   std::string session_reauth_token;
   std::string session_id;
+  std::string support_id;
 };
 
 struct ReauthorizeHostResponseStruct {
diff --git a/remoting/protocol/session_authz_authenticator.cc b/remoting/protocol/session_authz_authenticator.cc
index 0ca22a7..0d8b74e5 100644
--- a/remoting/protocol/session_authz_authenticator.cc
+++ b/remoting/protocol/session_authz_authenticator.cc
@@ -197,10 +197,9 @@
     const jingle_xmpp::XmlElement& message,
     base::OnceClosure resume_callback) {
   session_authz_state_ = SessionAuthzState::VERIFYING_SESSION_TOKEN;
-  internal::VerifySessionTokenRequestStruct request;
-  request.session_token = message.TextNamed(kSessionTokenTag);
+  std::string session_token = message.TextNamed(kSessionTokenTag);
   service_client_->VerifySessionToken(
-      request,
+      session_token,
       base::BindOnce(&SessionAuthzAuthenticator::OnVerifiedSessionToken,
                      base::Unretained(this), message,
                      std::move(resume_callback)));
diff --git a/remoting/protocol/session_authz_authenticator_unittest.cc b/remoting/protocol/session_authz_authenticator_unittest.cc
index 6e86234..1f3c2f4 100644
--- a/remoting/protocol/session_authz_authenticator_unittest.cc
+++ b/remoting/protocol/session_authz_authenticator_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <optional>
+#include <string_view>
 #include <tuple>
 
 #include "base/functional/bind.h"
@@ -40,11 +41,12 @@
 using testing::ByMove;
 using testing::Return;
 
-constexpr char kFakeHostToken[] = "fake_host_token";
-constexpr char kFakeSessionId[] = "fake_session_id";
-constexpr char kFakeSessionToken[] = "fake_session_token";
-constexpr char kFakeSharedSecret[] = "fake_shared_secret";
-constexpr char kFakeSessionReauthToken[] = "fake_session_reauth_token";
+constexpr std::string_view kFakeHostToken = "fake_host_token";
+constexpr std::string_view kFakeSessionId = "fake_session_id";
+constexpr std::string_view kFakeSessionToken = "fake_session_token";
+constexpr std::string_view kFakeSharedSecret = "fake_shared_secret";
+constexpr std::string_view kFakeSessionReauthToken =
+    "fake_session_reauth_token";
 constexpr base::TimeDelta kFakeSessionReauthTokenLifetime = base::Minutes(5);
 constexpr int kMessageSize = 100;
 constexpr int kMessages = 1;
@@ -58,8 +60,8 @@
 }
 
 auto RespondVerifySessionToken(
-    const std::string& session_id = kFakeSessionId,
-    const std::string& shared_secret = kFakeSharedSecret,
+    std::string_view session_id = kFakeSessionId,
+    std::string_view shared_secret = kFakeSharedSecret,
     const std::optional<SessionPolicies>& session_policies = std::nullopt) {
   auto response =
       std::make_unique<internal::VerifySessionTokenResponseStruct>();
@@ -175,8 +177,8 @@
           message->TextNamed(SessionAuthzAuthenticator::kHostTokenTag);
       ASSERT_FALSE(host_token_.empty());
       session_authz_state_ = SessionAuthzState::READY_TO_SEND_SESSION_TOKEN;
-      underlying_ = create_base_authenticator_callback_.Run(kFakeSharedSecret,
-                                                            MESSAGE_READY);
+      underlying_ = create_base_authenticator_callback_.Run(
+          std::string(kFakeSharedSecret), MESSAGE_READY);
       std::move(resume_callback).Run();
       return;
     case SessionAuthzState::AUTHORIZED:
@@ -205,7 +207,7 @@
     jingle_xmpp::XmlElement* session_token_element =
         new jingle_xmpp::XmlElement(
             SessionAuthzAuthenticator::kSessionTokenTag);
-    session_token_element->SetBodyText(kFakeSessionToken);
+    session_token_element->SetBodyText(std::string(kFakeSessionToken));
     message->AddElement(session_token_element);
     session_authz_state_ = SessionAuthzState::AUTHORIZED;
   }
@@ -278,11 +280,7 @@
 TEST_F(SessionAuthzAuthenticatorTest, SuccessfulAuth) {
   EXPECT_CALL(*mock_service_client_, GenerateHostToken(_))
       .WillOnce(RespondGenerateHostToken());
-  internal::VerifySessionTokenRequestStruct
-      expected_verify_session_token_request;
-  expected_verify_session_token_request.session_token = kFakeSessionToken;
-  EXPECT_CALL(*mock_service_client_,
-              VerifySessionToken(expected_verify_session_token_request, _))
+  EXPECT_CALL(*mock_service_client_, VerifySessionToken(kFakeSessionToken, _))
       .WillOnce(RespondVerifySessionToken());
 
   StartAuthExchange();
@@ -431,11 +429,9 @@
   ASSERT_EQ(host_->state(), Authenticator::ACCEPTED);
   ASSERT_EQ(client_->state(), Authenticator::ACCEPTED);
 
-  internal::ReauthorizeHostRequestStruct request;
-  request.session_id = kFakeSessionId;
-  request.session_reauth_token = kFakeSessionReauthToken;
-  EXPECT_CALL(*mock_service_client_, ReauthorizeHost(request, _))
-      .WillOnce(base::test::RunOnceCallback<1>(
+  EXPECT_CALL(*mock_service_client_,
+              ReauthorizeHost(kFakeSessionReauthToken, kFakeSessionId, _))
+      .WillOnce(base::test::RunOnceCallback<2>(
           ProtobufHttpStatus(net::HttpStatusCode::HTTP_FORBIDDEN), nullptr));
   EXPECT_CALL(state_change_after_accepted_callback, Run());
 
diff --git a/remoting/protocol/session_authz_reauthorizer.cc b/remoting/protocol/session_authz_reauthorizer.cc
index 1b45b06..db24e80 100644
--- a/remoting/protocol/session_authz_reauthorizer.cc
+++ b/remoting/protocol/session_authz_reauthorizer.cc
@@ -83,12 +83,10 @@
 
 void SessionAuthzReauthorizer::Reauthorize() {
   HOST_LOG << "Reauthorizing session...";
-  internal::ReauthorizeHostRequestStruct request;
-  request.session_id = session_id_;
-  request.session_reauth_token = session_reauth_token_;
   service_client_->ReauthorizeHost(
-      request, base::BindOnce(&SessionAuthzReauthorizer::OnReauthorizeResult,
-                              base::Unretained(this)));
+      session_reauth_token_, session_id_,
+      base::BindOnce(&SessionAuthzReauthorizer::OnReauthorizeResult,
+                     base::Unretained(this)));
 }
 
 void SessionAuthzReauthorizer::OnReauthorizeResult(
diff --git a/remoting/protocol/session_authz_reauthorizer_unittest.cc b/remoting/protocol/session_authz_reauthorizer_unittest.cc
index 054a8bd4..0a39394 100644
--- a/remoting/protocol/session_authz_reauthorizer_unittest.cc
+++ b/remoting/protocol/session_authz_reauthorizer_unittest.cc
@@ -27,31 +27,22 @@
 
 using testing::_;
 
-constexpr char kSessionId[] = "fake_session_id";
-constexpr char kInitialReauthToken[] = "fake_initial_reauth_token";
+constexpr std::string_view kSessionId = "fake_session_id";
+constexpr std::string_view kInitialReauthToken = "fake_initial_reauth_token";
 constexpr base::TimeDelta kInitialTokenLifetime = base::Minutes(10);
 
-internal::ReauthorizeHostRequestStruct CreateRequest(
-    std::string_view session_id,
-    std::string_view session_reauth_token) {
-  internal::ReauthorizeHostRequestStruct request;
-  request.session_id = session_id;
-  request.session_reauth_token = session_reauth_token;
-  return request;
-}
-
 auto Respond(std::string_view session_reauth_token,
              base::TimeDelta session_reauth_token_lifetime) {
   auto response = std::make_unique<internal::ReauthorizeHostResponseStruct>();
   response->session_reauth_token = session_reauth_token;
   response->session_reauth_token_lifetime = session_reauth_token_lifetime;
-  return base::test::RunOnceCallback<1>(ProtobufHttpStatus::OK(),
+  return base::test::RunOnceCallback<2>(ProtobufHttpStatus::OK(),
                                         std::move(response));
 }
 
 template <typename CodeType>
 auto RespondError(CodeType code) {
-  return base::test::RunOnceCallbackRepeatedly<1>(ProtobufHttpStatus(code),
+  return base::test::RunOnceCallbackRepeatedly<2>(ProtobufHttpStatus(code),
                                                   nullptr);
 }
 
@@ -87,32 +78,29 @@
 
 TEST_F(SessionAuthzReauthorizerTest, MultipleSuccessfulReauths) {
   // Reauth is not triggered before half of the token lifetime has passed.
-  EXPECT_CALL(service_client_, ReauthorizeHost(_, _)).Times(0);
+  EXPECT_CALL(service_client_, ReauthorizeHost(_, _, _)).Times(0);
   task_environment_.FastForwardBy(kInitialTokenLifetime / 2 -
                                   base::Seconds(10));
 
   // Reauth is triggered now.
-  EXPECT_CALL(
-      service_client_,
-      ReauthorizeHost(CreateRequest(kSessionId, kInitialReauthToken), _))
+  EXPECT_CALL(service_client_,
+              ReauthorizeHost(kInitialReauthToken, kSessionId, _))
       .WillOnce(Respond("fake_second_reauth_token", base::Minutes(8)));
   task_environment_.FastForwardBy(base::Seconds(10));
 
-  EXPECT_CALL(service_client_, ReauthorizeHost(_, _)).Times(0);
+  EXPECT_CALL(service_client_, ReauthorizeHost(_, _, _)).Times(0);
   task_environment_.FastForwardBy(base::Minutes(4) - base::Seconds(10));
 
-  EXPECT_CALL(
-      service_client_,
-      ReauthorizeHost(CreateRequest(kSessionId, "fake_second_reauth_token"), _))
+  EXPECT_CALL(service_client_,
+              ReauthorizeHost("fake_second_reauth_token", kSessionId, _))
       .WillOnce(Respond("fake_third_reauth_token", base::Minutes(6)));
   task_environment_.FastForwardBy(base::Seconds(10));
 }
 
 TEST_F(SessionAuthzReauthorizerTest,
        ReauthFailedWithNonretriableError_ClosesSession) {
-  EXPECT_CALL(
-      service_client_,
-      ReauthorizeHost(CreateRequest(kSessionId, kInitialReauthToken), _))
+  EXPECT_CALL(service_client_,
+              ReauthorizeHost(kInitialReauthToken, kSessionId, _))
       .WillOnce(RespondError(net::HTTP_FORBIDDEN));
   EXPECT_CALL(on_reauthorization_failed_callback_, Run())
       .WillOnce(ResetReauthorizer());
@@ -120,9 +108,8 @@
 }
 
 TEST_F(SessionAuthzReauthorizerTest, RetryReauthAndSucceed) {
-  EXPECT_CALL(
-      service_client_,
-      ReauthorizeHost(CreateRequest(kSessionId, kInitialReauthToken), _))
+  EXPECT_CALL(service_client_,
+              ReauthorizeHost(kInitialReauthToken, kSessionId, _))
       .WillRepeatedly(RespondError(net::ERR_INTERNET_DISCONNECTED));
   task_environment_.FastForwardBy(kInitialTokenLifetime / 2);
   const net::BackoffEntry* backoff_entry =
@@ -130,23 +117,20 @@
   task_environment_.FastForwardBy(backoff_entry->GetTimeUntilRelease());
   task_environment_.FastForwardBy(backoff_entry->GetTimeUntilRelease());
 
-  EXPECT_CALL(
-      service_client_,
-      ReauthorizeHost(CreateRequest(kSessionId, kInitialReauthToken), _))
+  EXPECT_CALL(service_client_,
+              ReauthorizeHost(kInitialReauthToken, kSessionId, _))
       .WillOnce(Respond("fake_second_reauth_token", base::Minutes(8)));
   task_environment_.FastForwardBy(backoff_entry->GetTimeUntilRelease());
 
-  EXPECT_CALL(
-      service_client_,
-      ReauthorizeHost(CreateRequest(kSessionId, "fake_second_reauth_token"), _))
+  EXPECT_CALL(service_client_,
+              ReauthorizeHost("fake_second_reauth_token", kSessionId, _))
       .WillOnce(Respond("fake_third_reauth_token", base::Minutes(6)));
   task_environment_.FastForwardBy(base::Minutes(4));
 }
 
 TEST_F(SessionAuthzReauthorizerTest, RetriesExceedTokenLifetime_ClosesSession) {
-  EXPECT_CALL(
-      service_client_,
-      ReauthorizeHost(CreateRequest(kSessionId, kInitialReauthToken), _))
+  EXPECT_CALL(service_client_,
+              ReauthorizeHost(kInitialReauthToken, kSessionId, _))
       .WillRepeatedly(RespondError(net::ERR_INTERNET_DISCONNECTED));
   EXPECT_CALL(on_reauthorization_failed_callback_, Run()).Times(0);
   task_environment_.FastForwardBy(kInitialTokenLifetime / 2);
diff --git a/remoting/test/session_authz_playground.cc b/remoting/test/session_authz_playground.cc
index f4367bf3..8b05982 100644
--- a/remoting/test/session_authz_playground.cc
+++ b/remoting/test/session_authz_playground.cc
@@ -7,6 +7,7 @@
 #include <cstdio>
 #include <cstdlib>
 #include <memory>
+#include <string_view>
 #include <vector>
 
 #include "base/command_line.h"
@@ -67,7 +68,8 @@
 
   service_client_ = std::make_unique<CorpSessionAuthzServiceClient>(
       url_loader_factory_owner_->GetURLLoaderFactory(),
-      CreateOAuthTokenGetter(host_config_file_path));
+      CreateOAuthTokenGetter(host_config_file_path),
+      /* support_id= */ std::string_view());
 
   run_loop_ = std::make_unique<base::RunLoop>();
   GenerateHostToken();
@@ -99,10 +101,8 @@
 void SessionAuthzPlayground::VerifySessionToken(const std::string& session_id) {
   printf("Enter session token generated by the client: ");
   std::string session_token = test::ReadString();
-  internal::VerifySessionTokenRequestStruct request;
-  request.session_token = session_token;
   service_client_->VerifySessionToken(
-      request,
+      session_token,
       base::BindOnce(
           [](const std::string& session_id, const ProtobufHttpStatus& status,
              std::unique_ptr<internal::VerifySessionTokenResponseStruct>
@@ -139,11 +139,8 @@
   if (!shouldReauthorize) {
     run_loop_->Quit();
   }
-  internal::ReauthorizeHostRequestStruct request;
-  request.session_id = session_id;
-  request.session_reauth_token = reauth_token;
   service_client_->ReauthorizeHost(
-      request,
+      reauth_token, session_id,
       base::BindOnce([](const ProtobufHttpStatus& status,
                         std::unique_ptr<internal::ReauthorizeHostResponseStruct>
                             response) {
diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc
index 1c3ead1..4fb8e2a9 100644
--- a/sandbox/policy/win/sandbox_win.cc
+++ b/sandbox/policy/win/sandbox_win.cc
@@ -594,6 +594,7 @@
     base::Process* process) {
   base::LaunchOptions options;
   options.handles_to_inherit = handles_to_inherit;
+  options.feedback_cursor_off = true;
   // Network process runs in a job even when unsandboxed. This is to ensure it
   // does not outlive the browser, which could happen if there is a lot of I/O
   // on process shutdown, in which case TerminateProcess can fail. See
@@ -829,6 +830,16 @@
                                     trace_id, "pid", process_id);
   }
 
+  void OnCreateThreadActionCreateFailure(DWORD last_error) override {
+    UMA_HISTOGRAM_SPARSE(
+        "Process.Sandbox.IPC.ThreadCreateRemoteThreadErrorCode", last_error);
+  }
+
+  void OnCreateThreadActionDuplicateFailure(DWORD last_error) override {
+    UMA_HISTOGRAM_SPARSE("Process.Sandbox.IPC.ThreadDuplicateHandleErrorCode",
+                         last_error);
+  }
+
  private:
   // When parallel launching is enabled, target creation will happen on the
   // thread pool. This is atomic to keep track of the number of threads that are
diff --git a/sandbox/win/src/broker_services.cc b/sandbox/win/src/broker_services.cc
index 89159fd..f6b6d8f95 100644
--- a/sandbox/win/src/broker_services.cc
+++ b/sandbox/win/src/broker_services.cc
@@ -735,6 +735,10 @@
   alt_desktop_.reset();
 }
 
+BrokerServicesDelegate* BrokerServicesBase::GetMetricsDelegate() {
+  return broker_services_delegate_.get();
+}
+
 void BrokerServicesBase::SetBrokerServicesDelegateForTesting(
     std::unique_ptr<BrokerServicesDelegate> delegate) {
   broker_services_delegate_ = std::move(delegate);
diff --git a/sandbox/win/src/broker_services.h b/sandbox/win/src/broker_services.h
index fc2ef22..bfad1f5 100644
--- a/sandbox/win/src/broker_services.h
+++ b/sandbox/win/src/broker_services.h
@@ -74,6 +74,9 @@
   void SetBrokerServicesDelegateForTesting(
       std::unique_ptr<BrokerServicesDelegate> delegate);
 
+  // Gets helper to log histograms from within the exe.
+  BrokerServicesDelegate* GetMetricsDelegate();
+
   static void FreezeTargetConfigForTesting(TargetConfig* config);
 
  private:
diff --git a/sandbox/win/src/handle_closer_agent.cc b/sandbox/win/src/handle_closer_agent.cc
index e1fa5e0..4facea7 100644
--- a/sandbox/win/src/handle_closer_agent.cc
+++ b/sandbox/win/src/handle_closer_agent.cc
@@ -44,6 +44,7 @@
 
 // Additional warmup done just when CSRSS is being disconnected.
 bool CsrssDisconnectWarmup() {
+  std::ignore = ::GetUserDefaultLCID();
   return ::EnumSystemLocalesEx(EnumLocalesProcEx, LOCALE_WINDOWS, 0, 0);
 }
 
diff --git a/sandbox/win/src/parallel_launch_test.cc b/sandbox/win/src/parallel_launch_test.cc
index a4fd309..9d95060 100644
--- a/sandbox/win/src/parallel_launch_test.cc
+++ b/sandbox/win/src/parallel_launch_test.cc
@@ -38,6 +38,8 @@
 
   void AfterTargetProcessCreateOnCreationThread(const void* trace_id,
                                                 DWORD process_id) override {}
+  void OnCreateThreadActionCreateFailure(DWORD last_error) override {}
+  void OnCreateThreadActionDuplicateFailure(DWORD last_error) override {}
 };
 
 class ParallelLaunchTest : public testing::Test {
diff --git a/sandbox/win/src/process_thread_dispatcher.cc b/sandbox/win/src/process_thread_dispatcher.cc
index 48af0fe..2f9a75f 100644
--- a/sandbox/win/src/process_thread_dispatcher.cc
+++ b/sandbox/win/src/process_thread_dispatcher.cc
@@ -97,7 +97,6 @@
   DWORD ret = ProcessPolicy::CreateThreadAction(*ipc->client_info, stack_size,
                                                 start_address, parameter,
                                                 creation_flags, &handle);
-
   ipc->return_info.win32_result = ret;
   ipc->return_info.handle = handle;
   return true;
diff --git a/sandbox/win/src/process_thread_policy.cc b/sandbox/win/src/process_thread_policy.cc
index 1211a8b..d6e7ce2 100644
--- a/sandbox/win/src/process_thread_policy.cc
+++ b/sandbox/win/src/process_thread_policy.cc
@@ -13,10 +13,12 @@
 #include "base/memory/free_deleter.h"
 #include "base/win/scoped_handle.h"
 #include "build/build_config.h"
+#include "sandbox/win/src/broker_services.h"
 #include "sandbox/win/src/ipc_tags.h"
 #include "sandbox/win/src/nt_internals.h"
 #include "sandbox/win/src/policy_engine_opcodes.h"
 #include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
 #include "sandbox/win/src/sandbox_nt_util.h"
 #include "sandbox/win/src/sandbox_types.h"
 #include "sandbox/win/src/win_utils.h"
@@ -109,11 +111,24 @@
       ::CreateRemoteThread(client_info.process, nullptr, stack_size,
                            start_address, parameter, creation_flags, nullptr));
   if (!local_handle.is_valid()) {
-    return ::GetLastError();
+    // Gather diagnostics for failures - we avoid always DumpWithoutCrashing as
+    // it might mask child process crashes that result when this IPC returns
+    // failure. See crbug.com/389729365.
+    DWORD gle = ::GetLastError();
+    BrokerServicesBase::GetInstance()
+        ->GetMetricsDelegate()
+        ->OnCreateThreadActionCreateFailure(gle);
+    return gle;
   }
   if (!CallDuplicateHandle(::GetCurrentProcess(), local_handle.get(),
                            client_info.process, handle, 0, FALSE,
                            DUPLICATE_SAME_ACCESS)) {
+    // Gather diagnostics for failures - we avoid always DumpWithoutCrashing as
+    // it might mask child process crashes that result when this IPC returns
+    // failure. See crbug.com/389729365.
+    BrokerServicesBase::GetInstance()
+        ->GetMetricsDelegate()
+        ->OnCreateThreadActionDuplicateFailure(::GetLastError());
     return ERROR_ACCESS_DENIED;
   }
   return ERROR_SUCCESS;
diff --git a/sandbox/win/src/sandbox.h b/sandbox/win/src/sandbox.h
index 483ea39..f6c4b29 100644
--- a/sandbox/win/src/sandbox.h
+++ b/sandbox/win/src/sandbox.h
@@ -303,6 +303,14 @@
   // this will be called on the thread pool.
   virtual void AfterTargetProcessCreateOnCreationThread(const void* trace_id,
                                                         DWORD process_id) = 0;
+
+  // Record error histograms when CreateThreadAction IPC failed to create a
+  // thread in the target process.
+  virtual void OnCreateThreadActionCreateFailure(DWORD last_error) = 0;
+  // Record error histograms when CreateThreadAction IPC failed to duplicate a
+  // handle into the child process.
+  virtual void OnCreateThreadActionDuplicateFailure(DWORD last_error) = 0;
+
   virtual ~BrokerServicesDelegate() {}
 };
 
diff --git a/sandbox/win/tests/common/controller.cc b/sandbox/win/tests/common/controller.cc
index 72f9ad5d..ff3a801a 100644
--- a/sandbox/win/tests/common/controller.cc
+++ b/sandbox/win/tests/common/controller.cc
@@ -155,6 +155,8 @@
 
   void AfterTargetProcessCreateOnCreationThread(const void* trace_id,
                                                 DWORD process_id) override {}
+  void OnCreateThreadActionCreateFailure(DWORD last_error) override {}
+  void OnCreateThreadActionDuplicateFailure(DWORD last_error) override {}
 };
 
 BrokerServices* GetBroker() {
diff --git a/services/network/cookie_settings.cc b/services/network/cookie_settings.cc
index 36483846..e37e8f3a 100644
--- a/services/network/cookie_settings.cc
+++ b/services/network/cookie_settings.cc
@@ -49,6 +49,15 @@
          !cookie.IsPartitioned();
 }
 
+// Third-party cookies are considered restricted when they are blocked and could
+// be unblocked by mitigations.
+bool OverridesSimulateThirdPartyCookieRestriction(
+    net::CookieSettingOverrides overrides) {
+  return overrides.HasAll(
+      {net::CookieSettingOverride::kForceDisableThirdPartyCookies,
+       net::CookieSettingOverride::kForceEnableThirdPartyCookieMitigations});
+}
+
 bool IsValidType(ContentSettingsType type) {
   // ContentSettingsType::TPCD_METADATA_GRANTS settings are managed by the
   // `network::tpcd::metadata::Manager` and are considered valid ContentSettings
@@ -220,7 +229,8 @@
   bool allowed = IsCookieAllowed(cookie, setting_with_metadata);
   if (cookie_inclusion_status) {
     AugmentInclusionStatus(cookie, top_frame_origin, setting_with_metadata,
-                           first_party_set_metadata, *cookie_inclusion_status);
+                           first_party_set_metadata, overrides,
+                           *cookie_inclusion_status);
   }
 
   RecordAllowedByStorageAccessType(
@@ -295,12 +305,12 @@
   for (net::CookieWithAccessResult& cookie : maybe_included_cookies) {
     AugmentInclusionStatus(cookie.cookie, top_frame_origin,
                            setting_with_metadata, first_party_set_metadata,
-                           cookie.access_result.status);
+                           overrides, cookie.access_result.status);
   }
   for (net::CookieWithAccessResult& cookie : excluded_cookies) {
     AugmentInclusionStatus(cookie.cookie, top_frame_origin,
                            setting_with_metadata, first_party_set_metadata,
-                           cookie.access_result.status);
+                           overrides, cookie.access_result.status);
   }
   const auto to_be_moved = std::ranges::stable_partition(
       maybe_included_cookies, [](const net::CookieWithAccessResult& cookie) {
@@ -399,17 +409,21 @@
     base::optional_ref<const url::Origin> top_frame_origin,
     const CookieSettings::CookieSettingWithMetadata& setting_with_metadata,
     const net::FirstPartySetMetadata& first_party_set_metadata,
+    net::CookieSettingOverrides overrides,
     net::CookieInclusionStatus& out_status) const {
   bool affected_by_3pcd_origin_trial =
       top_frame_origin &&
       IsBlockedByTopLevel3pcdOriginTrial(top_frame_origin->GetURL());
+  bool should_attach_3pcd_status =
+      OverridesSimulateThirdPartyCookieRestriction(overrides);
 
   if (IsCookieAllowed(cookie, setting_with_metadata)) {
     if (!setting_with_metadata.is_third_party_request() ||
         !ShouldApply3pcdRelatedReasons(cookie)) {
       return;
     }
-    if (ShouldBlockThirdPartyCookies() || affected_by_3pcd_origin_trial) {
+    if (ShouldBlockThirdPartyCookies() || affected_by_3pcd_origin_trial ||
+        should_attach_3pcd_status) {
       out_status.MaybeSetExemptionReason(GetExemptionReason(
           setting_with_metadata.third_party_cookie_allow_mechanism()));
       return;
@@ -427,7 +441,8 @@
 
   if (setting_with_metadata.is_third_party_request() &&
       setting_with_metadata.allow_partitioned_cookies()) {
-    if (IsThirdPartyPhaseoutEnabled() &&
+    if ((IsThirdPartyPhaseoutEnabled() || affected_by_3pcd_origin_trial ||
+         should_attach_3pcd_status) &&
         !setting_with_metadata.is_explicit_setting()) {
       // This cookie is blocked due to 3PCD.
       if (!ShouldApply3pcdRelatedReasons(cookie)) {
@@ -443,14 +458,6 @@
       }
       return;
     }
-    if (affected_by_3pcd_origin_trial &&
-        ShouldApply3pcdRelatedReasons(cookie)) {
-      // This cookie is blocked by the Origin Trial for 3PCD.
-      out_status.AddExclusionReason(
-          net::CookieInclusionStatus::EXCLUDE_THIRD_PARTY_PHASEOUT);
-
-      return;
-    }
   }
 
   // The cookie is blocked, but not by 3PCD.
diff --git a/services/network/cookie_settings.h b/services/network/cookie_settings.h
index 84f2bbc..314d9abc 100644
--- a/services/network/cookie_settings.h
+++ b/services/network/cookie_settings.h
@@ -222,6 +222,7 @@
       base::optional_ref<const url::Origin> top_frame_origin,
       const CookieSettings::CookieSettingWithMetadata& setting_with_metadata,
       const net::FirstPartySetMetadata& first_party_set_metadata,
+      net::CookieSettingOverrides overrides,
       net::CookieInclusionStatus& out_status) const;
 
   // Returns true if at least one content settings is session only.
diff --git a/services/network/cookie_settings_unittest.cc b/services/network/cookie_settings_unittest.cc
index f6efc93..f86847b 100644
--- a/services/network/cookie_settings_unittest.cc
+++ b/services/network/cookie_settings_unittest.cc
@@ -519,6 +519,66 @@
       /*expected_bucket_count=*/1);
 }
 
+TEST_F(CookieSettingsTest,
+       IsCookieAccessible_CookieSettingOverrideBlockSameSiteNoneCookie) {
+  CookieSettings settings;
+  net::CookieInclusionStatus status;
+
+  std::unique_ptr<net::CanonicalCookie> cookie =
+      MakeCanonicalSameSiteNoneCookie("name", kURL);
+  const url::Origin top_level_origin = url::Origin::Create(GURL(kOtherURL));
+
+  settings.set_block_third_party_cookies(false);
+
+  auto is_third_party_cookie_accessible =
+      [&](net::CookieSettingOverrides overrides) -> bool {
+    return settings.IsCookieAccessible(
+        *cookie, GURL(kURL), net::SiteForCookies(), top_level_origin,
+        net::FirstPartySetMetadata(), overrides, &status);
+  };
+  // Third-party cookie is accessible with no source blocking it.
+  ASSERT_TRUE(is_third_party_cookie_accessible(net::CookieSettingOverrides()));
+  ASSERT_TRUE(status.HasWarningReason(
+      net::CookieInclusionStatus::WARN_THIRD_PARTY_PHASEOUT));
+
+  status.ResetForTesting();
+  // Third-party cookies are blocked by overrides.
+  EXPECT_FALSE(is_third_party_cookie_accessible(
+      {net::CookieSettingOverride::kForceDisableThirdPartyCookies}));
+  EXPECT_TRUE(status.HasExclusionReason(
+      net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES));
+
+  net::CookieSettingOverrides overrides(
+      {net::CookieSettingOverride::kForceDisableThirdPartyCookies,
+       net::CookieSettingOverride::kForceEnableThirdPartyCookieMitigations});
+  status.ResetForTesting();
+  // Verify that cookie has the phaseout exclusion reason with both required
+  // overrides present.
+  EXPECT_FALSE(is_third_party_cookie_accessible(overrides));
+  EXPECT_TRUE(status.HasExclusionReason(
+      net::CookieInclusionStatus::EXCLUDE_THIRD_PARTY_PHASEOUT));
+
+  // No override can overrule a site-specific setting.
+  settings.set_content_settings(
+      ContentSettingsType::COOKIES,
+      {CreateSetting(kURL, "*", CONTENT_SETTING_BLOCK)});
+  status.ResetForTesting();
+  EXPECT_FALSE(is_third_party_cookie_accessible(overrides));
+  // Cookies blocked by a site-specific setting should still use
+  // `EXCLUDE_USER_PREFERENCES` reason.
+  EXPECT_TRUE(status.HasExclusionReason(
+      net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES));
+
+  // No override can overrule a global setting.
+  status.ResetForTesting();
+  settings.set_content_settings(
+      ContentSettingsType::COOKIES,
+      {CreateSetting("*", "*", CONTENT_SETTING_BLOCK)});
+  EXPECT_FALSE(is_third_party_cookie_accessible(overrides));
+  EXPECT_TRUE(status.HasOnlyExclusionReason(
+      net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES));
+}
+
 TEST_F(CookieSettingsTest, ShouldAlwaysAllowCookies) {
   CookieSettings settings;
   settings.set_secure_origin_cookies_allowed_schemes({kChromeScheme});
@@ -1817,6 +1877,63 @@
                   _, _, _))));
 }
 
+TEST_F(
+    CookieSettingsTest,
+    AnnotateAndMoveUserBlockedCookies_CrossSiteEmbed_CookieSettingOverrideBlock3PC) {
+  CookieSettings settings;
+  settings.set_block_third_party_cookies(false);
+
+  const url::Origin origin = url::Origin::Create(GURL(kOtherURL));
+
+  net::CookieSettingOverrides overrides(
+      {net::CookieSettingOverride::kForceDisableThirdPartyCookies});
+  {
+    net::CookieAccessResultList maybe_included_cookies = {
+        {*MakeCanonicalSameSiteNoneCookie("third_party", kURL), {}}};
+    net::CookieAccessResultList excluded_cookies = {};
+
+    EXPECT_FALSE(settings.AnnotateAndMoveUserBlockedCookies(
+        GURL(kURL), net::SiteForCookies(), &origin,
+        net::FirstPartySetMetadata(), overrides, maybe_included_cookies,
+        excluded_cookies));
+
+    EXPECT_THAT(
+        excluded_cookies,
+        UnorderedElementsAre(MatchesCookieWithAccessResult(
+            net::MatchesCookieWithName("third_party"),
+            MatchesCookieAccessResult(
+                net::HasExactlyExclusionReasonsForTesting(
+                    std::vector<net::CookieInclusionStatus::ExclusionReason>{
+                        net::CookieInclusionStatus::ExclusionReason::
+                            EXCLUDE_USER_PREFERENCES}),
+                _, _, _))));
+  }
+  // Both overrides should be present to yield the phaseout exclusion reason.
+  overrides.Put(
+      net::CookieSettingOverride::kForceEnableThirdPartyCookieMitigations);
+  {
+    net::CookieAccessResultList maybe_included_cookies = {
+        {*MakeCanonicalSameSiteNoneCookie("third_party", kURL), {}}};
+    net::CookieAccessResultList excluded_cookies = {};
+    EXPECT_FALSE(settings.AnnotateAndMoveUserBlockedCookies(
+        GURL(kURL), net::SiteForCookies(), &origin,
+        net::FirstPartySetMetadata(), overrides, maybe_included_cookies,
+        excluded_cookies));
+
+    // Verify that the excluded cookie has the expected reason.
+    EXPECT_THAT(
+        excluded_cookies,
+        UnorderedElementsAre(MatchesCookieWithAccessResult(
+            net::MatchesCookieWithName("third_party"),
+            MatchesCookieAccessResult(
+                net::HasExactlyExclusionReasonsForTesting(
+                    std::vector<net::CookieInclusionStatus::ExclusionReason>{
+                        net::CookieInclusionStatus::ExclusionReason::
+                            EXCLUDE_THIRD_PARTY_PHASEOUT}),
+                _, _, _))));
+  }
+}
+
 TEST_F(CookieSettingsTest,
        AnnotateAndMoveUserBlockedCookies_SameSiteEmbed_3PCAllowed) {
   CookieSettings settings;
diff --git a/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.cc b/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.cc
index 0cee2e02..2c73f897 100644
--- a/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.cc
+++ b/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.cc
@@ -55,6 +55,7 @@
   out->may_throttle_if_undrawn_frames = data.may_throttle_if_undrawn_frames();
   out->has_shared_element_resources = data.has_shared_element_resources();
   out->is_handling_interaction = data.is_handling_interaction();
+  out->is_handling_animation = data.is_handling_animation();
   out->send_frame_token_to_embedder = data.send_frame_token_to_embedder();
   out->min_page_scale_factor = data.min_page_scale_factor();
   out->is_software = data.is_software();
diff --git a/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.h b/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.h
index 1b5b9c0e..378d4858 100644
--- a/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.h
+++ b/services/viz/public/cpp/compositing/compositor_frame_metadata_mojom_traits.h
@@ -79,6 +79,11 @@
     return metadata.is_handling_interaction;
   }
 
+  static bool is_handling_animation(
+      const viz::CompositorFrameMetadata& metadata) {
+    return metadata.is_handling_animation;
+  }
+
   static SkColor4f root_background_color(
       const viz::CompositorFrameMetadata& metadata) {
     return metadata.root_background_color;
diff --git a/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.cc b/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.cc
index 9eec4eb..c3330ff 100644
--- a/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.cc
+++ b/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.cc
@@ -61,7 +61,7 @@
   viz::ResourceId id;
 
   gpu::SyncToken sync_token;
-  viz::MemoryBufferId memory_buffer_id;
+  gpu::Mailbox memory_buffer_id;
   if (!data.ReadSize(&out->size) || !data.ReadFormat(&out->format) ||
       !data.ReadMemoryBufferId(&memory_buffer_id) ||
       !data.ReadSyncToken(&sync_token) ||
@@ -91,46 +91,4 @@
   return true;
 }
 
-// static
-viz::mojom::MemoryBufferIdDataView::Tag
-UnionTraits<viz::mojom::MemoryBufferIdDataView, viz::MemoryBufferId>::GetTag(
-    const viz::MemoryBufferId& memory_buffer_id) {
-  return absl::visit(
-      base::Overloaded{
-          [](gpu::Mailbox) {
-            return viz::mojom::MemoryBufferIdDataView::Tag::kMailbox;
-          },
-
-          [](viz::SharedBitmapId) {
-            return viz::mojom::MemoryBufferIdDataView::Tag::kSharedBitmapId;
-          },
-      },
-      memory_buffer_id);
-}
-
-// static
-bool UnionTraits<viz::mojom::MemoryBufferIdDataView, viz::MemoryBufferId>::Read(
-    viz::mojom::MemoryBufferIdDataView memory_buffer_id,
-    viz::MemoryBufferId* out) {
-  switch (memory_buffer_id.tag()) {
-    case viz::mojom::MemoryBufferIdDataView::Tag::kMailbox: {
-      gpu::Mailbox mailbox;
-      if (!memory_buffer_id.ReadMailbox(&mailbox)) {
-        return false;
-      }
-      *out = mailbox;
-      return true;
-    }
-    case viz::mojom::MemoryBufferIdDataView::Tag::kSharedBitmapId: {
-      viz::SharedBitmapId shared_bitmap_id;
-      if (!memory_buffer_id.ReadSharedBitmapId(&shared_bitmap_id)) {
-        return false;
-      }
-      *out = shared_bitmap_id;
-      return true;
-    }
-  }
-  return false;
-}
-
 }  // namespace mojo
diff --git a/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.h b/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.h
index 94add9ba..57c556c 100644
--- a/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.h
+++ b/services/viz/public/cpp/compositing/transferable_resource_mojom_traits.h
@@ -45,7 +45,7 @@
     return resource.size;
   }
 
-  static viz::MemoryBufferId memory_buffer_id(
+  static gpu::Mailbox memory_buffer_id(
       const viz::TransferableResource& resource) {
     return resource.memory_buffer_id();
   }
@@ -120,24 +120,6 @@
                    viz::TransferableResource* out);
 };
 
-template <>
-struct UnionTraits<viz::mojom::MemoryBufferIdDataView, viz::MemoryBufferId> {
-  static viz::mojom::MemoryBufferIdDataView::Tag GetTag(
-      const viz::MemoryBufferId& memory_buffer_id);
-
-  static gpu::Mailbox mailbox(const viz::MemoryBufferId& memory_buffer_id) {
-    return absl::get<gpu::Mailbox>(memory_buffer_id);
-  }
-
-  static viz::SharedBitmapId shared_bitmap_id(
-      const viz::MemoryBufferId& memory_buffer_id) {
-    return absl::get<viz::SharedBitmapId>(memory_buffer_id);
-  }
-
-  static bool Read(viz::mojom::MemoryBufferIdDataView memory_buffer_id,
-                   viz::MemoryBufferId* out);
-};
-
 }  // namespace mojo
 
 #endif  // SERVICES_VIZ_PUBLIC_CPP_COMPOSITING_TRANSFERABLE_RESOURCE_MOJOM_TRAITS_H_
diff --git a/services/viz/public/mojom/compositing/compositor_frame_metadata.mojom b/services/viz/public/mojom/compositing/compositor_frame_metadata.mojom
index 9ae2759..95c491ee 100644
--- a/services/viz/public/mojom/compositing/compositor_frame_metadata.mojom
+++ b/services/viz/public/mojom/compositing/compositor_frame_metadata.mojom
@@ -35,6 +35,7 @@
   bool may_contain_video;
   bool may_throttle_if_undrawn_frames;
   bool is_handling_interaction;
+  bool is_handling_animation;
   skia.mojom.SkColor4f root_background_color;
   array<ui.mojom.LatencyInfo> latency_info;
   array<SurfaceRange> referenced_surfaces;
diff --git a/services/viz/public/mojom/compositing/transferable_resource.mojom b/services/viz/public/mojom/compositing/transferable_resource.mojom
index 688db8b1..3038b05 100644
--- a/services/viz/public/mojom/compositing/transferable_resource.mojom
+++ b/services/viz/public/mojom/compositing/transferable_resource.mojom
@@ -23,12 +23,6 @@
   kReleaseFence,
 };
 
-// See components/viz/common/resources/transferable_resource.h.
-union MemoryBufferId {
-  gpu.mojom.Mailbox mailbox;
-  SharedBitmapId shared_bitmap_id;
-};
-
 // Represents a visual resource (e.g., image, video frame) that can be
 // transferred between processes for rendering and compositing within Chromium's
 // Viz display system. Encapsulates the resource's data, format, and metadata
@@ -39,7 +33,7 @@
   ResourceId id;
   SharedImageFormat format;
   gfx.mojom.Size size;
-  MemoryBufferId memory_buffer_id;
+  gpu.mojom.Mailbox memory_buffer_id;
   gpu.mojom.SyncToken sync_token;
   uint32 texture_target;
   SynchronizationType synchronization_type;
diff --git a/styleguide/java/nullaway.md b/styleguide/java/nullaway.md
index 1312756c..8a48f880 100644
--- a/styleguide/java/nullaway.md
+++ b/styleguide/java/nullaway.md
@@ -16,7 +16,7 @@
 [Chromium's NullAway configuration] is as follows:
 * [JSpecify mode] is enabled.
    * `@Nullable` is `TYPE_USE`.
-   * Non-annotated means non-null for fields, parameters, and return types.
+   * Non-annotated means non-null (no need for `@NonNull`).
    * Nullness of local variables is inferred.
 * Copies of [supported annotations] exist under
   `org.chromium.build.annotations`.
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 133e945..1e4a2d3 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -240,6 +240,7 @@
   testonly = true
 
   data = [
+    "//testing/buildbot/filters/android.emulator_10.content_unittests.filter",
     "//testing/buildbot/filters/android.emulator_14.content_unittests.filter",
     "//testing/buildbot/filters/android.emulator_15_16.content_unittests.filter",
     "//testing/buildbot/filters/android.device.content_unittests.filter",
diff --git a/testing/buildbot/filters/android.emulator_10.content_unittests.filter b/testing/buildbot/filters/android.emulator_10.content_unittests.filter
new file mode 100644
index 0000000..359ceb8d
--- /dev/null
+++ b/testing/buildbot/filters/android.emulator_10.content_unittests.filter
@@ -0,0 +1,2 @@
+# crbug.com/390669723
+-FontUniqueNameLookupTest.TestMatchPostScriptNameTtc
\ No newline at end of file
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index a0eafd3..5835ec90 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -341,6 +341,38 @@
             ]
         }
     ],
+    "AndroidAppIntegrationV2": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled_SkipSchemaCheck",
+                    "params": {
+                        "skip_schema_check": "true",
+                        "use_large_favicon": "true"
+                    },
+                    "enable_features": [
+                        "AndroidAppIntegrationModule",
+                        "AndroidAppIntegrationV2",
+                        "AndroidAppIntegrationWithFavicon"
+                    ]
+                },
+                {
+                    "name": "Enabled",
+                    "params": {
+                        "use_large_favicon": "true"
+                    },
+                    "enable_features": [
+                        "AndroidAppIntegrationModule",
+                        "AndroidAppIntegrationV2",
+                        "AndroidAppIntegrationWithFavicon"
+                    ]
+                }
+            ]
+        }
+    ],
     "AndroidAutofillPrefillRequestsForChangePassword": [
         {
             "platforms": [
@@ -5349,8 +5381,10 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled",
+                    "name": "EnabledV2",
                     "params": {
+                        "disable_blend": "true",
+                        "enable_v2": "true",
                         "max_tiles_number": "1",
                         "show_see_more": "true",
                         "show_tabs_in_one_module": "true",
@@ -5362,10 +5396,8 @@
                     ]
                 },
                 {
-                    "name": "EnabledV2",
+                    "name": "Enabled",
                     "params": {
-                        "disable_blend": "true",
-                        "enable_v2": "true",
                         "max_tiles_number": "1",
                         "show_see_more": "true",
                         "show_tabs_in_one_module": "true",
@@ -9584,21 +9616,6 @@
             ]
         }
     ],
-    "ExternalRateControlForMediaFoundationVideoEncoder": [
-        {
-            "platforms": [
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "MediaFoundationUseSWBRCForH264Camera"
-                    ]
-                }
-            ]
-        }
-    ],
     "ExtremeLightweightUAFDetector": [
         {
             "platforms": [
@@ -11635,21 +11652,6 @@
             ]
         }
     ],
-    "IOSDisableParcelTracking": [
-        {
-            "platforms": [
-                "ios"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "IOSDisableParcelTracking"
-                    ]
-                }
-            ]
-        }
-    ],
     "IOSDockingPromo": [
         {
             "platforms": [
@@ -14868,27 +14870,6 @@
             ]
         }
     ],
-    "NewEvSignalsEnabled": [
-        {
-            "platforms": [
-                "windows",
-                "mac",
-                "linux"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "params": {
-                        "DisableFileSystemInfo": "true",
-                        "DisableSettings": "true"
-                    },
-                    "enable_features": [
-                        "NewEvSignalsEnabled"
-                    ]
-                }
-            ]
-        }
-    ],
     "NewOSCryptAlgorithmForPasswords": [
         {
             "platforms": [
@@ -21965,6 +21946,27 @@
             ]
         }
     ],
+    "ServiceWorkerSrcdocSupport": [
+        {
+            "platforms": [
+                "android",
+                "android_webview",
+                "chromeos",
+                "fuchsia",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "ServiceWorkerSrcdocSupport"
+                    ]
+                }
+            ]
+        }
+    ],
     "ServiceWorkerStaticRouterRaceNetworkRequestPerformanceImprovement": [
         {
             "platforms": [
@@ -24068,6 +24070,22 @@
             ]
         }
     ],
+    "UseAdHocSigningForWebAppShims": [
+        {
+            "platforms": [
+                "mac"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "MachPortRendezvousValidatePeerRequirements",
+                        "UseAdHocSigningForWebAppShims"
+                    ]
+                }
+            ]
+        }
+    ],
     "UseBoringSSLForRandBytes": [
         {
             "platforms": [
@@ -24941,10 +24959,7 @@
             ],
             "experiments": [
                 {
-                    "name": "Enabled_2_pending_frames",
-                    "params": {
-                        "PendingFrames": "2"
-                    },
+                    "name": "Enabled",
                     "enable_features": [
                         "VSyncAlignedPresent"
                     ]
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn
index 0f84aa08..26b9996c9 100644
--- a/third_party/android_deps/BUILD.gn
+++ b/third_party/android_deps/BUILD.gn
@@ -694,28 +694,6 @@
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
-  java_prebuilt("com_google_dagger_hilt_core_java") {
-    jar_path = "cipd/libs/com_google_dagger_hilt_core/hilt-core-2.52.jar"
-    output_name = "com_google_dagger_hilt_core"
-    supports_android = true
-    enable_bytecode_checks = false
-    deps = [
-      ":com_google_code_findbugs_jsr305_java",
-      ":javax_inject_javax_inject_java",
-      "//third_party/android_deps:dagger_java",
-    ]
-
-    # https://crbug.com/1412551
-    requires_android = true
-
-    # Google3 organizes targets differently from maven. Restrict to the only classes we use.
-    jar_included_patterns = [
-      "dagger/hilt/internal/GeneratedComponentManager.class",
-      "dagger/hilt/internal/GeneratedComponentManagerHolder.class",
-    ]
-  }
-
-  # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
   if (google_play_services_package == "//third_party/android_deps") {
     android_aar_prebuilt("google_firebase_firebase_iid_java") {
       aar_path =
diff --git a/third_party/android_deps/additional_readme_paths.json b/third_party/android_deps/additional_readme_paths.json
index 36c906a..bec9a62 100644
--- a/third_party/android_deps/additional_readme_paths.json
+++ b/third_party/android_deps/additional_readme_paths.json
@@ -36,7 +36,6 @@
     "libs/com_google_code_findbugs_jsr305",
     "libs/com_google_code_gson_gson",
     "libs/com_google_dagger_dagger",
-    "libs/com_google_dagger_hilt_core",
     "libs/com_google_errorprone_error_prone_annotations",
     "libs/com_google_firebase_firebase_annotations",
     "libs/com_google_firebase_firebase_common",
diff --git a/third_party/android_deps/build.gradle b/third_party/android_deps/build.gradle
index 40e7d31b..9960005 100644
--- a/third_party/android_deps/build.gradle
+++ b/third_party/android_deps/build.gradle
@@ -142,7 +142,6 @@
 
     String daggerVersion = '2.52'
     compile "com.google.dagger:dagger:${daggerVersion}"
-    compile "com.google.dagger:hilt-core:${daggerVersion}"
 
     // Used by ModuleInterfaceProcessor.java
     buildCompile 'com.squareup:javapoet:1.13.0'
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
index 71ae93c..f41bb3ff 100644
--- a/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
+++ b/third_party/android_deps/buildSrc/src/main/groovy/BuildConfigGenerator.groovy
@@ -716,7 +716,6 @@
         if (dependencyExtension == 'jar' && (
                 dependencyId.startsWith('io_grpc_') ||
                 dependencyId == 'com_google_firebase_firebase_encoders' ||
-                dependencyId == 'com_google_dagger_hilt_core' ||
                 dependencyId == 'com_google_guava_guava_android')) {
             sb.append('  # https://crbug.com/1412551\n')
             sb.append('  requires_android = true\n')
@@ -816,14 +815,6 @@
                 // and android_aar_prebuilt template will fail if it's not set explictly.
                 sb.append('  extract_native_libraries = true\n')
                 break
-            case 'com_google_dagger_hilt_core':
-                sb.append('\n')
-                sb.append('  # Google3 organizes targets differently from maven. Restrict to the only classes we use.\n')
-                sb.append('  jar_included_patterns = [\n')
-                sb.append('    "dagger/hilt/internal/GeneratedComponentManager.class",\n')
-                sb.append('    "dagger/hilt/internal/GeneratedComponentManagerHolder.class",\n')
-                sb.append('  ]\n')
-                break
             case 'com_google_auto_service_auto_service_annotations_java':
                 sb.append('  preferred_dep = true\n')
                 break
diff --git a/third_party/android_deps/libs/com_google_dagger_hilt_core/3pp/3pp.pb b/third_party/android_deps/libs/com_google_dagger_hilt_core/3pp/3pp.pb
deleted file mode 100644
index d743b51b..0000000
--- a/third_party/android_deps/libs/com_google_dagger_hilt_core/3pp/3pp.pb
+++ /dev/null
@@ -1,16 +0,0 @@
-# Copyright 2021 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
-
-create {
-  source {
-    script { name: "fetch.py" }
-  }
-}
-
-upload {
-  pkg_prefix: "chromium/third_party/android_deps/libs"
-  universal: true
-}
diff --git a/third_party/android_deps/libs/com_google_dagger_hilt_core/3pp/fetch.py b/third_party/android_deps/libs/com_google_dagger_hilt_core/3pp/fetch.py
deleted file mode 100755
index 772e0a0..0000000
--- a/third_party/android_deps/libs/com_google_dagger_hilt_core/3pp/fetch.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/usr/bin/env python3
-# Copyright 2021 The Chromium Authors
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# This is generated, do not edit. Update BuildConfigGenerator.groovy and
-# 3ppFetch.template instead.
-
-import pathlib
-import sys
-
-_3PP_DIR = pathlib.Path(__file__).resolve().parent
-sys.path.insert(0, str(_3PP_DIR.parents[2]))
-import fetch_common
-
-_REPO_URL = 'https://repo.maven.apache.org/maven2'
-SPEC = fetch_common.Spec(repo_url=_REPO_URL,
-                         group_name='com/google/dagger',
-                         module_name='hilt-core',
-                         file_ext='jar',
-                         patch_version='cr1',
-                         version_override=None,
-                         version_filter=None)
-
-if __name__ == '__main__':
-    fetch_common.main(SPEC)
diff --git a/third_party/android_deps/libs/com_google_dagger_hilt_core/LICENSE b/third_party/android_deps/libs/com_google_dagger_hilt_core/LICENSE
deleted file mode 100644
index d645695..0000000
--- a/third_party/android_deps/libs/com_google_dagger_hilt_core/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
diff --git a/third_party/android_deps/libs/com_google_dagger_hilt_core/OWNERS b/third_party/android_deps/libs/com_google_dagger_hilt_core/OWNERS
deleted file mode 100644
index aea47a05..0000000
--- a/third_party/android_deps/libs/com_google_dagger_hilt_core/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-file://third_party/android_deps/OWNERS
diff --git a/third_party/android_deps/libs/com_google_dagger_hilt_core/README.chromium b/third_party/android_deps/libs/com_google_dagger_hilt_core/README.chromium
deleted file mode 100644
index 63a52ab..0000000
--- a/third_party/android_deps/libs/com_google_dagger_hilt_core/README.chromium
+++ /dev/null
@@ -1,15 +0,0 @@
-Name: Hilt Core
-Short Name: hilt-core
-URL: https://github.com/google/dagger
-Version: 2.52
-License: Apache-2.0
-License File: LICENSE
-CPEPrefix: unknown
-Security Critical: yes
-Shipped: yes
-
-Description:
-A fast dependency injector for Android and Java.
-
-Local Modifications:
-No modifications.
diff --git a/third_party/android_deps/libs/com_google_dagger_hilt_core/cipd.yaml b/third_party/android_deps/libs/com_google_dagger_hilt_core/cipd.yaml
deleted file mode 100644
index 1aca8a3..0000000
--- a/third_party/android_deps/libs/com_google_dagger_hilt_core/cipd.yaml
+++ /dev/null
@@ -1,10 +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.
-
-# To create CIPD package run the following command.
-# cipd create --pkg-def cipd.yaml -tag version:2@2.52.cr1
-package: chromium/third_party/android_deps/libs/com_google_dagger_hilt_core
-description: "Hilt Core"
-data:
-- file: hilt-core-2.52.jar
diff --git a/third_party/angle b/third_party/angle
index 6dc14de..0dabfe5 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 6dc14dedf85a49786d5503fdf5e692d87d57bb2d
+Subproject commit 0dabfe5a6ac5377854a3f5ee673d56315c175e71
diff --git a/third_party/blink/common/permissions_policy/README.md b/third_party/blink/common/permissions_policy/README.md
index 8bbaff4..f1f7e9f 100644
--- a/third_party/blink/common/permissions_policy/README.md
+++ b/third_party/blink/common/permissions_policy/README.md
@@ -1,6 +1,6 @@
 ## Permissions Policy Guide (Previously Feature Policy)
 Permissions policy is the new name for feature policy with a new HTTP header which uses
-[structured header](https://www.rfc-editor.org/rfc/rfc9651) syntax.
+[Structured Field](https://www.rfc-editor.org/rfc/rfc9651) syntax.
 
 ### How to add a new feature to permissions policy
 
@@ -103,10 +103,11 @@
 
 Example HTTP header: `Document-Policy: force-load-at-top=?0, lossy-images-max-bpp=1.0`
 
-- `force-load-at-top` is set to boolean value false (`?0` in [structured header syntax](https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html)), i.e. the
- feature is disallowed in current document;
-- `lossy-images-max-bpp` is set to 1.0, i.e. lossy image format (e.g. jpeg) images with
-byte per pixel rate higher than 1.0 will be blocked.
+- `force-load-at-top` is set to boolean value false (`?0` in [Structured Field
+syntax](https://www.rfc-editor.org/rfc/rfc9651#section-3.3.6)), i.e. the feature
+is disallowed in current document;
+- `lossy-images-max-bpp` is set to 1.0, i.e. lossy image format (e.g. jpeg)
+images with byte per pixel rate higher than 1.0 will be blocked.
 
 
 #### Adding a new feature to document policy
diff --git a/third_party/blink/perf_tests/webcodecs/copyTo-test.js b/third_party/blink/perf_tests/webcodecs/copyTo-test.js
index cae17ce9..6d18a00 100644
--- a/third_party/blink/perf_tests/webcodecs/copyTo-test.js
+++ b/third_party/blink/perf_tests/webcodecs/copyTo-test.js
@@ -10,10 +10,10 @@
 
 function runCopyToTest(frame, desc) {
   let isDone = false;
+  let size = frame.allocationSize();
+  let buf = new makeSharedBuffer(size);
 
   function runTest() {
-    let size = frame.allocationSize();
-    let buf = new makeSharedBuffer(size);
     let startTime = PerfTestRunner.now();
     PerfTestRunner.addRunTestStartMarker();
     frame.copyTo(buf)
@@ -43,16 +43,15 @@
 
 function runBatchCopyToTest(frames, desc) {
   let isDone = false;
+  let frames_and_buffers = frames.map(frame => {
+    let size = frame.allocationSize();
+    let buf = new makeSharedBuffer(size);
+    return [frame, buf];
+  });
 
   function runTest() {
     let startTime = PerfTestRunner.now();
     PerfTestRunner.addRunTestStartMarker();
-
-    let frames_and_buffers = frames.map(frame => {
-      let size = frame.allocationSize();
-      let buf = new makeSharedBuffer(size);
-      return [frame, buf];
-    });
     let readback_promises = frames_and_buffers.map(([frame, buf]) => {
       return frame.copyTo(buf);
     });
diff --git a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
index 103a4b29..88fb5e1b 100644
--- a/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
+++ b/third_party/blink/public/mojom/use_counter/metrics/web_feature.mojom
@@ -684,16 +684,16 @@
   kExecCommandOnInputOrTextarea = 1027,
   kV8History_ScrollRestoration_AttributeGetter = 1028,
   kV8History_ScrollRestoration_AttributeSetter = 1029,
-  kSVG1DOMFilter = 1030,
+  kObsoleteSVG1DOMFilter = 1030,
   kOfflineAudioContextStartRendering = 1031,
   kOfflineAudioContextSuspend = 1032,
   kOfflineAudioContextResume = 1033,
-  kSVG1DOMPaintServer = 1035,
+  kObsoleteSVG1DOMPaintServer = 1035,
   kSVGSVGElementFragmentSVGView = 1036,
   kSVGSVGElementFragmentSVGViewElement = 1037,
   kPresentationConnectionClose = 1038,
   kSVG1DOMShape = 1039,
-  kSVG1DOMText = 1040,
+  kObsoleteSVG1DOMText = 1040,
   kRTCPeerConnectionConstructorConstraints = 1041,
   kRTCPeerConnectionConstructorCompliant = 1042,
   kRTCPeerConnectionCreateOfferLegacyFailureCallback = 1044,
diff --git a/third_party/blink/renderer/bindings/IDLExtendedAttributes.md b/third_party/blink/renderer/bindings/IDLExtendedAttributes.md
index afe3cce2a..8bcc4034 100644
--- a/third_party/blink/renderer/bindings/IDLExtendedAttributes.md
+++ b/third_party/blink/renderer/bindings/IDLExtendedAttributes.md
@@ -707,7 +707,6 @@
 ```webidl
 [HighEntropy] attribute Node interestingAttribute;
 [HighEntropy] Node getInterestingNode();
-[HighEntropy] const INTERESTING_CONSTANT = 1;
 ```
 
 Attributes and methods labeled with `[HighEntropy=Direct]` are simple surfaces which can be expressed as a sequence of bytes without any need for additional parsing logic.
@@ -764,7 +763,7 @@
 ```webidl
 [Measure] attribute Node interestingAttribute;
 [Measure] Node getInterestingNode();
-[Measure] const INTERESTING_CONSTANT = 1;
+// Note that Measure is longer supported on constants.
 ```
 
 ### [MeasureAs]
@@ -781,7 +780,7 @@
 ```webidl
 [MeasureAs=AttributeWeAreInterestedIn] attribute Node interestingAttribute;
 [MeasureAs=MethodsAreInterestingToo] Node getInterestingNode();
-[MeasureAs=EvenSomeConstantsAreInteresting] const INTERESTING_CONSTANT = 1;
+// Note that MeasureAs is longer supported on constants.
 ```
 
 ### [NotEnumerable]
diff --git a/third_party/blink/renderer/bindings/core/v8/script_function.cc b/third_party/blink/renderer/bindings/core/v8/script_function.cc
index abadeca..a78b8c7 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_function.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_function.cc
@@ -27,8 +27,7 @@
     kDOMWrappersTag,
     WrapperTypeInfo::kWrapperTypeNoPrototype,
     WrapperTypeInfo::kCustomWrappableId,
-    WrapperTypeInfo::kNotInheritFromActiveScriptWrappable,
-    WrapperTypeInfo::kCustomWrappableKind,
+    WrapperTypeInfo::kIdlOtherType,
 };
 
 }  // namespace
diff --git a/third_party/blink/renderer/bindings/generated_in_modules.gni b/third_party/blink/renderer/bindings/generated_in_modules.gni
index d0e982dc..0d68f8d 100644
--- a/third_party/blink/renderer/bindings/generated_in_modules.gni
+++ b/third_party/blink/renderer/bindings/generated_in_modules.gni
@@ -2470,6 +2470,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_launch_params.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_launch_queue.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_launch_queue.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_layout_worklet_global_scope.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_layout_worklet_global_scope.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_linear_acceleration_sensor.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_linear_acceleration_sensor.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_lock.cc",
@@ -2814,6 +2816,8 @@
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_service_worker_global_scope.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_service_worker_registration.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_service_worker_registration.h",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_shadow_realm_global_scope.cc",
+  "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_shadow_realm_global_scope.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_shared_storage.cc",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_shared_storage.h",
   "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_shared_storage_worklet.cc",
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
index 3d7a12e..8303f4df 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -2248,35 +2248,6 @@
     return func_def
 
 
-def make_constant_callback_def(cg_context, function_name):
-    assert isinstance(cg_context, CodeGenContext)
-    assert isinstance(function_name, str)
-
-    logging_nodes = SequenceNode([
-        make_report_deprecate_as(cg_context),
-        make_report_measure_as(cg_context),
-        make_log_activity(cg_context),
-    ])
-    if not logging_nodes:
-        return None
-
-    func_def = _make_empty_callback_def(cg_context, function_name)
-    body = func_def.body
-
-    v8_set_return_value = _format(
-        "bindings::V8SetReturnValue(${info}, ${class_name}::Constant::{});",
-        constant_name(cg_context))
-    body.extend([
-        make_runtime_call_timer_scope(cg_context),
-        make_bindings_trace_event(cg_context),
-        logging_nodes,
-        EmptyNode(),
-        TextNode(v8_set_return_value),
-    ])
-
-    return func_def
-
-
 def make_constant_constant_def(cg_context, constant_name):
     # IDL constant's C++ constant definition
     assert isinstance(cg_context, CodeGenContext)
@@ -4634,43 +4605,11 @@
     ])
 
 
-def _make_constant_callback_registration_table(table_name, constant_entries):
-    assert isinstance(table_name, str)
-    assert isinstance(constant_entries, (list, tuple))
-    assert all(
-        isinstance(entry, _PropEntryConstant)
-        and isinstance(entry.const_callback_name, str)
-        for entry in constant_entries)
-
-    T = TextNode
-
-    entry_nodes = []
-    pattern = (
-        "{{"  #
-        "\"{property_name}\", "
-        "{constant_callback}"
-        "}},")
-    for entry in constant_entries:
-        text = _format(
-            pattern,
-            property_name=entry.property_.identifier,
-            constant_callback=entry.const_callback_name)
-        entry_nodes.append(T(text))
-
-    return ListNode([
-        T("static const IDLMemberInstaller::ConstantCallbackConfig " +
-          table_name + "[] = {"),
-        ListNode(entry_nodes),
-        T("};"),
-    ])
-
-
 def _make_constant_value_registration_table(table_name, constant_entries):
     assert isinstance(table_name, str)
     assert isinstance(constant_entries, (list, tuple))
     assert all(
-        isinstance(entry, _PropEntryConstant)
-        and entry.const_callback_name is None for entry in constant_entries)
+        isinstance(entry, _PropEntryConstant) for entry in constant_entries)
 
     T = TextNode
 
@@ -4892,13 +4831,11 @@
 
 class _PropEntryConstant(_PropEntryBase):
     def __init__(self, is_context_dependent, exposure_conditional, world,
-                 constant, const_callback_name, const_constant_name):
-        assert _is_none_or_str(const_callback_name)
+                 constant, const_constant_name):
         assert isinstance(const_constant_name, str)
 
         _PropEntryBase.__init__(self, is_context_dependent,
                                 exposure_conditional, world, constant)
-        self.const_callback_name = const_callback_name
         self.const_constant_name = const_constant_name
 
 
@@ -5053,27 +4990,16 @@
             constant=constant,
             for_world=world,
             v8_callback_type=CodeGenContext.V8_ACCESSOR_NAME_GETTER_CALLBACK)
-        const_callback_name = callback_function_name(cgc)
-        const_callback_node = make_constant_callback_def(
-            cgc, const_callback_name)
-        if const_callback_node is None:
-            const_callback_name = None
         # IDL constant's C++ constant name
         const_constant_name = _format("${class_name}::Constant::{}",
                                       constant_name(cgc))
 
-        callback_def_nodes.extend([
-            const_callback_node,
-            EmptyNode(),
-        ])
-
         constant_entries.append(
             _PropEntryConstant(
                 is_context_dependent=is_context_dependent,
                 exposure_conditional=exposure_conditional,
                 world=world,
                 constant=constant,
-                const_callback_name=const_callback_name,
                 const_constant_name=const_constant_name))
 
     def process_constructor_group(constructor_group, is_context_dependent,
@@ -5936,25 +5862,12 @@
     install_properties(table_name, entries, _make_attribute_registration_table,
                        installer_call_text)
 
-    table_name = "kConstantCallbackTable"
-    installer_call_text = _format(
-        pattern_without_interface_name,
-        install_func="IDLMemberInstaller::InstallConstants",
-        table_name=table_name)
-    constant_callback_entries = list(
-        filter(lambda entry: entry.const_callback_name, constant_entries))
-    install_properties(table_name, constant_callback_entries,
-                       _make_constant_callback_registration_table,
-                       installer_call_text)
-
     table_name = "kConstantValueTable"
     installer_call_text = _format(
         pattern_without_interface_name,
         install_func="IDLMemberInstaller::InstallConstants",
         table_name=table_name)
-    constant_value_entries = list(
-        filter(lambda entry: not entry.const_callback_name, constant_entries))
-    install_properties(table_name, constant_value_entries,
+    install_properties(table_name, constant_entries,
                        _make_constant_value_registration_table,
                        installer_call_text)
 
@@ -6539,7 +6452,6 @@
     ${class_name}::kMaxSubclassTag,
     {wrapper_type_prototype},
     {wrapper_class_id},
-    {active_script_wrappable_inheritance},
     {idl_definition_kind},
     {is_skipped_in_interface_object_prototype_chain},
 }};
@@ -6567,20 +6479,12 @@
         wrapper_class_id = "WrapperTypeInfo::kNodeClassId"
     else:
         wrapper_class_id = "WrapperTypeInfo::kObjectClassId"
-    if class_like.code_generator_info.is_active_script_wrappable:
-        active_script_wrappable_inheritance = (
-            "WrapperTypeInfo::kInheritFromActiveScriptWrappable")
-    else:
-        active_script_wrappable_inheritance = (
-            "WrapperTypeInfo::kNotInheritFromActiveScriptWrappable")
-    if class_like.is_interface:
+    if class_like.is_interface or class_like.is_callback_interface:
         idl_definition_kind = "WrapperTypeInfo::kIdlInterface"
     elif class_like.is_namespace:
         idl_definition_kind = "WrapperTypeInfo::kIdlNamespace"
-    elif class_like.is_callback_interface:
-        idl_definition_kind = "WrapperTypeInfo::kIdlCallbackInterface"
     elif class_like.is_async_iterator or class_like.is_sync_iterator:
-        idl_definition_kind = "WrapperTypeInfo::kIdlAsyncOrSyncIterator"
+        idl_definition_kind = "WrapperTypeInfo::kIdlOtherType"
     else:
         assert False
     is_skipped_in_interface_object_prototype_chain = (
@@ -6592,8 +6496,6 @@
           wrapper_type_info_of_inherited=wrapper_type_info_of_inherited,
           wrapper_type_prototype=wrapper_type_prototype,
           wrapper_class_id=wrapper_class_id,
-          active_script_wrappable_inheritance=(
-              active_script_wrappable_inheritance),
           idl_definition_kind=idl_definition_kind,
           is_skipped_in_interface_object_prototype_chain=(
               is_skipped_in_interface_object_prototype_chain)))
@@ -6695,9 +6597,6 @@
         if entry.exposure_conditional.is_always_true:
             callback_names.append(entry.attr_get_callback_name)
             callback_names.append(entry.attr_set_callback_name)
-    for entry in constant_entries:
-        if entry.exposure_conditional.is_always_true:
-            callback_names.append(entry.const_callback_name)
     for entry in constructor_entries:
         if entry.exposure_conditional.is_always_true:
             callback_names.append(entry.ctor_callback_name)
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/observable_array.py b/third_party/blink/renderer/bindings/scripts/bind_gen/observable_array.py
index e6a93ea..ae77342 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/observable_array.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/observable_array.py
@@ -86,8 +86,7 @@
     kDOMWrappersTag,
     WrapperTypeInfo::kWrapperTypeNoPrototype,
     WrapperTypeInfo::kObjectClassId,
-    WrapperTypeInfo::kNotInheritFromActiveScriptWrappable,
-    WrapperTypeInfo::kIdlObservableArray,
+    WrapperTypeInfo::kIdlOtherType,
 };
 
 // static
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/path_manager.py b/third_party/blink/renderer/bindings/scripts/bind_gen/path_manager.py
index 95a24e9b..de97f07 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/path_manager.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/path_manager.py
@@ -9,6 +9,7 @@
 
 from . import name_style
 from .blink_v8_bridge import blink_class_name
+from web_idl.composition_parts import WithExtendedAttributes
 
 
 class PathManager(object):
@@ -104,9 +105,22 @@
             self._impl_component = default_component
         elif len(components) == 1:
             component = components[0]
-            self._is_cross_components = False
-            self._api_component = component
-            self._impl_component = component
+            # Global interfaces generally have exposed constructors, which we
+            # don't currently label with their component. If a global interface
+            # is defined in core, put the impl in modules even if no partial
+            # interfaces are defined in modules.
+            # TODO(japhet, caseq): Figure out why exposed constructors don't
+            # influence component calculations.
+            if (isinstance(idl_definition, WithExtendedAttributes)
+                    and "Global" in idl_definition.extended_attributes
+                    and component == "core"):
+                self._is_cross_components = True
+                self._api_component = web_idl.Component("core")
+                self._impl_component = web_idl.Component("modules")
+            else:
+                self._is_cross_components = False
+                self._api_component = component
+                self._impl_component = component
         elif len(components) == 2:
             assert components[0] == "core"
             assert components[1] == "modules"
diff --git a/third_party/blink/renderer/core/dom/container_node.cc b/third_party/blink/renderer/core/dom/container_node.cc
index 0ac17ca2..0778eed 100644
--- a/third_party/blink/renderer/core/dom/container_node.cc
+++ b/third_party/blink/renderer/core/dom/container_node.cc
@@ -171,7 +171,11 @@
     ExceptionState& exception_state) {
   if (auto* fragment = DynamicTo<DocumentFragment>(node)) {
     GetChildNodes(*fragment, nodes);
-    fragment->RemoveChildren();
+    if (fragment->HoldsUnnotifiedChildren()) {
+      fragment->ForgetChildren();
+    } else {
+      fragment->RemoveChildren();
+    }
     return !nodes.empty();
   }
   nodes.push_back(&node);
@@ -1247,7 +1251,8 @@
   probe::DidInsertDOMNode(this);
 }
 
-void ContainerNode::ParserFinishedBuildingDocumentFragment() {
+void ContainerNode::ParserFinishedBuildingDocumentFragment(
+    ShouldNotifyInsertedNodes call_mode) {
   EventDispatchForbiddenScope assert_no_event_dispatch;
   ScriptForbiddenScope forbid_script;
   const bool may_contain_shadow_roots = GetDocument().MayContainShadowRoots();
@@ -1256,10 +1261,11 @@
       ChildrenChange::ForFinishingBuildingDocumentFragmentTree();
   for (Node& node : NodeTraversal::DescendantsOf(*this)) {
     NotifyNodeAtEndOfBuildingFragmentTree(node, change,
-                                          may_contain_shadow_roots);
+                                          may_contain_shadow_roots, call_mode);
   }
 
-  if (GetDocument().ShouldInvalidateNodeListCaches(nullptr)) {
+  if (call_mode == ShouldNotifyInsertedNodes::kNotify &&
+      GetDocument().ShouldInvalidateNodeListCaches(nullptr)) {
     GetDocument().InvalidateNodeListCaches(nullptr);
   }
 }
@@ -1267,7 +1273,8 @@
 void ContainerNode::NotifyNodeAtEndOfBuildingFragmentTree(
     Node& node,
     const ChildrenChange& change,
-    bool may_contain_shadow_roots) {
+    bool may_contain_shadow_roots,
+    ShouldNotifyInsertedNodes call_mode) {
   // Fast path parser only creates disconnected nodes.
   DCHECK(!node.isConnected());
 
@@ -1287,13 +1294,15 @@
   // kInsertionShouldCallDidNotifySubtreeInsertions, but only if the node
   // is connected. None of the nodes are connected at this point, so it's
   // not needed here.
-  node.InsertedInto(*this);
+  if (call_mode == ShouldNotifyInsertedNodes::kNotify) {
+    node.InsertedInto(*this);
+  }
 
   if (ShadowRoot* shadow_root = node.GetShadowRoot()) {
     for (Node& shadow_node :
          NodeTraversal::InclusiveDescendantsOf(*shadow_root)) {
-      NotifyNodeAtEndOfBuildingFragmentTree(shadow_node, change,
-                                            may_contain_shadow_roots);
+      NotifyNodeAtEndOfBuildingFragmentTree(
+          shadow_node, change, may_contain_shadow_roots, call_mode);
     }
   }
 
diff --git a/third_party/blink/renderer/core/dom/container_node.h b/third_party/blink/renderer/core/dom/container_node.h
index f9b48f2..fff8df5 100644
--- a/third_party/blink/renderer/core/dom/container_node.h
+++ b/third_party/blink/renderer/core/dom/container_node.h
@@ -188,7 +188,11 @@
   // Called when the parser has finished building a DocumentFragment. This is
   // not called if the parser fails parsing (if parsing fails, the
   // DocumentFragment is orphaned and will eventually be gc'd).
-  void ParserFinishedBuildingDocumentFragment();
+  //
+  // ShouldNotifyInsertedNodes controls whether to skip notifications that are
+  // redone if the contents of the DocumentFragment are moved to a new parent.
+  enum class ShouldNotifyInsertedNodes { kNotify, kSkip };
+  void ParserFinishedBuildingDocumentFragment(ShouldNotifyInsertedNodes);
   void ParserRemoveChild(Node&);
   void ParserInsertBefore(Node* new_child, Node& ref_child);
   void ParserTakeAllChildrenFrom(ContainerNode&);
@@ -528,7 +532,8 @@
   // it was inserted.
   void NotifyNodeAtEndOfBuildingFragmentTree(Node& node,
                                              const ChildrenChange& change,
-                                             bool may_contain_shadow_roots);
+                                             bool may_contain_shadow_roots,
+                                             ShouldNotifyInsertedNodes);
 
   NodeListsNodeData& EnsureNodeLists();
   void RemoveBetween(Node* previous_child, Node* next_child, Node& old_child);
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 6395917c..a55e16e 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -1643,6 +1643,8 @@
                                                 parser_behavior, nullptr);
     SetParsingState(kFinishedParsing);
     if (success) {
+      body->ParserFinishedBuildingDocumentFragment(
+          ShouldNotifyInsertedNodes::kNotify);
       // When DCHECK is enabled, use SetContent() and verify fast-path
       // content matches. This effectively means the results of the fast-path
       // parser aren't used with DCHECK enabled, but it provides a way to
diff --git a/third_party/blink/renderer/core/dom/document_fragment.cc b/third_party/blink/renderer/core/dom/document_fragment.cc
index 31fd2d1..c548af0e 100644
--- a/third_party/blink/renderer/core/dom/document_fragment.cc
+++ b/third_party/blink/renderer/core/dom/document_fragment.cc
@@ -103,6 +103,26 @@
       source, this, context_element, parser_content_policy, exception_state);
 }
 
+void DocumentFragment::ForgetChildren() {
+  DCHECK(HoldsUnnotifiedChildren());
+
+  if (!hasChildren()) {
+    return;
+  }
+
+  Node* next_child = firstChild();
+  do {
+    Node* child = next_child;
+    child->SetParentOrShadowHostNode(nullptr);
+    child->SetPreviousSibling(nullptr);
+    next_child = child->nextSibling();
+    child->SetNextSibling(nullptr);
+  } while (next_child);
+
+  SetFirstChild(nullptr);
+  SetLastChild(nullptr);
+}
+
 void DocumentFragment::Trace(Visitor* visitor) const {
   visitor->Trace(document_part_root_);
   ContainerNode::Trace(visitor);
diff --git a/third_party/blink/renderer/core/dom/document_fragment.h b/third_party/blink/renderer/core/dom/document_fragment.h
index c71cf52..fda76b52 100644
--- a/third_party/blink/renderer/core/dom/document_fragment.h
+++ b/third_party/blink/renderer/core/dom/document_fragment.h
@@ -53,6 +53,19 @@
   bool CanContainRangeEndPoint() const final { return true; }
   virtual bool IsTemplateContent() const { return false; }
 
+  // These represent whether this DocumentFragment is one that holds
+  // children that have not had Node::InsertedInto called.  We use such
+  // DocumentFragment objects internally for temporary storage of
+  // parsing results.  This requires that when we move the children
+  // *out* of the DocumentFragment we also not call RemovedFrom.
+  bool HoldsUnnotifiedChildren() const { return holds_unnotified_children_; }
+  void SetHoldsUnnotifiedChildren(bool v) { holds_unnotified_children_ = v; }
+
+  // When HoldsUnnotifiedChildren() is true, a caller that is taking the
+  // children can call ForgetChildren to disconnect them without any
+  // notifications.
+  void ForgetChildren();
+
   // This will catch anyone doing an unnecessary check.
   bool IsDocumentFragment() const = delete;
 
@@ -74,6 +87,7 @@
   bool ChildTypeAllowed(NodeType) const override;
 
   Member<DocumentPartRoot> document_part_root_;
+  bool holds_unnotified_children_ = false;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index b64fcd3..eb91d32 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -1273,45 +1273,63 @@
 
 void Element::InterestGained() {
   CHECK(RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled());
+  CHECK(IsInTreeScope());
+  CHECK(GetDocument().IsActive());
 
-  if (!IsInTreeScope()) {
-    return;
-  }
-
-  Element* interest_target_element = this->interestTargetElement();
-  AtomicString interest_action = this->interestAction();
-  if (interest_target_element && !interest_action.IsNull()) {
-    // TODO(crbug.com/326681249): This should only fire if action is valid.
-    Event* interest_event = InterestEvent::Create(event_type_names::kInterest,
-                                                  interest_action, this);
+  if (Element* interest_target_element = this->interestTargetElement()) {
+    Event* interest_event =
+        InterestEvent::Create(event_type_names::kInterest, this);
     interest_target_element->DispatchEvent(*interest_event);
     if (!interest_event->defaultPrevented()) {
       if (auto* popover = DynamicTo<HTMLElement>(interest_target_element);
           popover && popover->PopoverType() != PopoverValueType::kNone) {
-        if (!(interest_action.empty() ||
-              EqualIgnoringASCIICase(interest_action,
-                                     keywords::kTogglePopover))) {
-          return;
-        }
-
         // TODO(crbug.com/326681249): This might need to queue a task with a
         // delay based on CSS properties.
-        auto& document = GetDocument();
-        bool can_show = popover->IsPopoverReady(
-            PopoverTriggerAction::kShow,
-            /*exception_state=*/nullptr,
-            /*include_event_handler_text=*/true, &document);
-        bool can_hide = popover->IsPopoverReady(
-            PopoverTriggerAction::kHide,
-            /*exception_state=*/nullptr,
-            /*include_event_handler_text=*/true, &document);
-        if (can_hide) {
+        if (popover->IsPopoverReady(PopoverTriggerAction::kShow,
+                                    /*exception_state=*/nullptr,
+                                    /*include_event_handler_text=*/true,
+                                    &GetDocument())) {
+          popover->InvokePopover(*this);
+        }
+      } else if (auto* dialog =
+                     DynamicTo<HTMLDialogElement>(interest_target_element)) {
+        if (!dialog->IsOpen()) {
+          dialog->showModal(ASSERT_NO_EXCEPTION);
+        }
+      }
+    }
+  }
+}
+
+void Element::InterestLost() {
+  CHECK(RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled());
+  CHECK(IsInTreeScope());
+
+  // TODO(masonf): It would be a good idea to add a DHECK method that makes sure
+  // we never get InterestLost without first getting an InterestGained.
+
+  if (Element* interest_target_element = this->interestTargetElement()) {
+    Event* lose_interest_event =
+        InterestEvent::Create(event_type_names::kLoseinterest, this);
+    interest_target_element->DispatchEvent(*lose_interest_event);
+    if (!lose_interest_event->defaultPrevented()) {
+      if (auto* popover = DynamicTo<HTMLElement>(interest_target_element);
+          popover && popover->PopoverType() != PopoverValueType::kNone) {
+        // TODO(crbug.com/326681249): This might need to queue a task with a
+        // delay based on CSS properties.
+        if (popover->IsPopoverReady(PopoverTriggerAction::kHide,
+                                    /*exception_state=*/nullptr,
+                                    /*include_event_handler_text=*/true,
+                                    &GetDocument())) {
           popover->HidePopoverInternal(
               HidePopoverFocusBehavior::kFocusPreviousElement,
               HidePopoverTransitionBehavior::kFireEventsAndWaitForTransitions,
               /*exception_state=*/nullptr);
-        } else if (can_show) {
-          popover->InvokePopover(*this);
+        }
+      } else if (auto* dialog =
+                     DynamicTo<HTMLDialogElement>(interest_target_element)) {
+        if (dialog->IsOpen()) {
+          dialog->close();
         }
       }
     }
@@ -6625,11 +6643,6 @@
 
   FocusStateChanged();
 
-  if (received &&
-      RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled()) {
-    InterestGained();
-  }
-
   if (GetLayoutObject() || received) {
     return;
   }
@@ -10205,8 +10218,13 @@
 
   InvalidateIfHasEffectiveAppearance();
 
-  if (hovered && RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled()) {
-    InterestGained();
+  if (RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled() &&
+      IsInTreeScope() && GetDocument().IsActive()) {
+    if (hovered) {
+      InterestGained();
+    } else {
+      InterestLost();
+    }
   }
 }
 
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 356ebee..026f1c33 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -1071,10 +1071,10 @@
     return false;
   }
 
+  // Implementation of the `interesttarget` feature.
   void InterestGained();
-
+  void InterestLost();
   virtual Element* interestTargetElement() { return nullptr; }
-  virtual AtomicString interestAction() const { return g_null_atom; }
 
   // The implementations of |innerText()| and |GetInnerTextWithoutUpdate()| are
   // found in "element_inner_text.cc".
diff --git a/third_party/blink/renderer/core/dom/interest_invoker_element.idl b/third_party/blink/renderer/core/dom/interest_invoker_element.idl
index 95f7549..e47885c 100644
--- a/third_party/blink/renderer/core/dom/interest_invoker_element.idl
+++ b/third_party/blink/renderer/core/dom/interest_invoker_element.idl
@@ -5,5 +5,4 @@
 [RuntimeEnabled=HTMLInterestTargetAttribute]
 interface mixin InterestInvokerElement {
   [CEReactions,Reflect=interesttarget] attribute Element? interestTargetElement;
-  [CEReactions,Reflect=interestaction] attribute DOMString interestAction;
 };
diff --git a/third_party/blink/renderer/core/editing/element_inner_text.cc b/third_party/blink/renderer/core/editing/element_inner_text.cc
index 3238fe2..abea002b89 100644
--- a/third_party/blink/renderer/core/editing/element_inner_text.cc
+++ b/third_party/blink/renderer/core/editing/element_inner_text.cc
@@ -92,6 +92,7 @@
   unsigned ProcessFirstLineAndGetOffset(const LayoutText& layout_text);
   void ProcessNode(const Node& node);
   void ProcessOptionElement(const HTMLOptionElement& element);
+  void ProcessOptGroupElement(const HTMLOptGroupElement& element);
   void ProcessSelectElement(const HTMLSelectElement& element);
   void ProcessTextNode(const Text& node);
 
@@ -427,8 +428,59 @@
   result_.EmitRequiredLineBreak(1);
 }
 
+void ElementInnerTextCollector::ProcessOptGroupElement(
+    const HTMLOptGroupElement& optgroup) {
+  CHECK(RuntimeEnabledFeatures::SelectParserRelaxationEnabled());
+  // Note: We should emit newline for OPTGROUP even if it has no OPTION.
+  // e.g. <div>a<select><optgroup></select>b</div>.innerText == "a\nb"
+  result_.EmitRequiredLineBreak(1);
+  Element* descendant = ElementTraversal::FirstChild(optgroup);
+  while (descendant) {
+    if (visitor_) {
+      visitor_->WillVisit(*descendant, result_.length());
+    }
+    // TODO(crbug.com/389573453): Consider handling <hr> elements here.
+    if (auto* option = DynamicTo<HTMLOptionElement>(descendant)) {
+      ProcessOptionElement(*option);
+      descendant =
+          ElementTraversal::NextSkippingChildren(*descendant, &optgroup);
+    } else if (IsA<HTMLOptGroupElement>(descendant)) {
+      // TODO(crbug.com/389573453): Consider adding nested <optgroup>s here. For
+      // now we will skip them.
+      descendant =
+          ElementTraversal::NextSkippingChildren(*descendant, &optgroup);
+    } else {
+      descendant = ElementTraversal::Next(*descendant, &optgroup);
+    }
+  }
+  result_.EmitRequiredLineBreak(1);
+}
+
 void ElementInnerTextCollector::ProcessSelectElement(
     const HTMLSelectElement& select_element) {
+  if (RuntimeEnabledFeatures::SelectParserRelaxationEnabled()) {
+    // TODO(crbug.com/40271842): Consider Handling display:none on various
+    // elements here, especially options.
+    Element* descendant = ElementTraversal::FirstChild(select_element);
+    while (descendant) {
+      if (visitor_) {
+        visitor_->WillVisit(*descendant, result_.length());
+      }
+      // TODO(crbug.com/389573453): Consider handling <hr> elements here.
+      if (auto* option = DynamicTo<HTMLOptionElement>(descendant)) {
+        ProcessOptionElement(*option);
+        descendant = ElementTraversal::NextSkippingChildren(*descendant,
+                                                            &select_element);
+      } else if (auto* optgroup = DynamicTo<HTMLOptGroupElement>(descendant)) {
+        ProcessOptGroupElement(*optgroup);
+        descendant = ElementTraversal::NextSkippingChildren(*descendant,
+                                                            &select_element);
+      } else {
+        descendant = ElementTraversal::Next(*descendant, &select_element);
+      }
+    }
+    return;
+  }
   for (const Node& child : NodeTraversal::ChildrenOf(select_element)) {
     if (visitor_) {
       visitor_->WillVisit(child, result_.length());
diff --git a/third_party/blink/renderer/core/editing/serializers/serialization.cc b/third_party/blink/renderer/core/editing/serializers/serialization.cc
index 19abbc7..736ad759 100644
--- a/third_party/blink/renderer/core/editing/serializers/serialization.cc
+++ b/third_party/blink/renderer/core/editing/serializers/serialization.cc
@@ -683,6 +683,9 @@
         markup, document, *fragment, *context_element, parser_content_policy,
         parser_behavior, &log_tag_stats);
     if (parsed_fast_path) {
+      fragment->SetHoldsUnnotifiedChildren(true);
+      fragment->ParserFinishedBuildingDocumentFragment(
+          DocumentFragment::ShouldNotifyInsertedNodes::kSkip);
       LogFastPathParserTotalTime(parse_timer.Elapsed());
 #if DCHECK_IS_ON()
       // As a sanity check for the fast-path, create another fragment using
diff --git a/third_party/blink/renderer/core/events/interest_event.cc b/third_party/blink/renderer/core/events/interest_event.cc
index b943932..8eec3c0 100644
--- a/third_party/blink/renderer/core/events/interest_event.cc
+++ b/third_party/blink/renderer/core/events/interest_event.cc
@@ -16,39 +16,33 @@
                              const InterestEventInit* initializer)
     : Event(type, initializer) {
   DCHECK(RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled());
-  if (initializer->hasInvoker()) {
-    invoker_ = initializer->invoker();
-  }
-  if (initializer->hasAction()) {
-    action_ = initializer->action();
+  if (initializer->hasSource()) {
+    source_ = initializer->source();
   }
 }
 
-InterestEvent::InterestEvent(const AtomicString& type,
-                             const String& action,
-                             Element* invoker)
+InterestEvent::InterestEvent(const AtomicString& type, Element* source)
     : Event(type, Bubbles::kNo, Cancelable::kYes, ComposedMode::kComposed),
-      invoker_(invoker),
-      action_(action) {
+      source_(source) {
   DCHECK(RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled());
 }
 
-Element* InterestEvent::invoker() const {
-  Element* invoker = invoker_.Get();
-  if (!invoker) {
+Element* InterestEvent::source() const {
+  Element* source = source_.Get();
+  if (!source) {
     return nullptr;
   }
 
   if (auto* current = currentTarget()) {
     CHECK(current->ToNode());
-    return &current->ToNode()->GetTreeScope().Retarget(*invoker);
+    return &current->ToNode()->GetTreeScope().Retarget(*source);
   }
   DCHECK_EQ(eventPhase(), Event::PhaseType::kNone);
-  return invoker;
+  return source;
 }
 
 void InterestEvent::Trace(Visitor* visitor) const {
-  visitor->Trace(invoker_);
+  visitor->Trace(source_);
   Event::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/events/interest_event.h b/third_party/blink/renderer/core/events/interest_event.h
index fed5bd7..6db27e7 100644
--- a/third_party/blink/renderer/core/events/interest_event.h
+++ b/third_party/blink/renderer/core/events/interest_event.h
@@ -23,16 +23,12 @@
     return MakeGarbageCollected<InterestEvent>(type, initializer);
   }
 
-  static InterestEvent* Create(const AtomicString& type,
-                               const String& action,
-                               Element* invoker) {
-    return MakeGarbageCollected<InterestEvent>(type, action, invoker);
+  static InterestEvent* Create(const AtomicString& type, Element* source) {
+    return MakeGarbageCollected<InterestEvent>(type, source);
   }
 
   InterestEvent(const AtomicString& type, const InterestEventInit* initializer);
-  InterestEvent(const AtomicString& type,
-                const String& action,
-                Element* invoker);
+  InterestEvent(const AtomicString& type, Element* source);
 
   const AtomicString& InterfaceName() const override {
     return event_interface_names::kInterestEvent;
@@ -40,14 +36,11 @@
 
   void Trace(Visitor*) const override;
 
-  const String& action() const { return action_; }
-
-  Element* invoker() const;
-  void SetInvoker(Element* invoker) { invoker_ = invoker; }
+  Element* source() const;
+  void SetSource(Element* source) { source_ = source; }
 
  private:
-  Member<Element> invoker_;
-  String action_;
+  Member<Element> source_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/events/interest_event.idl b/third_party/blink/renderer/core/events/interest_event.idl
index 4b71a42..2717627 100644
--- a/third_party/blink/renderer/core/events/interest_event.idl
+++ b/third_party/blink/renderer/core/events/interest_event.idl
@@ -7,11 +7,9 @@
     Exposed=Window
 ] interface InterestEvent : Event {
     constructor(DOMString type, optional InterestEventInit eventInitDict = {});
-    readonly attribute Element? invoker;
-    readonly attribute DOMString action;
+    readonly attribute Element? source;
 };
 
 dictionary InterestEventInit : EventInit {
-    Element? invoker = null;
-    DOMString action = "";
+    Element? source = null;
 };
diff --git a/third_party/blink/renderer/core/frame/deprecation/PRESUBMIT.py b/third_party/blink/renderer/core/frame/deprecation/PRESUBMIT.py
index 60a1350..c7d43f4 100644
--- a/third_party/blink/renderer/core/frame/deprecation/PRESUBMIT.py
+++ b/third_party/blink/renderer/core/frame/deprecation/PRESUBMIT.py
@@ -17,8 +17,6 @@
 # add more formal support.
 EXEMPTED_FROM_RENDERER_GENERATION = {
     "PrivacySandboxExtensionsAPI": True,
-    "ThirdPartyCookieAccessWarning": True,
-    "ThirdPartyCookieAccessError": True,
 }
 
 # pyright: reportMissingImports=false
diff --git a/third_party/blink/renderer/core/frame/deprecation/deprecation.json5 b/third_party/blink/renderer/core/frame/deprecation/deprecation.json5
index 6e9ba4c..3b59d1a 100644
--- a/third_party/blink/renderer/core/frame/deprecation/deprecation.json5
+++ b/third_party/blink/renderer/core/frame/deprecation/deprecation.json5
@@ -557,24 +557,6 @@
       milestone: 71,
     },
     {
-      name: "ThirdPartyCookieAccessError",
-      message: "Cookies marked with `SameSite=None; Secure` and not `Partitioned` are blocked in cross-site contexts.",
-      translation_note: "This error occurs when the website attempts to access third-party cookies and the browser blocks them.",
-      web_features: [
-        // This deprecation is used for Legacy Technology Report generation only.
-      ],
-      chrome_status_feature: 5133113939722240,
-    },
-    {
-      name: "ThirdPartyCookieAccessWarning",
-      message: "In a future version of the browser, cookies marked with `SameSite=None; Secure` and not `Partitioned` will be blocked in cross-site context.",
-      translation_note: "This warning occurs when the website attempts to access third-party cookies but the browser does not block them yet.",
-      web_features: [
-        // This deprecation is used for Legacy Technology Report generation only.
-      ],
-      chrome_status_feature: 5133113939722240,
-    },
-    {
       name: "UnloadHandler",
       message: "Unload event listeners are deprecated and will be removed.",
       translation_note: "A deprecation warning shown in the DevTools Issues tab. It's shown when a listener for the `unload` event is added.",
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_image_source.h b/third_party/blink/renderer/core/html/canvas/canvas_image_source.h
index d40a6cd697..bab9631 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_image_source.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_image_source.h
@@ -29,7 +29,6 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "third_party/blink/renderer/core/core_export.h"
-#include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
 #include "third_party/blink/renderer/platform/graphics/image_orientation.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
index 22ee4d2..a755f48 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.cc
@@ -417,17 +417,6 @@
   setAttribute(html_names::kPopovertargetactionAttr, value);
 }
 
-AtomicString HTMLFormControlElement::interestAction() const {
-  CHECK(RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled());
-  const AtomicString& attribute_value =
-      FastGetAttribute(html_names::kInterestactionAttr);
-  if (attribute_value && !attribute_value.IsNull() &&
-      !attribute_value.empty()) {
-    return attribute_value;
-  }
-  return g_empty_atom;
-}
-
 void HTMLFormControlElement::DefaultEventHandler(Event& event) {
   // Buttons that aren't form participants might be Invoker buttons or Popover
   // buttons.
diff --git a/third_party/blink/renderer/core/html/forms/html_form_control_element.h b/third_party/blink/renderer/core/html/forms/html_form_control_element.h
index 2352765..e26d943f 100644
--- a/third_party/blink/renderer/core/html/forms/html_form_control_element.h
+++ b/third_party/blink/renderer/core/html/forms/html_form_control_element.h
@@ -117,8 +117,6 @@
 
   Element* interestTargetElement() override;
 
-  AtomicString interestAction() const override;
-
   void DefaultEventHandler(Event&) override;
 
   void SetHovered(bool hovered) override;
diff --git a/third_party/blink/renderer/core/html/forms/html_opt_group_element.cc b/third_party/blink/renderer/core/html/forms/html_opt_group_element.cc
index 5e01b04..5e4beeac 100644
--- a/third_party/blink/renderer/core/html/forms/html_opt_group_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_opt_group_element.cc
@@ -50,6 +50,10 @@
          node.HasTagName(html_names::kHrTag);
 }
 
+HTMLLegendElement* FirstChildLegend(const HTMLOptGroupElement& optgroup) {
+  return Traversal<HTMLLegendElement>::FirstChild(optgroup);
+}
+
 }  // namespace
 
 HTMLOptGroupElement::HTMLOptGroupElement(Document& document)
@@ -102,15 +106,24 @@
   DCHECK_NE(change.type,
             ChildrenChangeType::kFinishedBuildingDocumentFragmentTree);
   if (change.type == ChildrenChangeType::kElementInserted) {
-    if (auto* option = DynamicTo<HTMLOptionElement>(change.sibling_changed))
+    if (auto* option = DynamicTo<HTMLOptionElement>(change.sibling_changed)) {
       select->OptionInserted(*option, option->Selected());
+    } else if (IsA<HTMLLegendElement>(change.sibling_changed)) {
+      UpdateGroupLabel();
+    }
   } else if (change.type == ChildrenChangeType::kElementRemoved) {
-    if (auto* option = DynamicTo<HTMLOptionElement>(change.sibling_changed))
+    if (auto* option = DynamicTo<HTMLOptionElement>(change.sibling_changed)) {
       select->OptionRemoved(*option);
+    } else if (IsA<HTMLLegendElement>(change.sibling_changed)) {
+      UpdateGroupLabel();
+    }
   } else if (change.type == ChildrenChangeType::kAllChildrenRemoved) {
     for (Node* node : change.removed_nodes) {
-      if (auto* option = DynamicTo<HTMLOptionElement>(node))
+      if (auto* option = DynamicTo<HTMLOptionElement>(node)) {
         select->OptionRemoved(*option);
+      } else if (IsA<HTMLLegendElement>(change.sibling_changed)) {
+        UpdateGroupLabel();
+      }
     }
   }
 }
@@ -150,10 +163,8 @@
   String label_attribute_text = LabelAttributeText();
   if (RuntimeEnabledFeatures::CustomizableSelectEnabled() &&
       label_attribute_text.ContainsOnlyWhitespaceOrEmpty()) {
-    for (auto& node : NodeTraversal::DescendantsOf(*this)) {
-      if (auto* legend = DynamicTo<HTMLLegendElement>(node)) {
-        return legend->textContent();
-      }
+    if (auto* legend = FirstChildLegend(*this)) {
+      return legend->textContent();
     }
   }
   return label_attribute_text;
@@ -233,7 +244,7 @@
   HTMLDivElement& label = OptGroupLabelElement();
   label.setTextContent(label_text);
   label.setAttribute(html_names::kAriaLabelAttr, AtomicString(label_text));
-  if (label_text.ContainsOnlyWhitespaceOrEmpty()) {
+  if (label_text.ContainsOnlyWhitespaceOrEmpty() || FirstChildLegend(*this)) {
     if (customizable_select_rendering_) {
       // If the author uses <legend> to label the <optgroup> instead of the
       // label attribute, then we don't want extra space being taken up for the
diff --git a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
index 27043496..02ca1a7 100644
--- a/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
+++ b/third_party/blink/renderer/core/html/forms/internal_popup_menu.cc
@@ -385,6 +385,7 @@
   const HeapVector<Member<HTMLElement>>& items = owner_element.GetListItems();
   for (; context.list_index_ < items.size(); ++context.list_index_) {
     Element& child = *items[context.list_index_];
+    // TODO this shouldn't just look at parentNode right??
     if (!IsA<HTMLOptGroupElement>(child.parentNode()))
       context.FinishGroupIfNecessary();
     if (auto* option = DynamicTo<HTMLOptionElement>(child))
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.cc b/third_party/blink/renderer/core/html/html_anchor_element.cc
index 2a34e87..e1440edf 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.cc
+++ b/third_party/blink/renderer/core/html/html_anchor_element.cc
@@ -629,17 +629,6 @@
       html_names::kInteresttargetAttr);
 }
 
-AtomicString HTMLAnchorElementBase::interestAction() const {
-  CHECK(RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled());
-  const AtomicString& attribute_value =
-      FastGetAttribute(html_names::kInterestactionAttr);
-  if (attribute_value && !attribute_value.IsNull() &&
-      !attribute_value.empty()) {
-    return attribute_value;
-  }
-  return g_empty_atom;
-}
-
 void HTMLAnchorElementBase::HandleClick(MouseEvent& event) {
   event.SetDefaultHandled();
 
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.h b/third_party/blink/renderer/core/html/html_anchor_element.h
index ae3a23b..f9dcb765 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.h
+++ b/third_party/blink/renderer/core/html/html_anchor_element.h
@@ -114,8 +114,6 @@
 
   Element* interestTargetElement() override;
 
-  AtomicString interestAction() const override;
-
   void Trace(Visitor*) const override;
 
  protected:
diff --git a/third_party/blink/renderer/core/html/html_area_element.cc b/third_party/blink/renderer/core/html/html_area_element.cc
index 699ddc0..0602e0e4 100644
--- a/third_party/blink/renderer/core/html/html_area_element.cc
+++ b/third_party/blink/renderer/core/html/html_area_element.cc
@@ -239,17 +239,6 @@
       html_names::kInteresttargetAttr);
 }
 
-AtomicString HTMLAreaElement::interestAction() const {
-  CHECK(RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled());
-  const AtomicString& attribute_value =
-      FastGetAttribute(html_names::kInterestactionAttr);
-  if (attribute_value && !attribute_value.IsNull() &&
-      !attribute_value.empty()) {
-    return attribute_value;
-  }
-  return g_empty_atom;
-}
-
 void HTMLAreaElement::UpdateSelectionOnFocus(
     SelectionBehaviorOnFocus selection_behavior,
     const FocusOptions* options) {
diff --git a/third_party/blink/renderer/core/html/html_area_element.h b/third_party/blink/renderer/core/html/html_area_element.h
index c99edcb8..7622ae2 100644
--- a/third_party/blink/renderer/core/html/html_area_element.h
+++ b/third_party/blink/renderer/core/html/html_area_element.h
@@ -72,7 +72,6 @@
   void SetFocused(bool, mojom::blink::FocusType) override;
 
   Element* interestTargetElement() override;
-  AtomicString interestAction() const override;
 
   enum Shape { kDefault, kPoly, kRect, kCircle };
   void InvalidateCachedPath();
diff --git a/third_party/blink/renderer/core/html/html_attribute_names.json5 b/third_party/blink/renderer/core/html/html_attribute_names.json5
index 868d155..ccb30fd 100644
--- a/third_party/blink/renderer/core/html/html_attribute_names.json5
+++ b/third_party/blink/renderer/core/html/html_attribute_names.json5
@@ -118,7 +118,6 @@
     "inert",
     "inputmode",
     "integrity",
-    "interestaction",
     "interesttarget",
     "is",
     "ismap",
diff --git a/third_party/blink/renderer/core/html/html_frame_owner_element.cc b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
index 344cb61..5a708bb 100644
--- a/third_party/blink/renderer/core/html/html_frame_owner_element.cc
+++ b/third_party/blink/renderer/core/html/html_frame_owner_element.cc
@@ -201,11 +201,7 @@
   // perform some bookkeeping that ordinarily would only be done deeper in the
   // frame setup logic that gets triggered in the *NON* state-preserving atomic
   // move flow.
-  if (GetDocument().StatePreservingAtomicMoveInProgress()) {
-    // State-preserving atomic moves can only be in-progress when all elements
-    // are connected, and when `HTMLFrameOwnerElement` is connected, it must
-    // have a non-null `ContentFrame()`.
-    CHECK(ContentFrame());
+  if (GetDocument().StatePreservingAtomicMoveInProgress() && ContentFrame()) {
     // During a state-preserving atomic move, we must specifically inform all of
     // `this`'s ancestor nodes of the new connected frame they are adopting.
     //
@@ -231,7 +227,8 @@
   // Not doing (1) is a good thing, since we're trying to preserve the frame,
   // but we still have to do (2) manually to maintain bookkeeping consistency
   // among the ancestor nodes.
-  if (GetDocument().StatePreservingAtomicMoveInProgress()) {
+  if (GetDocument().StatePreservingAtomicMoveInProgress() &&
+      insertion_point.isConnected()) {
     // `this` is no longer connected, so we have to decrement our subframe count
     // separately from our old ancestors's subframe count (i.e.,
     // `insertion_point`).
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser_fastpath.cc b/third_party/blink/renderer/core/html/parser/html_document_parser_fastpath.cc
index 508002b..fab0b60 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser_fastpath.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser_fastpath.cc
@@ -1716,7 +1716,6 @@
   number_of_bytes_parsed = parser.NumberOfBytesParsed();
   // The time needed to parse is typically < 1ms (even at the 99%).
   if (success) {
-    root_node.ParserFinishedBuildingDocumentFragment();
     UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
         "Blink.HTMLFastPathParser.SuccessfulParseTime2", parse_timer.Elapsed(),
         base::Microseconds(1), base::Milliseconds(10), 100);
diff --git a/third_party/blink/renderer/core/layout/block_node.cc b/third_party/blink/renderer/core/layout/block_node.cc
index c1293752..2f8b8bc 100644
--- a/third_party/blink/renderer/core/layout/block_node.cc
+++ b/third_party/blink/renderer/core/layout/block_node.cc
@@ -169,7 +169,6 @@
 template <typename Callback>
 NOINLINE void DetermineAlgorithmAndRun(const LayoutAlgorithmParams& params,
                                        const Callback& callback) {
-  const ComputedStyle& style = params.node.Style();
   const LayoutBox& box = *params.node.GetLayoutBox();
   if (box.IsFlexibleBox()) {
     CreateAlgorithmAndRun<FlexLayoutAlgorithm>(params, callback);
@@ -198,7 +197,7 @@
   // we would have done block fragmentation with the legacy engine.
   // Otherwise writing data back into the legacy tree will fail. Look for
   // the flow thread.
-  else if (GetFlowThread(box) && style.SpecifiesColumns()) {
+  else if (GetFlowThread(box) && params.node.Style().SpecifiesColumns()) {
     CreateAlgorithmAndRun<ColumnLayoutAlgorithm>(params, callback);
   } else if (!box.Parent() && params.node.IsPaginatedRoot()) [[unlikely]] {
     CreateAlgorithmAndRun<PaginatedRootLayoutAlgorithm>(params, callback);
@@ -1359,15 +1358,6 @@
     PlaceChildrenInLayoutBox(child_fragment, previous_column_break_token,
                              /* needs_invalidation_check */ true);
 
-    // If the multicol container has inline children, there may still be floats
-    // there, but they aren't stored as child fragments of |column| in that case
-    // (but rather inside fragment items). Make sure that they get positioned,
-    // too.
-    if (const FragmentItems* items = child_fragment.Items()) {
-      CopyFragmentItemsToLayoutBox(child_fragment, *items,
-                                   previous_column_break_token);
-    }
-
     previous_column_break_token = child_fragment.GetBreakToken();
   }
 
diff --git a/third_party/blink/renderer/core/layout/build.gni b/third_party/blink/renderer/core/layout/build.gni
index 1033f64..b35e22d 100644
--- a/third_party/blink/renderer/core/layout/build.gni
+++ b/third_party/blink/renderer/core/layout/build.gni
@@ -199,6 +199,8 @@
   "grid/grid_subtree.h",
   "grid/grid_track_collection.cc",
   "grid/grid_track_collection.h",
+  "grid/grid_track_sizing_algorithm.cc",
+  "grid/grid_track_sizing_algorithm.h",
   "grid/layout_grid.cc",
   "grid/layout_grid.h",
   "grid/subgrid_min_max_sizes_cache.h",
diff --git a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc
index 84bdbb0..8b0d3e5b 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/grid/grid_layout_algorithm.cc
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/core/layout/fragmentation_utils.h"
 #include "third_party/blink/renderer/core/layout/grid/grid_break_token_data.h"
 #include "third_party/blink/renderer/core/layout/grid/grid_item.h"
+#include "third_party/blink/renderer/core/layout/grid/grid_track_sizing_algorithm.h"
 #include "third_party/blink/renderer/core/layout/length_utils.h"
 #include "third_party/blink/renderer/core/layout/logical_box_fragment.h"
 #include "third_party/blink/renderer/core/layout/relative_utils.h"
@@ -85,111 +86,6 @@
 
 namespace {
 
-void CacheGridItemsProperties(const GridLayoutTrackCollection& track_collection,
-                              GridItems* grid_items) {
-  DCHECK(grid_items);
-
-  GridItemDataPtrVector grid_items_spanning_multiple_ranges;
-  const auto track_direction = track_collection.Direction();
-
-  for (auto& grid_item : grid_items->IncludeSubgriddedItems()) {
-    if (!grid_item.MustCachePlacementIndices(track_direction)) {
-      continue;
-    }
-
-    const auto& range_indices = grid_item.RangeIndices(track_direction);
-    auto& track_span_properties = (track_direction == kForColumns)
-                                      ? grid_item.column_span_properties
-                                      : grid_item.row_span_properties;
-
-    grid_item.ComputeSetIndices(track_collection);
-    track_span_properties.Reset();
-
-    // If a grid item spans only one range, then we can just cache the track
-    // span properties directly. On the contrary, if a grid item spans multiple
-    // tracks, it is added to |grid_items_spanning_multiple_ranges| as we need
-    // to do more work to cache its track span properties.
-    //
-    // TODO(layout-dev): Investigate applying this concept to spans > 1.
-    if (range_indices.begin == range_indices.end) {
-      track_span_properties =
-          track_collection.RangeProperties(range_indices.begin);
-    } else {
-      grid_items_spanning_multiple_ranges.emplace_back(&grid_item);
-    }
-  }
-
-  if (grid_items_spanning_multiple_ranges.empty())
-    return;
-
-  auto CompareGridItemsByStartLine =
-      [track_direction](GridItemData* lhs, GridItemData* rhs) -> bool {
-    return lhs->StartLine(track_direction) < rhs->StartLine(track_direction);
-  };
-  std::sort(grid_items_spanning_multiple_ranges.begin(),
-            grid_items_spanning_multiple_ranges.end(),
-            CompareGridItemsByStartLine);
-
-  auto CacheGridItemsSpanningMultipleRangesProperty =
-      [&](TrackSpanProperties::PropertyId property) {
-        // At this point we have the remaining grid items sorted by start line
-        // in the respective direction; this is important since we'll process
-        // both, the ranges in the track collection and the grid items,
-        // incrementally.
-        wtf_size_t current_range_index = 0;
-        const wtf_size_t range_count = track_collection.RangeCount();
-
-        for (auto* grid_item : grid_items_spanning_multiple_ranges) {
-          // We want to find the first range in the collection that:
-          //   - Spans tracks located AFTER the start line of the current grid
-          //   item; this can be done by checking that the last track number of
-          //   the current range is NOT less than the current grid item's start
-          //   line. Furthermore, since grid items are sorted by start line, if
-          //   at any point a range is located BEFORE the current grid item's
-          //   start line, the same range will also be located BEFORE any
-          //   subsequent item's start line.
-          //   - Contains a track that fulfills the specified property.
-          while (current_range_index < range_count &&
-                 (track_collection.RangeEndLine(current_range_index) <=
-                      grid_item->StartLine(track_direction) ||
-                  !track_collection.RangeProperties(current_range_index)
-                       .HasProperty(property))) {
-            ++current_range_index;
-          }
-
-          // Since we discarded every range in the track collection, any
-          // following grid item cannot fulfill the property.
-          if (current_range_index == range_count)
-            break;
-
-          // Notice that, from the way we build the ranges of a track collection
-          // (see |GridRangeBuilder::EnsureTrackCoverage|), any given range
-          // must either be completely contained or excluded from a grid item's
-          // span. Thus, if the current range's last track is also located
-          // BEFORE the item's end line, then this range, including a track that
-          // fulfills the specified property, is completely contained within
-          // this item's boundaries. Otherwise, this and every subsequent range
-          // are excluded from the grid item's span, meaning that such item
-          // cannot satisfy the property we are looking for.
-          if (track_collection.RangeEndLine(current_range_index) <=
-              grid_item->EndLine(track_direction)) {
-            grid_item->SetTrackSpanProperty(property, track_direction);
-          }
-        }
-      };
-
-  CacheGridItemsSpanningMultipleRangesProperty(
-      TrackSpanProperties::kHasFlexibleTrack);
-  CacheGridItemsSpanningMultipleRangesProperty(
-      TrackSpanProperties::kHasIntrinsicTrack);
-  CacheGridItemsSpanningMultipleRangesProperty(
-      TrackSpanProperties::kHasAutoMinimumTrack);
-  CacheGridItemsSpanningMultipleRangesProperty(
-      TrackSpanProperties::kHasFixedMinimumTrack);
-  CacheGridItemsSpanningMultipleRangesProperty(
-      TrackSpanProperties::kHasFixedMaximumTrack);
-}
-
 bool HasBlockSizeDependentGridItem(const GridItems& grid_items) {
   for (const auto& grid_item : grid_items.IncludeSubgriddedItems()) {
     if (grid_item.is_sizing_dependent_on_block_size)
@@ -733,132 +629,6 @@
              : track_collection.MinorBaseline(end_set_index - 1);
 }
 
-namespace {
-
-struct FirstSetGeometry {
-  LayoutUnit start_offset;
-  LayoutUnit gutter_size;
-};
-
-FirstSetGeometry ComputeFirstSetGeometry(
-    const GridSizingTrackCollection& track_collection,
-    const ComputedStyle& container_style,
-    LayoutUnit available_size,
-    LayoutUnit start_border_scrollbar_padding) {
-  const bool is_for_columns = track_collection.Direction() == kForColumns;
-
-  const auto& content_alignment = is_for_columns
-                                      ? container_style.JustifyContent()
-                                      : container_style.AlignContent();
-  const auto overflow = content_alignment.Overflow();
-
-  // Determining the free-space is typically unnecessary, i.e. if there is
-  // default alignment. Only compute this on-demand.
-  auto FreeSpace = [&]() -> LayoutUnit {
-    LayoutUnit free_space = available_size - track_collection.TotalTrackSize();
-
-    // If overflow is 'safe', make sure we don't overflow the 'start' edge
-    // (potentially causing some data loss as the overflow is unreachable).
-    return (overflow == OverflowAlignment::kSafe)
-               ? free_space.ClampNegativeToZero()
-               : free_space;
-  };
-
-  // The default alignment, perform adjustments on top of this.
-  FirstSetGeometry geometry{start_border_scrollbar_padding,
-                            track_collection.GutterSize()};
-
-  // If we have an indefinite |available_size| we can't perform any alignment,
-  // just return the default alignment.
-  if (available_size == kIndefiniteSize)
-    return geometry;
-
-  // TODO(ikilpatrick): 'space-between', 'space-around', and 'space-evenly' all
-  // divide by the free-space, and may have a non-zero modulo. Investigate if
-  // this should be distributed between the tracks.
-  switch (content_alignment.Distribution()) {
-    case ContentDistributionType::kSpaceBetween: {
-      // Default behavior for 'space-between' is to start align content.
-      const wtf_size_t track_count = track_collection.NonCollapsedTrackCount();
-      const LayoutUnit free_space = FreeSpace();
-      if (track_count < 2 || free_space < LayoutUnit())
-        return geometry;
-
-      geometry.gutter_size += free_space / (track_count - 1);
-      return geometry;
-    }
-    case ContentDistributionType::kSpaceAround: {
-      // Default behavior for 'space-around' is to safe center content.
-      const wtf_size_t track_count = track_collection.NonCollapsedTrackCount();
-      const LayoutUnit free_space = FreeSpace();
-      if (free_space < LayoutUnit()) {
-        return geometry;
-      }
-      if (track_count < 1) {
-        geometry.start_offset += free_space / 2;
-        return geometry;
-      }
-
-      LayoutUnit track_space = free_space / track_count;
-      geometry.start_offset += track_space / 2;
-      geometry.gutter_size += track_space;
-      return geometry;
-    }
-    case ContentDistributionType::kSpaceEvenly: {
-      // Default behavior for 'space-evenly' is to safe center content.
-      const wtf_size_t track_count = track_collection.NonCollapsedTrackCount();
-      const LayoutUnit free_space = FreeSpace();
-      if (free_space < LayoutUnit()) {
-        return geometry;
-      }
-
-      LayoutUnit track_space = free_space / (track_count + 1);
-      geometry.start_offset += track_space;
-      geometry.gutter_size += track_space;
-      return geometry;
-    }
-    case ContentDistributionType::kStretch:
-    case ContentDistributionType::kDefault:
-      break;
-  }
-
-  switch (content_alignment.GetPosition()) {
-    case ContentPosition::kLeft: {
-      DCHECK(is_for_columns);
-      if (IsLtr(container_style.Direction()))
-        return geometry;
-
-      geometry.start_offset += FreeSpace();
-      return geometry;
-    }
-    case ContentPosition::kRight: {
-      DCHECK(is_for_columns);
-      if (IsRtl(container_style.Direction()))
-        return geometry;
-
-      geometry.start_offset += FreeSpace();
-      return geometry;
-    }
-    case ContentPosition::kCenter: {
-      geometry.start_offset += FreeSpace() / 2;
-      return geometry;
-    }
-    case ContentPosition::kEnd:
-    case ContentPosition::kFlexEnd: {
-      geometry.start_offset += FreeSpace();
-      return geometry;
-    }
-    case ContentPosition::kStart:
-    case ContentPosition::kFlexStart:
-    case ContentPosition::kNormal:
-    case ContentPosition::kBaseline:
-    case ContentPosition::kLastBaseline:
-      return geometry;
-  }
-}
-
-}  // namespace
-
 void GridLayoutAlgorithm::ComputeGridGeometry(
     const GridSizingTree& grid_sizing_tree,
     LayoutUnit* intrinsic_block_size) {
@@ -959,9 +729,10 @@
 
       // Re-compute the row geometry now that we resolved the available block
       // size. "align-content: space-evenly", etc, require the resolved size.
-      auto first_set_geometry = ComputeFirstSetGeometry(
-          track_collection, container_style, grid_available_size_.block_size,
-          border_scrollbar_padding.block_start);
+      auto first_set_geometry =
+          GridTrackSizingAlgorithm::ComputeFirstSetGeometry(
+              track_collection, container_style, grid_available_size_,
+              border_scrollbar_padding);
 
       track_collection.FinalizeSetsGeometry(first_set_geometry.start_offset,
                                             first_set_geometry.gutter_size);
@@ -1743,27 +1514,24 @@
       }
     } else {
       auto& track_collection = layout_data.SizingCollection(track_direction);
-      CacheGridItemsProperties(track_collection, &grid_items);
-
-      const bool is_for_columns = track_direction == kForColumns;
-      const auto start_border_scrollbar_padding =
-          is_for_columns ? BorderScrollbarPadding().inline_start
-                         : BorderScrollbarPadding().block_start;
+      GridTrackSizingAlgorithm::CacheGridItemsProperties(track_collection,
+                                                         &grid_items);
 
       // If all tracks have a definite size upfront, we can use the current set
       // sizes as the used track sizes (applying alignment, if present).
       if (!track_collection.HasNonDefiniteTrack()) {
-        auto first_set_geometry = ComputeFirstSetGeometry(
-            track_collection, Style(),
-            is_for_columns ? grid_available_size_.inline_size
-                           : grid_available_size_.block_size,
-            start_border_scrollbar_padding);
+        auto first_set_geometry =
+            GridTrackSizingAlgorithm::ComputeFirstSetGeometry(
+                track_collection, Style(), grid_available_size_,
+                BorderScrollbarPadding());
 
         track_collection.FinalizeSetsGeometry(first_set_geometry.start_offset,
                                               first_set_geometry.gutter_size);
       } else {
         track_collection.CacheInitializedSetsGeometry(
-            start_border_scrollbar_padding);
+            (track_direction == kForColumns)
+                ? BorderScrollbarPadding().inline_start
+                : BorderScrollbarPadding().block_start);
       }
 
       if (track_collection.HasBaselines()) {
@@ -1935,12 +1703,10 @@
             sizing_subtree.GetGridItems(), track_collection);
       }
 
-      auto first_set_geometry = ComputeFirstSetGeometry(
-          track_collection, Style(),
-          is_for_columns ? grid_available_size_.inline_size
-                         : grid_available_size_.block_size,
-          is_for_columns ? BorderScrollbarPadding().inline_start
-                         : BorderScrollbarPadding().block_start);
+      auto first_set_geometry =
+          GridTrackSizingAlgorithm::ComputeFirstSetGeometry(
+              track_collection, Style(), grid_available_size_,
+              BorderScrollbarPadding());
 
       track_collection.FinalizeSetsGeometry(first_set_geometry.start_offset,
                                             first_set_geometry.gutter_size);
diff --git a/third_party/blink/renderer/core/layout/grid/grid_track_sizing_algorithm.cc b/third_party/blink/renderer/core/layout/grid/grid_track_sizing_algorithm.cc
new file mode 100644
index 0000000..f702994
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/grid/grid_track_sizing_algorithm.cc
@@ -0,0 +1,255 @@
+// Copyright 2025 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/layout/grid/grid_track_sizing_algorithm.h"
+
+#include "third_party/blink/renderer/core/layout/geometry/box_strut.h"
+#include "third_party/blink/renderer/core/layout/grid/grid_item.h"
+#include "third_party/blink/renderer/core/layout/grid/grid_track_collection.h"
+
+namespace blink {
+
+namespace {
+
+using GridItemDataPtrVector = Vector<GridItemData*, 16>;
+
+}  // namespace
+
+// static
+void GridTrackSizingAlgorithm::CacheGridItemsProperties(
+    const GridSizingTrackCollection& track_collection,
+    GridItems* grid_items) {
+  DCHECK(grid_items);
+
+  GridItemDataPtrVector grid_items_spanning_multiple_ranges;
+  const auto track_direction = track_collection.Direction();
+
+  for (auto& grid_item : grid_items->IncludeSubgriddedItems()) {
+    if (!grid_item.MustCachePlacementIndices(track_direction)) {
+      continue;
+    }
+
+    const auto& range_indices = grid_item.RangeIndices(track_direction);
+    auto& track_span_properties = (track_direction == kForColumns)
+                                      ? grid_item.column_span_properties
+                                      : grid_item.row_span_properties;
+
+    grid_item.ComputeSetIndices(track_collection);
+    track_span_properties.Reset();
+
+    // If a grid item spans only one range, then we can just cache the track
+    // span properties directly. On the contrary, if a grid item spans multiple
+    // tracks, it is added to `grid_items_spanning_multiple_ranges` as we need
+    // to do more work to cache its track span properties.
+    //
+    // TODO(layout-dev): Investigate applying this concept to spans > 1.
+    if (range_indices.begin == range_indices.end) {
+      track_span_properties =
+          track_collection.RangeProperties(range_indices.begin);
+    } else {
+      grid_items_spanning_multiple_ranges.emplace_back(&grid_item);
+    }
+  }
+
+  if (grid_items_spanning_multiple_ranges.empty()) {
+    return;
+  }
+
+  auto CompareGridItemsByStartLine =
+      [track_direction](GridItemData* lhs, GridItemData* rhs) -> bool {
+    return lhs->StartLine(track_direction) < rhs->StartLine(track_direction);
+  };
+  std::sort(grid_items_spanning_multiple_ranges.begin(),
+            grid_items_spanning_multiple_ranges.end(),
+            CompareGridItemsByStartLine);
+
+  auto CacheGridItemsSpanningMultipleRangesProperty =
+      [&](TrackSpanProperties::PropertyId property) {
+        // At this point we have the remaining grid items sorted by start line
+        // in the respective direction; this is important since we'll process
+        // both, the ranges in the track collection and the grid items,
+        // incrementally.
+        wtf_size_t current_range_index = 0;
+        const wtf_size_t range_count = track_collection.RangeCount();
+
+        for (auto* grid_item : grid_items_spanning_multiple_ranges) {
+          // We want to find the first range in the collection that:
+          //   - Spans tracks located AFTER the start line of the current grid
+          //   item; this can be done by checking that the last track number of
+          //   the current range is NOT less than the current grid item's start
+          //   line. Furthermore, since grid items are sorted by start line, if
+          //   at any point a range is located BEFORE the current grid item's
+          //   start line, the same range will also be located BEFORE any
+          //   subsequent item's start line.
+          //   - Contains a track that fulfills the specified property.
+          while (current_range_index < range_count &&
+                 (track_collection.RangeEndLine(current_range_index) <=
+                      grid_item->StartLine(track_direction) ||
+                  !track_collection.RangeProperties(current_range_index)
+                       .HasProperty(property))) {
+            ++current_range_index;
+          }
+
+          // Since we discarded every range in the track collection, any
+          // following grid item cannot fulfill the property.
+          if (current_range_index == range_count) {
+            break;
+          }
+
+          // Notice that, from the way we build the ranges of a track collection
+          // (see `GridRangeBuilder::EnsureTrackCoverage`), any given range
+          // must either be completely contained or excluded from a grid item's
+          // span. Thus, if the current range's last track is also located
+          // BEFORE the item's end line, then this range, including a track that
+          // fulfills the specified property, is completely contained within
+          // this item's boundaries. Otherwise, this and every subsequent range
+          // are excluded from the grid item's span, meaning that such item
+          // cannot satisfy the property we are looking for.
+          if (track_collection.RangeEndLine(current_range_index) <=
+              grid_item->EndLine(track_direction)) {
+            grid_item->SetTrackSpanProperty(property, track_direction);
+          }
+        }
+      };
+
+  CacheGridItemsSpanningMultipleRangesProperty(
+      TrackSpanProperties::kHasFlexibleTrack);
+  CacheGridItemsSpanningMultipleRangesProperty(
+      TrackSpanProperties::kHasIntrinsicTrack);
+  CacheGridItemsSpanningMultipleRangesProperty(
+      TrackSpanProperties::kHasAutoMinimumTrack);
+  CacheGridItemsSpanningMultipleRangesProperty(
+      TrackSpanProperties::kHasFixedMinimumTrack);
+  CacheGridItemsSpanningMultipleRangesProperty(
+      TrackSpanProperties::kHasFixedMaximumTrack);
+}
+
+// static
+GridTrackSizingAlgorithm::FirstSetGeometry
+GridTrackSizingAlgorithm::ComputeFirstSetGeometry(
+    const GridSizingTrackCollection& track_collection,
+    const ComputedStyle& container_style,
+    const LogicalSize& container_available_size,
+    const BoxStrut& container_border_scrollbar_padding) {
+  const bool is_for_columns = track_collection.Direction() == kForColumns;
+
+  const auto& available_size = is_for_columns
+                                   ? container_available_size.inline_size
+                                   : container_available_size.block_size;
+
+  // The default alignment, perform adjustments on top of this.
+  FirstSetGeometry geometry{
+      track_collection.GutterSize(),
+      is_for_columns ? container_border_scrollbar_padding.inline_start
+                     : container_border_scrollbar_padding.block_start};
+
+  // If we have an indefinite `available_size` we can't perform any alignment.
+  if (available_size == kIndefiniteSize) {
+    return geometry;
+  }
+
+  const auto& content_alignment = is_for_columns
+                                      ? container_style.JustifyContent()
+                                      : container_style.AlignContent();
+
+  // Determining the free space is typically unnecessary, i.e., if there is
+  // default alignment. Only compute this on-demand.
+  auto FreeSpace = [&]() -> LayoutUnit {
+    const auto free_space = available_size - track_collection.TotalTrackSize();
+
+    // If overflow is 'safe', make sure we don't overflow the 'start' edge
+    // (potentially causing some data loss as the overflow is unreachable).
+    return (content_alignment.Overflow() == OverflowAlignment::kSafe)
+               ? free_space.ClampNegativeToZero()
+               : free_space;
+  };
+
+  // TODO(ikilpatrick): 'space-between', 'space-around', and 'space-evenly' all
+  // divide by the free-space, and may have a non-zero modulo. Investigate if
+  // this should be distributed between the tracks.
+  switch (content_alignment.Distribution()) {
+    case ContentDistributionType::kSpaceBetween: {
+      // Default behavior for 'space-between' is to start align content.
+      const wtf_size_t track_count = track_collection.NonCollapsedTrackCount();
+      const LayoutUnit free_space = FreeSpace();
+      if (track_count < 2 || free_space < LayoutUnit()) {
+        return geometry;
+      }
+
+      geometry.gutter_size += free_space / (track_count - 1);
+      return geometry;
+    }
+    case ContentDistributionType::kSpaceAround: {
+      // Default behavior for 'space-around' is to safe center content.
+      const wtf_size_t track_count = track_collection.NonCollapsedTrackCount();
+      const LayoutUnit free_space = FreeSpace();
+      if (free_space < LayoutUnit()) {
+        return geometry;
+      }
+      if (track_count < 1) {
+        geometry.start_offset += free_space / 2;
+        return geometry;
+      }
+
+      LayoutUnit track_space = free_space / track_count;
+      geometry.start_offset += track_space / 2;
+      geometry.gutter_size += track_space;
+      return geometry;
+    }
+    case ContentDistributionType::kSpaceEvenly: {
+      // Default behavior for 'space-evenly' is to safe center content.
+      const wtf_size_t track_count = track_collection.NonCollapsedTrackCount();
+      const LayoutUnit free_space = FreeSpace();
+      if (free_space < LayoutUnit()) {
+        return geometry;
+      }
+
+      LayoutUnit track_space = free_space / (track_count + 1);
+      geometry.start_offset += track_space;
+      geometry.gutter_size += track_space;
+      return geometry;
+    }
+    case ContentDistributionType::kStretch:
+    case ContentDistributionType::kDefault:
+      break;
+  }
+
+  switch (content_alignment.GetPosition()) {
+    case ContentPosition::kLeft: {
+      DCHECK(is_for_columns);
+      if (IsLtr(container_style.Direction())) {
+        return geometry;
+      }
+
+      geometry.start_offset += FreeSpace();
+      return geometry;
+    }
+    case ContentPosition::kRight: {
+      DCHECK(is_for_columns);
+      if (IsRtl(container_style.Direction())) {
+        return geometry;
+      }
+
+      geometry.start_offset += FreeSpace();
+      return geometry;
+    }
+    case ContentPosition::kCenter: {
+      geometry.start_offset += FreeSpace() / 2;
+      return geometry;
+    }
+    case ContentPosition::kEnd:
+    case ContentPosition::kFlexEnd: {
+      geometry.start_offset += FreeSpace();
+      return geometry;
+    }
+    case ContentPosition::kStart:
+    case ContentPosition::kFlexStart:
+    case ContentPosition::kNormal:
+    case ContentPosition::kBaseline:
+    case ContentPosition::kLastBaseline:
+      return geometry;
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/grid/grid_track_sizing_algorithm.h b/third_party/blink/renderer/core/layout/grid/grid_track_sizing_algorithm.h
new file mode 100644
index 0000000..eb376e8a
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/grid/grid_track_sizing_algorithm.h
@@ -0,0 +1,43 @@
+// Copyright 2025 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_LAYOUT_GRID_GRID_TRACK_SIZING_ALGORITHM_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_GRID_GRID_TRACK_SIZING_ALGORITHM_H_
+
+#include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
+
+namespace blink {
+
+class ComputedStyle;
+class GridItems;
+class GridSizingTrackCollection;
+struct BoxStrut;
+
+class GridTrackSizingAlgorithm {
+  STACK_ALLOCATED();
+
+ public:
+  struct FirstSetGeometry {
+    LayoutUnit gutter_size;
+    LayoutUnit start_offset;
+  };
+
+  // Caches the track span properties necessary for the track sizing algorithm
+  // to work based on the grid items' placement within the track collection.
+  static void CacheGridItemsProperties(
+      const GridSizingTrackCollection& track_collection,
+      GridItems* grid_items);
+
+  // For the first track, computes the start offset and gutter size based on the
+  // alignment properties and available size of the container.
+  static FirstSetGeometry ComputeFirstSetGeometry(
+      const GridSizingTrackCollection& track_collection,
+      const ComputedStyle& container_style,
+      const LogicalSize& container_available_size,
+      const BoxStrut& container_border_scrollbar_padding);
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_GRID_GRID_TRACK_SIZING_ALGORITHM_H_
diff --git a/third_party/blink/renderer/core/svg/svg_a_element.cc b/third_party/blink/renderer/core/svg/svg_a_element.cc
index 09f7ae3..9bac8ecb8 100644
--- a/third_party/blink/renderer/core/svg/svg_a_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_a_element.cc
@@ -179,17 +179,6 @@
       svg_names::kInteresttargetAttr);
 }
 
-AtomicString SVGAElement::interestAction() const {
-  CHECK(RuntimeEnabledFeatures::HTMLInterestTargetAttributeEnabled());
-  const AtomicString& attribute_value =
-      FastGetAttribute(svg_names::kInterestactionAttr);
-  if (attribute_value && !attribute_value.IsNull() &&
-      !attribute_value.empty()) {
-    return attribute_value;
-  }
-  return g_empty_atom;
-}
-
 bool SVGAElement::HasActivationBehavior() const {
   return true;
 }
diff --git a/third_party/blink/renderer/core/svg/svg_a_element.h b/third_party/blink/renderer/core/svg/svg_a_element.h
index 0515d909a..59cb21f 100644
--- a/third_party/blink/renderer/core/svg/svg_a_element.h
+++ b/third_party/blink/renderer/core/svg/svg_a_element.h
@@ -38,7 +38,6 @@
   explicit SVGAElement(Document&);
 
   Element* interestTargetElement() override;
-  AtomicString interestAction() const override;
 
   void Trace(Visitor*) const override;
 
diff --git a/third_party/blink/renderer/core/svg/svg_component_transfer_function_element.idl b/third_party/blink/renderer/core/svg/svg_component_transfer_function_element.idl
index 91954532..ae72b520 100644
--- a/third_party/blink/renderer/core/svg/svg_component_transfer_function_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_component_transfer_function_element.idl
@@ -29,18 +29,18 @@
     Exposed=Window
 ] interface SVGComponentTransferFunctionElement : SVGElement {
     // Component Transfer Types
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN  = 0;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY = 1;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_TABLE    = 2;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE = 3;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_LINEAR   = 4;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_GAMMA    = 5;
+    const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN  = 0;
+    const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY = 1;
+    const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_TABLE    = 2;
+    const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE = 3;
+    const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_LINEAR   = 4;
+    const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_GAMMA    = 5;
 
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedEnumeration type;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumberList  tableValues;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber      slope;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber      intercept;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber      amplitude;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber      exponent;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber      offset;
+    readonly attribute SVGAnimatedEnumeration type;
+    readonly attribute SVGAnimatedNumberList  tableValues;
+    readonly attribute SVGAnimatedNumber      slope;
+    readonly attribute SVGAnimatedNumber      intercept;
+    readonly attribute SVGAnimatedNumber      amplitude;
+    readonly attribute SVGAnimatedNumber      exponent;
+    readonly attribute SVGAnimatedNumber      offset;
 };
diff --git a/third_party/blink/renderer/core/svg/svg_fe_blend_element.idl b/third_party/blink/renderer/core/svg/svg_fe_blend_element.idl
index ed662a9..b2dc700 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_blend_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_blend_element.idl
@@ -30,27 +30,27 @@
 ] interface SVGFEBlendElement : SVGElement {
 
     // Blend Mode Types
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_UNKNOWN = 0;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_NORMAL = 1;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_MULTIPLY = 2;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_SCREEN = 3;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_DARKEN = 4;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_LIGHTEN = 5;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_OVERLAY = 6;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_COLOR_DODGE = 7;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_COLOR_BURN = 8;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_HARD_LIGHT = 9;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_SOFT_LIGHT = 10;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_DIFFERENCE = 11;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_EXCLUSION = 12;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_HUE = 13;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_SATURATION = 14;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_COLOR = 15;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FEBLEND_MODE_LUMINOSITY = 16;
+    const unsigned short SVG_FEBLEND_MODE_UNKNOWN = 0;
+    const unsigned short SVG_FEBLEND_MODE_NORMAL = 1;
+    const unsigned short SVG_FEBLEND_MODE_MULTIPLY = 2;
+    const unsigned short SVG_FEBLEND_MODE_SCREEN = 3;
+    const unsigned short SVG_FEBLEND_MODE_DARKEN = 4;
+    const unsigned short SVG_FEBLEND_MODE_LIGHTEN = 5;
+    const unsigned short SVG_FEBLEND_MODE_OVERLAY = 6;
+    const unsigned short SVG_FEBLEND_MODE_COLOR_DODGE = 7;
+    const unsigned short SVG_FEBLEND_MODE_COLOR_BURN = 8;
+    const unsigned short SVG_FEBLEND_MODE_HARD_LIGHT = 9;
+    const unsigned short SVG_FEBLEND_MODE_SOFT_LIGHT = 10;
+    const unsigned short SVG_FEBLEND_MODE_DIFFERENCE = 11;
+    const unsigned short SVG_FEBLEND_MODE_EXCLUSION = 12;
+    const unsigned short SVG_FEBLEND_MODE_HUE = 13;
+    const unsigned short SVG_FEBLEND_MODE_SATURATION = 14;
+    const unsigned short SVG_FEBLEND_MODE_COLOR = 15;
+    const unsigned short SVG_FEBLEND_MODE_LUMINOSITY = 16;
 
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in1;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in2;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedEnumeration mode;
+    readonly attribute SVGAnimatedString in1;
+    readonly attribute SVGAnimatedString in2;
+    readonly attribute SVGAnimatedEnumeration mode;
 };
 
 SVGFEBlendElement includes SVGFilterPrimitiveStandardAttributes;
diff --git a/third_party/blink/renderer/core/svg/svg_fe_color_matrix_element.idl b/third_party/blink/renderer/core/svg/svg_fe_color_matrix_element.idl
index 50ea924..ec591a2 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_color_matrix_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_color_matrix_element.idl
@@ -29,15 +29,15 @@
     Exposed=Window
 ] interface SVGFEColorMatrixElement : SVGElement {
     // Color Matrix Types
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOLORMATRIX_TYPE_UNKNOWN = 0;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOLORMATRIX_TYPE_MATRIX = 1;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOLORMATRIX_TYPE_SATURATE = 2;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOLORMATRIX_TYPE_HUEROTATE = 3;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA = 4;
+    const unsigned short SVG_FECOLORMATRIX_TYPE_UNKNOWN = 0;
+    const unsigned short SVG_FECOLORMATRIX_TYPE_MATRIX = 1;
+    const unsigned short SVG_FECOLORMATRIX_TYPE_SATURATE = 2;
+    const unsigned short SVG_FECOLORMATRIX_TYPE_HUEROTATE = 3;
+    const unsigned short SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA = 4;
 
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in1;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedEnumeration type;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumberList values;
+    readonly attribute SVGAnimatedString in1;
+    readonly attribute SVGAnimatedEnumeration type;
+    readonly attribute SVGAnimatedNumberList values;
 };
 
 SVGFEColorMatrixElement includes SVGFilterPrimitiveStandardAttributes;
diff --git a/third_party/blink/renderer/core/svg/svg_fe_component_transfer_element.idl b/third_party/blink/renderer/core/svg/svg_fe_component_transfer_element.idl
index 52a4341..caf7f1c8 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_component_transfer_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_component_transfer_element.idl
@@ -28,7 +28,7 @@
 [
     Exposed=Window
 ] interface SVGFEComponentTransferElement : SVGElement {
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in1;
+    readonly attribute SVGAnimatedString in1;
 };
 
 SVGFEComponentTransferElement includes SVGFilterPrimitiveStandardAttributes;
diff --git a/third_party/blink/renderer/core/svg/svg_fe_composite_element.idl b/third_party/blink/renderer/core/svg/svg_fe_composite_element.idl
index 525ef250..d2810ea 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_composite_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_composite_element.idl
@@ -29,21 +29,21 @@
     Exposed=Window
 ] interface SVGFECompositeElement : SVGElement {
     // Composite Operators
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOMPOSITE_OPERATOR_UNKNOWN = 0;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOMPOSITE_OPERATOR_OVER = 1;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOMPOSITE_OPERATOR_IN = 2;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOMPOSITE_OPERATOR_OUT = 3;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOMPOSITE_OPERATOR_ATOP = 4;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOMPOSITE_OPERATOR_XOR = 5;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_FECOMPOSITE_OPERATOR_ARITHMETIC = 6;
+    const unsigned short SVG_FECOMPOSITE_OPERATOR_UNKNOWN = 0;
+    const unsigned short SVG_FECOMPOSITE_OPERATOR_OVER = 1;
+    const unsigned short SVG_FECOMPOSITE_OPERATOR_IN = 2;
+    const unsigned short SVG_FECOMPOSITE_OPERATOR_OUT = 3;
+    const unsigned short SVG_FECOMPOSITE_OPERATOR_ATOP = 4;
+    const unsigned short SVG_FECOMPOSITE_OPERATOR_XOR = 5;
+    const unsigned short SVG_FECOMPOSITE_OPERATOR_ARITHMETIC = 6;
 
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in2;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in1;
-    [ImplementedAs=svgOperator, MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedEnumeration operator;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber k1;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber k2;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber k3;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber k4;
+    readonly attribute SVGAnimatedString in2;
+    readonly attribute SVGAnimatedString in1;
+    [ImplementedAs=svgOperator] readonly attribute SVGAnimatedEnumeration operator;
+    readonly attribute SVGAnimatedNumber k1;
+    readonly attribute SVGAnimatedNumber k2;
+    readonly attribute SVGAnimatedNumber k3;
+    readonly attribute SVGAnimatedNumber k4;
 };
 
 SVGFECompositeElement includes SVGFilterPrimitiveStandardAttributes;
diff --git a/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.idl b/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.idl
index 3538b18..c7fc8cf 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_convolve_matrix_element.idl
@@ -29,22 +29,22 @@
     Exposed=Window
 ] interface SVGFEConvolveMatrixElement : SVGElement {
     // Edge Mode Values
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_EDGEMODE_UNKNOWN = 0;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_EDGEMODE_DUPLICATE = 1;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_EDGEMODE_WRAP = 2;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_EDGEMODE_NONE = 3;
+    const unsigned short SVG_EDGEMODE_UNKNOWN = 0;
+    const unsigned short SVG_EDGEMODE_DUPLICATE = 1;
+    const unsigned short SVG_EDGEMODE_WRAP = 2;
+    const unsigned short SVG_EDGEMODE_NONE = 3;
 
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in1;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedInteger orderX;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedInteger orderY;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumberList kernelMatrix;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber divisor;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber bias;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedInteger targetX;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedInteger targetY;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedEnumeration edgeMode;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber kernelUnitLengthX;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber kernelUnitLengthY;
+    readonly attribute SVGAnimatedString in1;
+    readonly attribute SVGAnimatedInteger orderX;
+    readonly attribute SVGAnimatedInteger orderY;
+    readonly attribute SVGAnimatedNumberList kernelMatrix;
+    readonly attribute SVGAnimatedNumber divisor;
+    readonly attribute SVGAnimatedNumber bias;
+    readonly attribute SVGAnimatedInteger targetX;
+    readonly attribute SVGAnimatedInteger targetY;
+    readonly attribute SVGAnimatedEnumeration edgeMode;
+    readonly attribute SVGAnimatedNumber kernelUnitLengthX;
+    readonly attribute SVGAnimatedNumber kernelUnitLengthY;
     [Measure] readonly attribute SVGAnimatedBoolean preserveAlpha;
 };
 
diff --git a/third_party/blink/renderer/core/svg/svg_fe_diffuse_lighting_element.idl b/third_party/blink/renderer/core/svg/svg_fe_diffuse_lighting_element.idl
index 593d161..1036bc3 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_diffuse_lighting_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_diffuse_lighting_element.idl
@@ -28,11 +28,11 @@
 [
     Exposed=Window
 ] interface SVGFEDiffuseLightingElement : SVGElement {
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in1;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber surfaceScale;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber diffuseConstant;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber kernelUnitLengthX;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber kernelUnitLengthY;
+    readonly attribute SVGAnimatedString in1;
+    readonly attribute SVGAnimatedNumber surfaceScale;
+    readonly attribute SVGAnimatedNumber diffuseConstant;
+    readonly attribute SVGAnimatedNumber kernelUnitLengthX;
+    readonly attribute SVGAnimatedNumber kernelUnitLengthY;
 };
 
 SVGFEDiffuseLightingElement includes SVGFilterPrimitiveStandardAttributes;
diff --git a/third_party/blink/renderer/core/svg/svg_fe_displacement_map_element.idl b/third_party/blink/renderer/core/svg/svg_fe_displacement_map_element.idl
index c535b68e..4ee5f02 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_displacement_map_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_displacement_map_element.idl
@@ -29,17 +29,17 @@
     Exposed=Window
 ] interface SVGFEDisplacementMapElement : SVGElement {
     // Channel Selectors
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_CHANNEL_UNKNOWN = 0;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_CHANNEL_R = 1;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_CHANNEL_G = 2;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_CHANNEL_B = 3;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_CHANNEL_A = 4;
+    const unsigned short SVG_CHANNEL_UNKNOWN = 0;
+    const unsigned short SVG_CHANNEL_R = 1;
+    const unsigned short SVG_CHANNEL_G = 2;
+    const unsigned short SVG_CHANNEL_B = 3;
+    const unsigned short SVG_CHANNEL_A = 4;
 
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in1;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in2;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber scale;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedEnumeration xChannelSelector;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedEnumeration yChannelSelector;
+    readonly attribute SVGAnimatedString in1;
+    readonly attribute SVGAnimatedString in2;
+    readonly attribute SVGAnimatedNumber scale;
+    readonly attribute SVGAnimatedEnumeration xChannelSelector;
+    readonly attribute SVGAnimatedEnumeration yChannelSelector;
 };
 
 SVGFEDisplacementMapElement includes SVGFilterPrimitiveStandardAttributes;
diff --git a/third_party/blink/renderer/core/svg/svg_fe_distant_light_element.idl b/third_party/blink/renderer/core/svg/svg_fe_distant_light_element.idl
index 0efa530..3f0fed1 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_distant_light_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_distant_light_element.idl
@@ -28,6 +28,6 @@
 [
     Exposed=Window
 ] interface SVGFEDistantLightElement : SVGElement {
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber azimuth;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber elevation;
+    readonly attribute SVGAnimatedNumber azimuth;
+    readonly attribute SVGAnimatedNumber elevation;
 };
diff --git a/third_party/blink/renderer/core/svg/svg_fe_drop_shadow_element.idl b/third_party/blink/renderer/core/svg/svg_fe_drop_shadow_element.idl
index 7766e41..6da4c9e 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_drop_shadow_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_drop_shadow_element.idl
@@ -22,13 +22,13 @@
 [
     Exposed=Window
 ] interface SVGFEDropShadowElement : SVGElement {
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in1;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber dx;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber dy;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber stdDeviationX;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber stdDeviationY;
+    readonly attribute SVGAnimatedString in1;
+    readonly attribute SVGAnimatedNumber dx;
+    readonly attribute SVGAnimatedNumber dy;
+    readonly attribute SVGAnimatedNumber stdDeviationX;
+    readonly attribute SVGAnimatedNumber stdDeviationY;
 
-    [MeasureAs=SVG1DOMFilter] void setStdDeviation(float stdDeviationX, float stdDeviationY);
+    void setStdDeviation(float stdDeviationX, float stdDeviationY);
 };
 
 SVGFEDropShadowElement includes SVGFilterPrimitiveStandardAttributes;
diff --git a/third_party/blink/renderer/core/svg/svg_fe_gaussian_blur_element.idl b/third_party/blink/renderer/core/svg/svg_fe_gaussian_blur_element.idl
index 31392491..ae6298b5 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_gaussian_blur_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_gaussian_blur_element.idl
@@ -28,11 +28,11 @@
 [
     Exposed=Window
 ] interface SVGFEGaussianBlurElement : SVGElement {
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in1;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber stdDeviationX;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber stdDeviationY;
+    readonly attribute SVGAnimatedString in1;
+    readonly attribute SVGAnimatedNumber stdDeviationX;
+    readonly attribute SVGAnimatedNumber stdDeviationY;
 
-    [MeasureAs=SVG1DOMFilter] void setStdDeviation(float stdDeviationX, float stdDeviationY);
+    void setStdDeviation(float stdDeviationX, float stdDeviationY);
 };
 
 SVGFEGaussianBlurElement includes SVGFilterPrimitiveStandardAttributes;
diff --git a/third_party/blink/renderer/core/svg/svg_fe_image_element.idl b/third_party/blink/renderer/core/svg/svg_fe_image_element.idl
index 69340c2..5e7dbb6 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_image_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_image_element.idl
@@ -28,7 +28,7 @@
 [
     Exposed=Window
 ] interface SVGFEImageElement : SVGElement {
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedPreserveAspectRatio preserveAspectRatio;
+    readonly attribute SVGAnimatedPreserveAspectRatio preserveAspectRatio;
 };
 
 SVGFEImageElement includes SVGFilterPrimitiveStandardAttributes;
diff --git a/third_party/blink/renderer/core/svg/svg_fe_merge_node_element.idl b/third_party/blink/renderer/core/svg/svg_fe_merge_node_element.idl
index 5cdde1ff..1752919 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_merge_node_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_merge_node_element.idl
@@ -28,5 +28,5 @@
 [
     Exposed=Window
 ] interface SVGFEMergeNodeElement : SVGElement {
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in1;
+    readonly attribute SVGAnimatedString in1;
 };
diff --git a/third_party/blink/renderer/core/svg/svg_fe_morphology_element.idl b/third_party/blink/renderer/core/svg/svg_fe_morphology_element.idl
index d1714bd..b9dc359 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_morphology_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_morphology_element.idl
@@ -29,14 +29,14 @@
     Exposed=Window
 ] interface SVGFEMorphologyElement : SVGElement {
     // Morphology Operators
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_MORPHOLOGY_OPERATOR_UNKNOWN = 0;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_MORPHOLOGY_OPERATOR_ERODE = 1;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_MORPHOLOGY_OPERATOR_DILATE = 2;
+    const unsigned short SVG_MORPHOLOGY_OPERATOR_UNKNOWN = 0;
+    const unsigned short SVG_MORPHOLOGY_OPERATOR_ERODE = 1;
+    const unsigned short SVG_MORPHOLOGY_OPERATOR_DILATE = 2;
 
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in1;
-    [ImplementedAs=svgOperator, MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedEnumeration operator;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber radiusX;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber radiusY;
+    readonly attribute SVGAnimatedString in1;
+    [ImplementedAs=svgOperator] readonly attribute SVGAnimatedEnumeration operator;
+    readonly attribute SVGAnimatedNumber radiusX;
+    readonly attribute SVGAnimatedNumber radiusY;
 };
 
 SVGFEMorphologyElement includes SVGFilterPrimitiveStandardAttributes;
diff --git a/third_party/blink/renderer/core/svg/svg_fe_offset_element.idl b/third_party/blink/renderer/core/svg/svg_fe_offset_element.idl
index 187b340..13feff0 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_offset_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_offset_element.idl
@@ -28,9 +28,9 @@
 [
     Exposed=Window
 ] interface SVGFEOffsetElement : SVGElement {
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in1;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber dx;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber dy;
+    readonly attribute SVGAnimatedString in1;
+    readonly attribute SVGAnimatedNumber dx;
+    readonly attribute SVGAnimatedNumber dy;
 };
 
 SVGFEOffsetElement includes SVGFilterPrimitiveStandardAttributes;
diff --git a/third_party/blink/renderer/core/svg/svg_fe_point_light_element.idl b/third_party/blink/renderer/core/svg/svg_fe_point_light_element.idl
index 9ce287b..46e068f 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_point_light_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_point_light_element.idl
@@ -28,7 +28,7 @@
 [
     Exposed=Window
 ] interface SVGFEPointLightElement : SVGElement {
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber x;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber y;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber z;
+    readonly attribute SVGAnimatedNumber x;
+    readonly attribute SVGAnimatedNumber y;
+    readonly attribute SVGAnimatedNumber z;
 };
diff --git a/third_party/blink/renderer/core/svg/svg_fe_specular_lighting_element.idl b/third_party/blink/renderer/core/svg/svg_fe_specular_lighting_element.idl
index 9a90e14b..ee9f1d5 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_specular_lighting_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_specular_lighting_element.idl
@@ -28,12 +28,12 @@
 [
     Exposed=Window
 ] interface SVGFESpecularLightingElement : SVGElement {
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in1;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber surfaceScale;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber specularConstant;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber specularExponent;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber kernelUnitLengthX;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber kernelUnitLengthY;
+    readonly attribute SVGAnimatedString in1;
+    readonly attribute SVGAnimatedNumber surfaceScale;
+    readonly attribute SVGAnimatedNumber specularConstant;
+    readonly attribute SVGAnimatedNumber specularExponent;
+    readonly attribute SVGAnimatedNumber kernelUnitLengthX;
+    readonly attribute SVGAnimatedNumber kernelUnitLengthY;
 };
 
 SVGFESpecularLightingElement includes SVGFilterPrimitiveStandardAttributes;
diff --git a/third_party/blink/renderer/core/svg/svg_fe_spot_light_element.idl b/third_party/blink/renderer/core/svg/svg_fe_spot_light_element.idl
index bd4ee0c..b880c0f 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_spot_light_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_spot_light_element.idl
@@ -28,12 +28,12 @@
 [
     Exposed=Window
 ] interface SVGFESpotLightElement : SVGElement {
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber x;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber y;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber z;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber pointsAtX;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber pointsAtY;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber pointsAtZ;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber specularExponent;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber limitingConeAngle;
+    readonly attribute SVGAnimatedNumber x;
+    readonly attribute SVGAnimatedNumber y;
+    readonly attribute SVGAnimatedNumber z;
+    readonly attribute SVGAnimatedNumber pointsAtX;
+    readonly attribute SVGAnimatedNumber pointsAtY;
+    readonly attribute SVGAnimatedNumber pointsAtZ;
+    readonly attribute SVGAnimatedNumber specularExponent;
+    readonly attribute SVGAnimatedNumber limitingConeAngle;
 };
diff --git a/third_party/blink/renderer/core/svg/svg_fe_tile_element.idl b/third_party/blink/renderer/core/svg/svg_fe_tile_element.idl
index ff6e867..c0d24fe0 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_tile_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_tile_element.idl
@@ -28,7 +28,7 @@
 [
     Exposed=Window
 ] interface SVGFETileElement : SVGElement {
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString in1;
+    readonly attribute SVGAnimatedString in1;
 };
 
 SVGFETileElement includes SVGFilterPrimitiveStandardAttributes;
diff --git a/third_party/blink/renderer/core/svg/svg_fe_turbulence_element.idl b/third_party/blink/renderer/core/svg/svg_fe_turbulence_element.idl
index 42ad8ab7..eaf1503 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_turbulence_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_fe_turbulence_element.idl
@@ -29,21 +29,21 @@
     Exposed=Window
 ] interface SVGFETurbulenceElement : SVGElement {
     // Turbulence Types
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_TURBULENCE_TYPE_UNKNOWN = 0;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_TURBULENCE_TYPE_FRACTALNOISE = 1;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_TURBULENCE_TYPE_TURBULENCE = 2;
+    const unsigned short SVG_TURBULENCE_TYPE_UNKNOWN = 0;
+    const unsigned short SVG_TURBULENCE_TYPE_FRACTALNOISE = 1;
+    const unsigned short SVG_TURBULENCE_TYPE_TURBULENCE = 2;
 
     // Stitch Options
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_STITCHTYPE_UNKNOWN = 0;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_STITCHTYPE_STITCH = 1;
-    [MeasureAs=SVG1DOMFilter] const unsigned short SVG_STITCHTYPE_NOSTITCH = 2;
+    const unsigned short SVG_STITCHTYPE_UNKNOWN = 0;
+    const unsigned short SVG_STITCHTYPE_STITCH = 1;
+    const unsigned short SVG_STITCHTYPE_NOSTITCH = 2;
 
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber baseFrequencyX;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber baseFrequencyY;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedInteger numOctaves;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedNumber seed;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedEnumeration stitchTiles;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedEnumeration type;
+    readonly attribute SVGAnimatedNumber baseFrequencyX;
+    readonly attribute SVGAnimatedNumber baseFrequencyY;
+    readonly attribute SVGAnimatedInteger numOctaves;
+    readonly attribute SVGAnimatedNumber seed;
+    readonly attribute SVGAnimatedEnumeration stitchTiles;
+    readonly attribute SVGAnimatedEnumeration type;
 };
 
 SVGFETurbulenceElement includes SVGFilterPrimitiveStandardAttributes;
diff --git a/third_party/blink/renderer/core/svg/svg_filter_element.idl b/third_party/blink/renderer/core/svg/svg_filter_element.idl
index 06e1295c0..3f76157 100644
--- a/third_party/blink/renderer/core/svg/svg_filter_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_filter_element.idl
@@ -29,12 +29,12 @@
 [
     Exposed=Window
 ] interface SVGFilterElement : SVGElement {
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedEnumeration filterUnits;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedEnumeration primitiveUnits;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedLength      x;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedLength      y;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedLength      width;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedLength      height;
+    readonly attribute SVGAnimatedEnumeration filterUnits;
+    readonly attribute SVGAnimatedEnumeration primitiveUnits;
+    readonly attribute SVGAnimatedLength      x;
+    readonly attribute SVGAnimatedLength      y;
+    readonly attribute SVGAnimatedLength      width;
+    readonly attribute SVGAnimatedLength      height;
 };
 
 SVGFilterElement includes SVGURIReference;
diff --git a/third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.idl b/third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.idl
index f6852b7..4a524220 100644
--- a/third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.idl
+++ b/third_party/blink/renderer/core/svg/svg_filter_primitive_standard_attributes.idl
@@ -27,9 +27,9 @@
 // https://drafts.fxtf.org/filter-effects/#InterfaceSVGFilterPrimitiveStandardAttributes
 
 interface mixin SVGFilterPrimitiveStandardAttributes {
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedLength x;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedLength y;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedLength width;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedLength height;
-    [MeasureAs=SVG1DOMFilter] readonly attribute SVGAnimatedString result;
+    readonly attribute SVGAnimatedLength x;
+    readonly attribute SVGAnimatedLength y;
+    readonly attribute SVGAnimatedLength width;
+    readonly attribute SVGAnimatedLength height;
+    readonly attribute SVGAnimatedString result;
 };
diff --git a/third_party/blink/renderer/core/svg/svg_gradient_element.idl b/third_party/blink/renderer/core/svg/svg_gradient_element.idl
index 8c1fa4f..62fb390 100644
--- a/third_party/blink/renderer/core/svg/svg_gradient_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_gradient_element.idl
@@ -29,14 +29,14 @@
     Exposed=Window
 ] interface SVGGradientElement : SVGElement {
     // Spread Method Types
-    [MeasureAs=SVG1DOMPaintServer] const unsigned short SVG_SPREADMETHOD_UNKNOWN = 0;
-    [MeasureAs=SVG1DOMPaintServer] const unsigned short SVG_SPREADMETHOD_PAD     = 1;
-    [MeasureAs=SVG1DOMPaintServer] const unsigned short SVG_SPREADMETHOD_REFLECT = 2;
-    [MeasureAs=SVG1DOMPaintServer] const unsigned short SVG_SPREADMETHOD_REPEAT  = 3;
+    const unsigned short SVG_SPREADMETHOD_UNKNOWN = 0;
+    const unsigned short SVG_SPREADMETHOD_PAD     = 1;
+    const unsigned short SVG_SPREADMETHOD_REFLECT = 2;
+    const unsigned short SVG_SPREADMETHOD_REPEAT  = 3;
 
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedEnumeration   gradientUnits;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedTransformList gradientTransform;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedEnumeration   spreadMethod;
+    readonly attribute SVGAnimatedEnumeration   gradientUnits;
+    readonly attribute SVGAnimatedTransformList gradientTransform;
+    readonly attribute SVGAnimatedEnumeration   spreadMethod;
 };
 
 SVGGradientElement includes SVGURIReference;
diff --git a/third_party/blink/renderer/core/svg/svg_linear_gradient_element.idl b/third_party/blink/renderer/core/svg/svg_linear_gradient_element.idl
index 02a6d07..b806265 100644
--- a/third_party/blink/renderer/core/svg/svg_linear_gradient_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_linear_gradient_element.idl
@@ -27,8 +27,8 @@
 
 [Exposed=Window]
 interface SVGLinearGradientElement : SVGGradientElement {
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedLength x1;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedLength y1;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedLength x2;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedLength y2;
+    readonly attribute SVGAnimatedLength x1;
+    readonly attribute SVGAnimatedLength y1;
+    readonly attribute SVGAnimatedLength x2;
+    readonly attribute SVGAnimatedLength y2;
 };
diff --git a/third_party/blink/renderer/core/svg/svg_pattern_element.idl b/third_party/blink/renderer/core/svg/svg_pattern_element.idl
index 49098b490..168a6425 100644
--- a/third_party/blink/renderer/core/svg/svg_pattern_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_pattern_element.idl
@@ -27,13 +27,13 @@
 
 [Exposed=Window]
 interface SVGPatternElement : SVGElement {
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedEnumeration patternUnits;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedEnumeration patternContentUnits;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedTransformList patternTransform;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedLength x;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedLength y;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedLength width;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedLength height;
+    readonly attribute SVGAnimatedEnumeration patternUnits;
+    readonly attribute SVGAnimatedEnumeration patternContentUnits;
+    readonly attribute SVGAnimatedTransformList patternTransform;
+    readonly attribute SVGAnimatedLength x;
+    readonly attribute SVGAnimatedLength y;
+    readonly attribute SVGAnimatedLength width;
+    readonly attribute SVGAnimatedLength height;
 };
 
 SVGPatternElement includes SVGFitToViewBox;
diff --git a/third_party/blink/renderer/core/svg/svg_radial_gradient_element.idl b/third_party/blink/renderer/core/svg/svg_radial_gradient_element.idl
index 3330290..a52e2d5 100644
--- a/third_party/blink/renderer/core/svg/svg_radial_gradient_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_radial_gradient_element.idl
@@ -27,10 +27,10 @@
 
 [Exposed=Window]
 interface SVGRadialGradientElement : SVGGradientElement {
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedLength cx;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedLength cy;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedLength r;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedLength fx;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedLength fy;
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedLength fr;
+    readonly attribute SVGAnimatedLength cx;
+    readonly attribute SVGAnimatedLength cy;
+    readonly attribute SVGAnimatedLength r;
+    readonly attribute SVGAnimatedLength fx;
+    readonly attribute SVGAnimatedLength fy;
+    readonly attribute SVGAnimatedLength fr;
 };
diff --git a/third_party/blink/renderer/core/svg/svg_stop_element.idl b/third_party/blink/renderer/core/svg/svg_stop_element.idl
index 36fc16ec..7bed08a 100644
--- a/third_party/blink/renderer/core/svg/svg_stop_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_stop_element.idl
@@ -27,5 +27,5 @@
 
 [Exposed=Window]
 interface SVGStopElement : SVGElement {
-    [MeasureAs=SVG1DOMPaintServer] readonly attribute SVGAnimatedNumber offset;
+    readonly attribute SVGAnimatedNumber offset;
 };
diff --git a/third_party/blink/renderer/core/svg/svg_text_content_element.idl b/third_party/blink/renderer/core/svg/svg_text_content_element.idl
index 2117f0d..a51524b7 100644
--- a/third_party/blink/renderer/core/svg/svg_text_content_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_text_content_element.idl
@@ -28,12 +28,12 @@
 [Exposed=Window]
 interface SVGTextContentElement : SVGGraphicsElement {
     // lengthAdjust Types
-    [MeasureAs=SVG1DOMText] const unsigned short LENGTHADJUST_UNKNOWN          = 0;
-    [MeasureAs=SVG1DOMText] const unsigned short LENGTHADJUST_SPACING          = 1;
-    [MeasureAs=SVG1DOMText] const unsigned short LENGTHADJUST_SPACINGANDGLYPHS = 2;
+    const unsigned short LENGTHADJUST_UNKNOWN          = 0;
+    const unsigned short LENGTHADJUST_SPACING          = 1;
+    const unsigned short LENGTHADJUST_SPACINGANDGLYPHS = 2;
 
-    [MeasureAs=SVG1DOMText] readonly attribute SVGAnimatedLength      textLength;
-    [MeasureAs=SVG1DOMText] readonly attribute SVGAnimatedEnumeration lengthAdjust;
+    readonly attribute SVGAnimatedLength      textLength;
+    readonly attribute SVGAnimatedEnumeration lengthAdjust;
 
     long getNumberOfChars();
     [HighEntropy, Measure] float getComputedTextLength();
diff --git a/third_party/blink/renderer/core/svg/svg_text_path_element.idl b/third_party/blink/renderer/core/svg/svg_text_path_element.idl
index 4fa851e..04b8c6a2 100644
--- a/third_party/blink/renderer/core/svg/svg_text_path_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_text_path_element.idl
@@ -28,18 +28,18 @@
 [Exposed=Window]
 interface SVGTextPathElement : SVGTextContentElement {
     // textPath Method Types
-    [MeasureAs=SVG1DOMText] const unsigned short TEXTPATH_METHODTYPE_UNKNOWN   = 0;
-    [MeasureAs=SVG1DOMText] const unsigned short TEXTPATH_METHODTYPE_ALIGN     = 1;
-    [MeasureAs=SVG1DOMText] const unsigned short TEXTPATH_METHODTYPE_STRETCH   = 2;
+    const unsigned short TEXTPATH_METHODTYPE_UNKNOWN   = 0;
+    const unsigned short TEXTPATH_METHODTYPE_ALIGN     = 1;
+    const unsigned short TEXTPATH_METHODTYPE_STRETCH   = 2;
 
     // textPath Spacing Types
-    [MeasureAs=SVG1DOMText] const unsigned short TEXTPATH_SPACINGTYPE_UNKNOWN  = 0;
-    [MeasureAs=SVG1DOMText] const unsigned short TEXTPATH_SPACINGTYPE_AUTO     = 1;
-    [MeasureAs=SVG1DOMText] const unsigned short TEXTPATH_SPACINGTYPE_EXACT    = 2;
+    const unsigned short TEXTPATH_SPACINGTYPE_UNKNOWN  = 0;
+    const unsigned short TEXTPATH_SPACINGTYPE_AUTO     = 1;
+    const unsigned short TEXTPATH_SPACINGTYPE_EXACT    = 2;
 
-    [MeasureAs=SVG1DOMText] readonly attribute SVGAnimatedLength startOffset;
-    [MeasureAs=SVG1DOMText] readonly attribute SVGAnimatedEnumeration method;
-    [MeasureAs=SVG1DOMText] readonly attribute SVGAnimatedEnumeration spacing;
+    readonly attribute SVGAnimatedLength startOffset;
+    readonly attribute SVGAnimatedEnumeration method;
+    readonly attribute SVGAnimatedEnumeration spacing;
 };
 
 SVGTextPathElement includes SVGURIReference;
diff --git a/third_party/blink/renderer/core/svg/svg_text_positioning_element.idl b/third_party/blink/renderer/core/svg/svg_text_positioning_element.idl
index 3e3e90c..dcf973b5 100644
--- a/third_party/blink/renderer/core/svg/svg_text_positioning_element.idl
+++ b/third_party/blink/renderer/core/svg/svg_text_positioning_element.idl
@@ -27,9 +27,9 @@
 
 [Exposed=Window]
 interface SVGTextPositioningElement : SVGTextContentElement {
-    [MeasureAs=SVG1DOMText] readonly attribute SVGAnimatedLengthList x;
-    [MeasureAs=SVG1DOMText] readonly attribute SVGAnimatedLengthList y;
-    [MeasureAs=SVG1DOMText] readonly attribute SVGAnimatedLengthList dx;
-    [MeasureAs=SVG1DOMText] readonly attribute SVGAnimatedLengthList dy;
-    [MeasureAs=SVG1DOMText] readonly attribute SVGAnimatedNumberList rotate;
+    readonly attribute SVGAnimatedLengthList x;
+    readonly attribute SVGAnimatedLengthList y;
+    readonly attribute SVGAnimatedLengthList dx;
+    readonly attribute SVGAnimatedLengthList dy;
+    readonly attribute SVGAnimatedNumberList rotate;
 };
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
index 293871a6..a8cf2d3 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
+++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
@@ -36,8 +36,7 @@
     kDOMWrappersTag,
     WrapperTypeInfo::kWrapperTypeObjectPrototype,
     WrapperTypeInfo::kObjectClassId,
-    WrapperTypeInfo::kNotInheritFromActiveScriptWrappable,
-    WrapperTypeInfo::kIdlBufferSourceType,
+    WrapperTypeInfo::kIdlOtherType,
 };
 
 const WrapperTypeInfo& DOMArrayBuffer::wrapper_type_info_ =
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.cc b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.cc
index 07ac168..33b5841 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.cc
+++ b/third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.cc
@@ -26,8 +26,7 @@
     kDOMWrappersTag,
     WrapperTypeInfo::kWrapperTypeObjectPrototype,
     WrapperTypeInfo::kObjectClassId,
-    WrapperTypeInfo::kNotInheritFromActiveScriptWrappable,
-    WrapperTypeInfo::kIdlBufferSourceType,
+    WrapperTypeInfo::kIdlOtherType,
 };
 
 const WrapperTypeInfo& DOMArrayBufferView::wrapper_type_info_ =
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc b/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc
index f139eedd..02a277fe 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc
+++ b/third_party/blink/renderer/core/typed_arrays/dom_data_view.cc
@@ -31,8 +31,7 @@
     kDOMWrappersTag,
     WrapperTypeInfo::kWrapperTypeObjectPrototype,
     WrapperTypeInfo::kObjectClassId,
-    WrapperTypeInfo::kNotInheritFromActiveScriptWrappable,
-    WrapperTypeInfo::kIdlBufferSourceType,
+    WrapperTypeInfo::kIdlOtherType,
 };
 
 const WrapperTypeInfo& DOMDataView::wrapper_type_info_ =
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc b/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc
index 58b2b72..4029df7 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc
+++ b/third_party/blink/renderer/core/typed_arrays/dom_shared_array_buffer.cc
@@ -28,8 +28,7 @@
     kDOMWrappersTag,
     WrapperTypeInfo::kWrapperTypeObjectPrototype,
     WrapperTypeInfo::kObjectClassId,
-    WrapperTypeInfo::kNotInheritFromActiveScriptWrappable,
-    WrapperTypeInfo::kIdlBufferSourceType,
+    WrapperTypeInfo::kIdlOtherType,
 };
 
 const WrapperTypeInfo& DOMSharedArrayBuffer::wrapper_type_info_ =
diff --git a/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc b/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc
index 84e804c..c143c18 100644
--- a/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc
+++ b/third_party/blink/renderer/core/typed_arrays/dom_typed_array.cc
@@ -63,8 +63,7 @@
           kDOMWrappersTag,                                                     \
           WrapperTypeInfo::kWrapperTypeObjectPrototype,                        \
           WrapperTypeInfo::kObjectClassId,                                     \
-          WrapperTypeInfo::kNotInheritFromActiveScriptWrappable,               \
-          WrapperTypeInfo::kIdlBufferSourceType,                               \
+          WrapperTypeInfo::kIdlOtherType,                                      \
       };                                                                       \
   template <>                                                                  \
   const WrapperTypeInfo& DOMTypedArray<val_t, v8::Type##Array,                 \
diff --git a/third_party/blink/renderer/modules/hid/hid_device.cc b/third_party/blink/renderer/modules/hid/hid_device.cc
index 27fcf6f..41c20ab 100644
--- a/third_party/blink/renderer/modules/hid/hid_device.cc
+++ b/third_party/blink/renderer/modules/hid/hid_device.cc
@@ -323,9 +323,10 @@
   return promise;
 }
 
-ScriptPromise<IDLUndefined> HIDDevice::sendReport(ScriptState* script_state,
-                                                  uint8_t report_id,
-                                                  const DOMArrayPiece& data) {
+ScriptPromise<IDLUndefined> HIDDevice::sendReport(
+    ScriptState* script_state,
+    uint8_t report_id,
+    base::span<const uint8_t> data) {
   auto* resolver =
       MakeGarbageCollected<ScriptPromiseResolver<IDLUndefined>>(script_state);
   auto promise = resolver->Promise();
@@ -340,14 +341,14 @@
     return promise;
   }
 
-  if (!base::CheckedNumeric<wtf_size_t>(data.ByteLength()).IsValid()) {
+  if (!base::CheckedNumeric<wtf_size_t>(data.size()).IsValid()) {
     resolver->RejectWithDOMException(DOMExceptionCode::kNotSupportedError,
                                      kArrayBufferTooBig);
     return promise;
   }
 
   Vector<uint8_t> vector;
-  vector.AppendSpan(data.ByteSpan());
+  vector.AppendSpan(data);
 
   device_requests_.insert(resolver);
   connection_->Write(
@@ -360,7 +361,7 @@
 ScriptPromise<IDLUndefined> HIDDevice::sendFeatureReport(
     ScriptState* script_state,
     uint8_t report_id,
-    const DOMArrayPiece& data) {
+    base::span<const uint8_t> data) {
   auto* resolver =
       MakeGarbageCollected<ScriptPromiseResolver<IDLUndefined>>(script_state);
   auto promise = resolver->Promise();
@@ -375,14 +376,14 @@
     return promise;
   }
 
-  if (!base::CheckedNumeric<wtf_size_t>(data.ByteLength()).IsValid()) {
+  if (!base::CheckedNumeric<wtf_size_t>(data.size()).IsValid()) {
     resolver->RejectWithDOMException(DOMExceptionCode::kNotSupportedError,
                                      kArrayBufferTooBig);
     return promise;
   }
 
   Vector<uint8_t> vector;
-  vector.AppendSpan(data.ByteSpan());
+  vector.AppendSpan(data);
 
   device_requests_.insert(resolver);
   connection_->SendFeatureReport(
diff --git a/third_party/blink/renderer/modules/hid/hid_device.h b/third_party/blink/renderer/modules/hid/hid_device.h
index 7eee882..877ef1f 100644
--- a/third_party/blink/renderer/modules/hid/hid_device.h
+++ b/third_party/blink/renderer/modules/hid/hid_device.h
@@ -81,10 +81,10 @@
                                      ExceptionState& exception_state);
   ScriptPromise<IDLUndefined> sendReport(ScriptState*,
                                          uint8_t report_id,
-                                         const DOMArrayPiece& data);
+                                         base::span<const uint8_t> data);
   ScriptPromise<IDLUndefined> sendFeatureReport(ScriptState*,
                                                 uint8_t report_id,
-                                                const DOMArrayPiece& data);
+                                                base::span<const uint8_t> data);
   ScriptPromise<NotShared<DOMDataView>> receiveFeatureReport(ScriptState*,
                                                              uint8_t report_id);
 
diff --git a/third_party/blink/renderer/modules/hid/hid_device.idl b/third_party/blink/renderer/modules/hid/hid_device.idl
index 21785814..b749d32 100644
--- a/third_party/blink/renderer/modules/hid/hid_device.idl
+++ b/third_party/blink/renderer/modules/hid/hid_device.idl
@@ -54,7 +54,7 @@
         CallWith=ScriptState,
         MeasureAs=HidDeviceSendReport
     ] Promise<undefined> sendReport(
-        [EnforceRange] octet reportId, BufferSource data);
+        [EnforceRange] octet reportId, [PassAsSpan] BufferSource data);
 
     // Send a feature report to the device. Set |reportId| to the first byte of
     // the report and |data| to the remaining bytes. Set |reportId| to zero if
@@ -64,7 +64,7 @@
         CallWith=ScriptState,
         MeasureAs=HidDeviceSendFeatureReport
     ] Promise<undefined> sendFeatureReport(
-        [EnforceRange] octet reportId, BufferSource data);
+        [EnforceRange] octet reportId, [PassAsSpan] BufferSource data);
 
     // Request a feature report from the device. Set |reportId| to the ID of the
     // feature report to request, or zero if the device does not use report IDs.
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_cast_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_cast_button_element.cc
index e22bc6b..6a0a54a 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_cast_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_cast_button_element.cc
@@ -55,9 +55,6 @@
   if (ElementFromCenter(*this) != &MediaElement()) {
     SetIsWanted(false);
   }
-
-  base::UmaHistogramBoolean("Media.Controls.OverlayCastButtonIsCovered",
-                            !IsWanted());
 }
 
 void MediaControlCastButtonElement::UpdateDisplayType() {
diff --git a/third_party/blink/renderer/modules/webcodecs/array_buffer_util.cc b/third_party/blink/renderer/modules/webcodecs/array_buffer_util.cc
index 6bf03ab..4b93f4a 100644
--- a/third_party/blink/renderer/modules/webcodecs/array_buffer_util.cc
+++ b/third_party/blink/renderer/modules/webcodecs/array_buffer_util.cc
@@ -1,46 +1,32 @@
 // 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.
-
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "third_party/blink/renderer/modules/webcodecs/array_buffer_util.h"
 
 #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h"
 
 namespace blink {
 
-ArrayBufferContents PinArrayBufferContent(
+ArrayBufferContents PinSharedArrayBufferContent(
     const AllowSharedBufferSource* buffer_union) {
   ArrayBufferContents result;
   switch (buffer_union->GetContentType()) {
     case AllowSharedBufferSource::ContentType::kArrayBufferAllowShared: {
       auto* buffer = buffer_union->GetAsArrayBufferAllowShared();
-      if (buffer && !buffer->IsDetached()) {
-        if (buffer->IsShared()) {
-          buffer->Content()->ShareWith(result);
-        } else {
-          static_cast<blink::DOMArrayBuffer*>(buffer)
-              ->ShareNonSharedForInternalUse(result);
-        }
+      if (buffer && !buffer->IsDetached() && buffer->IsShared()) {
+        buffer->Content()->ShareWith(result);
       }
-      return result;
+      break;
     }
     case AllowSharedBufferSource::ContentType::kArrayBufferViewAllowShared: {
       auto* view = buffer_union->GetAsArrayBufferViewAllowShared().Get();
-      if (view && !view->IsDetached()) {
-        if (view->IsShared()) {
-          view->BufferShared()->Content()->ShareWith(result);
-        } else {
-          view->buffer()->ShareNonSharedForInternalUse(result);
-        }
+      if (view && !view->IsDetached() && view->IsShared()) {
+        view->BufferShared()->Content()->ShareWith(result);
       }
-      return result;
+      break;
     }
   }
+  return result;
 }
 
 ArrayBufferContents TransferArrayBufferForSpan(
@@ -88,10 +74,10 @@
       continue;
     }
 
-    auto* contents_data = static_cast<const uint8_t*>(contents.Data());
-    if (data_range.data() < contents_data ||
-        data_range.data() + data_range.size() >
-            contents_data + contents.DataLength()) {
+    auto contents_data = contents.ByteSpan();
+    if (contents_data.empty() || data_range.empty() ||
+        data_range.data() < contents_data.data() ||
+        &data_range.back() > &contents_data.back()) {
       // This array buffer doesn't contain `data_range`. Let's ignore it.
       continue;
     }
diff --git a/third_party/blink/renderer/modules/webcodecs/array_buffer_util.h b/third_party/blink/renderer/modules/webcodecs/array_buffer_util.h
index 98b2bac..f6390e4 100644
--- a/third_party/blink/renderer/modules/webcodecs/array_buffer_util.h
+++ b/third_party/blink/renderer/modules/webcodecs/array_buffer_util.h
@@ -49,7 +49,9 @@
 // Ensures that the underlying memory for `buffer_union` remains valid
 // (owned by a returned instance of ArrayBufferContents)
 // even if the buffer is detached or truncated by client activity.
-ArrayBufferContents PinArrayBufferContent(
+// Returns a valid ArrayBufferContents only for shared buffers, otherwise
+// returns an empty ArrayBufferContents.
+ArrayBufferContents PinSharedArrayBufferContent(
     const AllowSharedBufferSource* buffer_union);
 
 // 1. Check if any on the array buffers from the `transfer_list` contain
diff --git a/third_party/blink/renderer/modules/webcodecs/video_frame.cc b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
index 9a1151e..f34853c 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_frame.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_frame.cc
@@ -1297,12 +1297,16 @@
     const VideoFrameLayout& dest_layout) {
   auto* background_readback = BackgroundReadback::From(
       *ExecutionContext::From(resolver->GetScriptState()));
-  if (!background_readback)
+  if (!background_readback) {
     return false;
+  }
 
-  ArrayBufferContents contents = PinArrayBufferContent(destination);
-  if (!contents.DataLength())
+  ArrayBufferContents contents = PinSharedArrayBufferContent(destination);
+  if (!contents.IsValid() || !contents.DataLength()) {
+    // `contents` is empty, most likely destination isn't a shared buffer.
+    // Async copyTo() can't be used.
     return false;
+  }
 
   auto readback_done_handler =
       [](ArrayBufferContents contents,
@@ -1388,12 +1392,14 @@
     DCHECK(local_frame->HasSharedImage());
 
     if (base::FeatureList::IsEnabled(kVideoFrameAsyncCopyTo)) {
+      // Check if we can run copyTo() asynchronously.
       if (CopyToAsync(resolver, local_frame, src_rect, destination,
                       dest_layout)) {
         return promise;
       }
     }
 
+    // Async version didn't work, let's copy planes synchronously.
     if (!CopyTexturablePlanes(*local_frame, src_rect, dest_layout, buffer)) {
       exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
                                         "Failed to read VideoFrame data.");
diff --git a/third_party/blink/renderer/modules/xr/BUILD.gn b/third_party/blink/renderer/modules/xr/BUILD.gn
index eca19ca..70c6d52 100644
--- a/third_party/blink/renderer/modules/xr/BUILD.gn
+++ b/third_party/blink/renderer/modules/xr/BUILD.gn
@@ -115,6 +115,7 @@
     "xr_space.cc",
     "xr_space.h",
     "xr_sub_image.h",
+    "xr_swap_chain.h",
     "xr_system.cc",
     "xr_system.h",
     "xr_target_ray_space.cc",
@@ -136,7 +137,13 @@
     "xr_webgl_depth_information.h",
     "xr_webgl_layer.cc",
     "xr_webgl_layer.h",
+    "xr_webgl_layer_client.h",
+    "xr_webgl_projection_layer.cc",
+    "xr_webgl_projection_layer.h",
+    "xr_webgl_sub_image.cc",
     "xr_webgl_sub_image.h",
+    "xr_webgl_swap_chain.cc",
+    "xr_webgl_swap_chain.h",
   ]
 
   deps = [
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
index 797e4e6..6a9e396 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
@@ -23,10 +23,14 @@
 #include "third_party/blink/renderer/modules/xr/xr_gpu_binding.h"
 #include "third_party/blink/renderer/modules/xr/xr_gpu_projection_layer.h"
 #include "third_party/blink/renderer/modules/xr/xr_gpu_swap_chain.h"
+#include "third_party/blink/renderer/modules/xr/xr_graphics_binding.h"
+#include "third_party/blink/renderer/modules/xr/xr_projection_layer.h"
 #include "third_party/blink/renderer/modules/xr/xr_session.h"
 #include "third_party/blink/renderer/modules/xr/xr_system.h"
 #include "third_party/blink/renderer/modules/xr/xr_viewport.h"
 #include "third_party/blink/renderer/modules/xr/xr_webgl_layer.h"
+#include "third_party/blink/renderer/modules/xr/xr_webgl_layer_client.h"
+#include "third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "ui/display/display.h"
@@ -629,9 +633,14 @@
   return high_res_now_ms;
 }
 
-void XRFrameProvider::SubmitWebGLLayer(XRWebGLLayer* layer, bool was_changed) {
-  CHECK(layer);
+void XRFrameProvider::SubmitWebGLLayer(XRWebGLLayerClient* layer_client,
+                                       bool was_changed) {
+  CHECK(layer_client);
   CHECK(immersive_session_);
+
+  const XRLayer* layer = layer_client->layer();
+  CHECK(layer);
+
   CHECK_EQ(layer->session(), immersive_session_);
   CHECK_EQ(layer->session()->GraphicsApi(), XRGraphicsBinding::Api::kWebGL);
   if (!immersive_presentation_provider_.is_bound())
@@ -640,7 +649,7 @@
   TRACE_EVENT1("gpu", "XRFrameProvider::SubmitWebGLLayer", "frame", frame_id_);
   DVLOG(3) << __func__ << ": frame=" << frame_id_;
 
-  WebGLRenderingContextBase* webgl_context = layer->context();
+  WebGLRenderingContextBase* webgl_context = layer_client->context();
 
   if (frame_id_ < 0) {
     // There is no valid frame_id_, and the browser side is not currently
@@ -681,7 +690,7 @@
   }
 
   scoped_refptr<StaticBitmapImage> image_ref =
-      layer->TransferToStaticBitmapImage();
+      layer_client->TransferToStaticBitmapImage();
 
   if (!image_ref)
     return;
@@ -802,15 +811,17 @@
 
 // TODO(bajones): This only works because we're restricted to a single layer at
 // the moment. Will need an overhaul when we get more robust layering support.
-void XRFrameProvider::UpdateWebGPULayerViewports(XRGPUProjectionLayer* layer) {
+void XRFrameProvider::UpdateLayerViewports(XRProjectionLayer* layer) {
   DCHECK(layer->session() == immersive_session_);
-  DCHECK(layer->session()->GraphicsApi() == XRGraphicsBinding::Api::kWebGPU);
   DCHECK(immersive_presentation_provider_.is_bound());
 
-  XRGPUBinding* webgpu_binding = static_cast<XRGPUBinding*>(layer->binding());
+  XRGraphicsBinding* binding = layer->binding();
 
-  // TODO(crbug.com/359418629): Adjust viewport calculations once we start using
-  // texture array-capable mailboxes.
+  // TODO(crbug.com/359418629): Currently we have no way to submit texture
+  // arrays to the compositor, so any array textures produced by the page will
+  // be copied to a side-by-side texture prior to submission. That does mean
+  // that we need to adjust the viewports from those reported to the page,
+  // however, by altering the texture width here...
   float width = layer->textureWidth() * layer->textureArrayLength();
   float height = layer->textureHeight();
 
@@ -822,16 +833,20 @@
     XRViewData* right_view =
         immersive_session_->ViewDataForEye(device::mojom::blink::XREye::kRight);
 
-    gfx::Rect left = webgpu_binding->GetViewportForView(layer, left_view);
-    gfx::Rect right = webgpu_binding->GetViewportForView(layer, right_view);
-    right.set_x(right.x() + layer->textureWidth());
+    gfx::Rect left = binding->GetViewportForView(layer, left_view);
+    gfx::Rect right = binding->GetViewportForView(layer, right_view);
+
+    // (continued from prior comment) ...and offsetting the viewports here.
+    if (layer->textureArrayLength() > 1) {
+      right.set_x(right.x() + layer->textureWidth());
+    }
 
     left_coords = NormalizeViewport(left, width, height);
     right_coords = NormalizeViewport(right, width, height);
   } else {
     XRViewData* mono_view =
         immersive_session_->ViewDataForEye(device::mojom::blink::XREye::kNone);
-    gfx::Rect viewport = webgpu_binding->GetViewportForView(layer, mono_view);
+    gfx::Rect viewport = binding->GetViewportForView(layer, mono_view);
 
     left_coords = NormalizeViewport(viewport, width, height);
 
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.h b/third_party/blink/renderer/modules/xr/xr_frame_provider.h
index 2d75ce3689..0fa6d06 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.h
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.h
@@ -26,9 +26,11 @@
 class LocalDOMWindow;
 class XRFrameTransport;
 class XRGPUProjectionLayer;
+class XRProjectionLayer;
 class XRSession;
 class XRSystem;
 class XRWebGLLayer;
+class XRWebGLLayerClient;
 
 // This class manages requesting and dispatching frame updates, which includes
 // pose information for a given XRDevice.
@@ -60,11 +62,13 @@
 
   void OnNonImmersiveVSync(double high_res_now_ms);
 
-  void SubmitWebGLLayer(XRWebGLLayer*, bool was_changed);
+  void SubmitWebGLLayer(XRWebGLLayerClient*, bool was_changed);
   void UpdateWebGLLayerViewports(XRWebGLLayer*);
 
   void SubmitWebGPULayer(XRGPUProjectionLayer*, bool was_queried);
-  void UpdateWebGPULayerViewports(XRGPUProjectionLayer*);
+
+  // Used for both WebGPU and WebGL layers.
+  void UpdateLayerViewports(XRProjectionLayer*);
 
   void Dispose();
   void OnFocusChanged();
diff --git a/third_party/blink/renderer/modules/xr/xr_gpu_binding.h b/third_party/blink/renderer/modules/xr/xr_gpu_binding.h
index 42b05595..8104244 100644
--- a/third_party/blink/renderer/modules/xr/xr_gpu_binding.h
+++ b/third_party/blink/renderer/modules/xr/xr_gpu_binding.h
@@ -52,7 +52,8 @@
 
   GPUDevice* device() const { return device_.Get(); }
 
-  gfx::Rect GetViewportForView(XRProjectionLayer* layer, XRViewData* view);
+  gfx::Rect GetViewportForView(XRProjectionLayer* layer,
+                               XRViewData* view) override;
 
   void Trace(Visitor*) const override;
 
diff --git a/third_party/blink/renderer/modules/xr/xr_gpu_projection_layer.cc b/third_party/blink/renderer/modules/xr/xr_gpu_projection_layer.cc
index 41b49d5..aeb2b2e 100644
--- a/third_party/blink/renderer/modules/xr/xr_gpu_projection_layer.cc
+++ b/third_party/blink/renderer/modules/xr/xr_gpu_projection_layer.cc
@@ -61,7 +61,7 @@
   XRFrameProvider* frame_provider = session()->xr()->frameProvider();
 
   if (viewport_updated_) {
-    frame_provider->UpdateWebGPULayerViewports(this);
+    frame_provider->UpdateLayerViewports(this);
     viewport_updated_ = false;
   }
 
diff --git a/third_party/blink/renderer/modules/xr/xr_gpu_swap_chain.cc b/third_party/blink/renderer/modules/xr/xr_gpu_swap_chain.cc
index 213e9495..30121c7 100644
--- a/third_party/blink/renderer/modules/xr/xr_gpu_swap_chain.cc
+++ b/third_party/blink/renderer/modules/xr/xr_gpu_swap_chain.cc
@@ -5,8 +5,6 @@
 #include "third_party/blink/renderer/modules/xr/xr_gpu_swap_chain.h"
 
 #include "third_party/blink/renderer/modules/webgpu/gpu_device.h"
-#include "third_party/blink/renderer/modules/webgpu/gpu_texture.h"
-#include "third_party/blink/renderer/modules/xr/xr_composition_layer.h"
 #include "third_party/blink/renderer/modules/xr/xr_layer_shared_image_manager.h"
 
 namespace blink {
@@ -39,41 +37,19 @@
   CHECK(device);
 }
 
-void XRGPUSwapChain::OnFrameStart() {
-  texture_queried_ = false;
-}
-void XRGPUSwapChain::OnFrameEnd() {
-  ResetCurrentTexture();
-}
-
-GPUTexture* XRGPUSwapChain::GetCurrentTexture() {
-  texture_queried_ = true;
-  if (!current_texture_) {
-    current_texture_ = ProduceTexture();
-  }
-  return current_texture_;
-}
-
-// Resets the cached texture so that next GetCurrentTexture call will trigger a
-// ProduceTexture call.
-GPUTexture* XRGPUSwapChain::ResetCurrentTexture() {
-  GPUTexture* texture = current_texture_.Get();
-  current_texture_ = nullptr;
-  return texture;
-}
-
 // Clears the contents of the current texture to transparent black or 0 (for
 // depth/stencil textures).
 void XRGPUSwapChain::ClearCurrentTexture(wgpu::CommandEncoder command_encoder) {
-  if (!current_texture_) {
+  GPUTexture* texture = current_texture();
+  if (!texture) {
     return;
   }
 
-  bool hasDepth = IsDepthFormat(current_texture_->Format());
-  bool hasStencil = IsStencilFormat(current_texture_->Format());
+  bool hasDepth = IsDepthFormat(texture->Format());
+  bool hasStencil = IsStencilFormat(texture->Format());
 
   // Clear each level of the texture array.
-  for (uint32_t i = 0; i < current_texture_->depthOrArrayLayers(); ++i) {
+  for (uint32_t i = 0; i < texture->depthOrArrayLayers(); ++i) {
     wgpu::TextureViewDescriptor view_desc = {
         .dimension = wgpu::TextureViewDimension::e2D,
         .baseMipLevel = 0,
@@ -82,8 +58,7 @@
         .arrayLayerCount = 1,
     };
 
-    wgpu::TextureView view =
-        current_texture_->GetHandle().CreateView(&view_desc);
+    wgpu::TextureView view = texture->GetHandle().CreateView(&view_desc);
 
     wgpu::RenderPassEncoder render_pass;
     if (hasDepth || hasStencil) {
@@ -131,8 +106,7 @@
 
 void XRGPUSwapChain::Trace(Visitor* visitor) const {
   visitor->Trace(device_);
-  visitor->Trace(current_texture_);
-  visitor->Trace(layer_);
+  XRSwapChain::Trace(visitor);
 }
 
 XRGPUStaticSwapChain::XRGPUStaticSwapChain(GPUDevice* device,
diff --git a/third_party/blink/renderer/modules/xr/xr_gpu_swap_chain.h b/third_party/blink/renderer/modules/xr/xr_gpu_swap_chain.h
index c707ea7..da24ae3 100644
--- a/third_party/blink/renderer/modules/xr/xr_gpu_swap_chain.h
+++ b/third_party/blink/renderer/modules/xr/xr_gpu_swap_chain.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_GPU_SWAP_CHAIN_H_
 #define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_GPU_SWAP_CHAIN_H_
 
+#include "third_party/blink/renderer/modules/webgpu/gpu_texture.h"
+#include "third_party/blink/renderer/modules/xr/xr_swap_chain.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
@@ -13,39 +15,22 @@
 
 class GPUDevice;
 class GPUTexture;
-class XRCompositionLayer;
 
-class XRGPUSwapChain : public GarbageCollected<XRGPUSwapChain> {
+class XRGPUSwapChain : public XRSwapChain<GPUTexture> {
  public:
   explicit XRGPUSwapChain(GPUDevice*);
-  virtual ~XRGPUSwapChain() = default;
-
-  GPUTexture* GetCurrentTexture();
-  virtual void OnFrameStart();
-  virtual void OnFrameEnd();
-
-  virtual const wgpu::TextureDescriptor& descriptor() const = 0;
+  ~XRGPUSwapChain() override = default;
 
   GPUDevice* device() { return device_.Get(); }
+  virtual const wgpu::TextureDescriptor& descriptor() const = 0;
 
-  virtual void SetLayer(XRCompositionLayer* layer) { layer_ = layer; }
-  XRCompositionLayer* layer() { return layer_.Get(); }
-
-  bool texture_was_queried() const { return texture_queried_; }
-
-  virtual void Trace(Visitor* visitor) const;
+  void Trace(Visitor* visitor) const override;
 
  protected:
-  virtual GPUTexture* ProduceTexture() = 0;
-
-  GPUTexture* ResetCurrentTexture();
   void ClearCurrentTexture(wgpu::CommandEncoder);
 
  private:
   Member<GPUDevice> device_;
-  Member<GPUTexture> current_texture_;
-  Member<XRCompositionLayer> layer_;
-  bool texture_queried_;
 };
 
 // A texture swap chain that is not communicated back to the compositor, used
diff --git a/third_party/blink/renderer/modules/xr/xr_graphics_binding.h b/third_party/blink/renderer/modules/xr/xr_graphics_binding.h
index 85ab25c..b108ce2 100644
--- a/third_party/blink/renderer/modules/xr/xr_graphics_binding.h
+++ b/third_party/blink/renderer/modules/xr/xr_graphics_binding.h
@@ -8,10 +8,16 @@
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
 
+namespace gfx {
+class Rect;
+}  // namespace gfx
+
 namespace blink {
 
 class XRCompositionLayer;
+class XRProjectionLayer;
 class XRSession;
+class XRViewData;
 
 // Base class for XRWebGLBinding and XRGPUBinding, which helps facilitate type
 // checking when layers are passed in to get sub images.
@@ -28,6 +34,9 @@
 
   bool OwnsLayer(XRCompositionLayer*);
 
+  virtual gfx::Rect GetViewportForView(XRProjectionLayer* layer,
+                                       XRViewData* view) = 0;
+
   void Trace(Visitor*) const override;
 
  private:
diff --git a/third_party/blink/renderer/modules/xr/xr_swap_chain.h b/third_party/blink/renderer/modules/xr/xr_swap_chain.h
new file mode 100644
index 0000000..953da37
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_swap_chain.h
@@ -0,0 +1,77 @@
+// Copyright 2025 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_MODULES_XR_XR_SWAP_CHAIN_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_SWAP_CHAIN_H_
+
+#include "third_party/blink/renderer/modules/xr/xr_composition_layer.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+
+namespace blink {
+
+// An XRSwapChain manages the creation and lifetime of textures used by an
+// XRCompositorLayer. The general lifetime is expected to be:
+//
+// OnFrameStart() - Called prior to the XRFrame callback for any active layers
+// GetCurrentTexture() - Called when a sub image is requested for the layer
+//  > ProduceTexture() - Called by GetCurrentTexture if a new texture is needed
+// OnFrameEnd() - Called prior to the frame submission
+//  > ResetCurrentTexture() - Called by OnFrameEnd if a new texture is required
+//                            for the next frame.
+
+template <typename Texture>
+class XRSwapChain : public GarbageCollected<XRSwapChain<Texture>> {
+ public:
+  XRSwapChain() {}
+  virtual ~XRSwapChain() = default;
+
+  Texture* GetCurrentTexture() {
+    texture_queried_ = true;
+    if (!current_texture_) {
+      current_texture_ = ProduceTexture();
+    }
+    return current_texture_;
+  }
+  virtual void OnFrameStart() { texture_queried_ = false; }
+  virtual void OnFrameEnd() { ResetCurrentTexture(); }
+
+  // Manage the XRCompositorLayer this swap chain is associated with.
+  virtual void SetLayer(XRCompositionLayer* layer) { layer_ = layer; }
+  XRCompositionLayer* layer() { return layer_.Get(); }
+
+  // Indicates if the texture was queried during the most recent frame.
+  bool texture_was_queried() const { return texture_queried_; }
+
+  virtual void Trace(Visitor* visitor) const {
+    visitor->Trace(current_texture_);
+    visitor->Trace(layer_);
+  }
+
+ protected:
+  // Produces a new Texture for the swap chain. Will not be called again unless
+  // the current texture is reset.
+  virtual Texture* ProduceTexture() = 0;
+
+  // Resets the cached texture so that next GetCurrentTexture call will trigger
+  // a ProduceTexture call.
+  Texture* ResetCurrentTexture() {
+    Texture* texture = current_texture_.Get();
+    current_texture_ = nullptr;
+    return texture;
+  }
+
+  // Return the current texture if one has been produced and not reset.
+  Texture* current_texture() { return current_texture_; }
+
+ private:
+  Member<Texture> current_texture_;
+  Member<XRCompositionLayer> layer_;
+
+  bool texture_queried_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_SWAP_CHAIN_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc b/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc
index 5cd8144..abfb1bab 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_binding.cc
@@ -19,12 +19,22 @@
 #include "third_party/blink/renderer/modules/xr/xr_utils.h"
 #include "third_party/blink/renderer/modules/xr/xr_viewer_pose.h"
 #include "third_party/blink/renderer/modules/xr/xr_webgl_layer.h"
+#include "third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.h"
 #include "third_party/blink/renderer/modules/xr/xr_webgl_sub_image.h"
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/size_conversions.h"
+#include "ui/gfx/geometry/size_f.h"
 
 namespace blink {
 
+namespace {
+
+const double kMinScaleFactor = 0.2;
+
+}  // namespace
+
 XRWebGLBinding* XRWebGLBinding::Create(XRSession* session,
                                        const V8XRWebGLRenderingContext* context,
                                        ExceptionState& exception_state) {
@@ -85,16 +95,114 @@
 XRProjectionLayer* XRWebGLBinding::createProjectionLayer(
     const XRProjectionLayerInit* init,
     ExceptionState& exception_state) {
-  NOTIMPLEMENTED();
-  return nullptr;
+  if (!CanCreateLayer(exception_state) ||
+      !ValidateLayerColorFormat(init->colorFormat(), exception_state) ||
+      !ValidateLayerDepthStencilFormat(init->depthFormat(), exception_state)) {
+    return nullptr;
+  }
+
+  // The max size will be either the native resolution or the default
+  // if that happens to be larger than the native res. (That can happen on
+  // desktop systems.)
+  double max_scale = std::max(session()->NativeFramebufferScale(), 1.0);
+
+  // Clamp the developer-requested framebuffer scale to ensure it's not too
+  // small to see or unreasonably large.
+  double scale_factor =
+      std::clamp(init->scaleFactor(), kMinScaleFactor, max_scale);
+  gfx::SizeF scaled_size =
+      gfx::ScaleSize(session()->RecommendedArrayTextureSize(), scale_factor);
+
+  // TODO(crbug.com/359418629): Remove once array Mailboxes are available.
+  scaled_size.set_width(scaled_size.width() *
+                        session()->array_texture_layers());
+
+  // If the scaled texture dimensions are larger than the max texture dimension
+  // for the context scale it down till it fits.
+  GLint max_texture_size = 0;
+  webgl_context_->ContextGL()->GetIntegerv(GL_MAX_TEXTURE_SIZE,
+                                           &max_texture_size);
+  if (scaled_size.width() > max_texture_size ||
+      scaled_size.height() > max_texture_size) {
+    double max_dimension = std::max(scaled_size.width(), scaled_size.height());
+    scaled_size = gfx::ScaleSize(scaled_size, max_texture_size / max_dimension);
+  }
+
+  gfx::Size texture_size = gfx::ToFlooredSize(scaled_size);
+
+  XRWebGLSwapChain::Descriptor color_desc = {};
+  color_desc.format = FormatForLayerFormat(init->colorFormat());
+  color_desc.internal_format =
+      InternalFormatForLayerFormat(init->colorFormat());
+  color_desc.type = TypeForLayerFormat(init->colorFormat());
+  color_desc.attachment_target = GL_COLOR_ATTACHMENT0;
+  color_desc.width = static_cast<uint32_t>(texture_size.width());
+  color_desc.height = static_cast<uint32_t>(texture_size.height());
+  color_desc.depth = 1;
+
+  XRWebGLSharedImageSwapChain* color_swap_chain =
+      MakeGarbageCollected<XRWebGLSharedImageSwapChain>(webgl_context_,
+                                                        color_desc, webgl2_);
+
+  // TODO(crbug.com/40700985): Return a wrapped swap chain for texture-array
+  // layers, like with the WebGPU layers.
+
+  XRWebGLStaticSwapChain* depth_stencil_swap_chain = nullptr;
+  if (init->depthFormat() != GL_NONE) {
+    XRWebGLSwapChain::Descriptor depth_stencil_desc = {};
+    depth_stencil_desc.format = FormatForLayerFormat(init->depthFormat());
+    depth_stencil_desc.internal_format =
+        InternalFormatForLayerFormat(init->depthFormat());
+    depth_stencil_desc.type = TypeForLayerFormat(init->depthFormat());
+    depth_stencil_desc.attachment_target = GL_DEPTH_ATTACHMENT;
+    depth_stencil_desc.width = static_cast<uint32_t>(texture_size.width());
+    depth_stencil_desc.height = static_cast<uint32_t>(texture_size.height());
+    depth_stencil_desc.depth = 1;
+
+    depth_stencil_swap_chain = MakeGarbageCollected<XRWebGLStaticSwapChain>(
+        webgl_context_, depth_stencil_desc, webgl2_);
+  }
+
+  return MakeGarbageCollected<XRWebGLProjectionLayer>(this, color_swap_chain,
+                                                      depth_stencil_swap_chain);
 }
 
 XRWebGLSubImage* XRWebGLBinding::getViewSubImage(
     XRProjectionLayer* layer,
     XRView* view,
     ExceptionState& exception_state) {
-  NOTIMPLEMENTED();
-  return nullptr;
+  CHECK(layer);
+  CHECK(view);
+  if (!OwnsLayer(layer)) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        "Layer was not created with this binding.");
+    return nullptr;
+  }
+
+  if (!view || view->session() != session()) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        "View was not created with the same session as this binding.");
+    return nullptr;
+  }
+
+  // Because we have validated that this is a layer owned by this binding we
+  // know that it is a XRWebGLProjectionLayer, because that's the only type of
+  // projection layer that this class returns.
+  XRWebGLProjectionLayer* gl_layer =
+      static_cast<XRWebGLProjectionLayer*>(layer);
+
+  XRViewData* viewData = view->ViewData();
+  if (viewData->ApplyViewportScaleForFrame()) {
+    gl_layer->MarkViewportUpdated();
+  }
+
+  gfx::Rect viewport = GetViewportForView(layer, viewData);
+
+  return MakeGarbageCollected<XRWebGLSubImage>(
+      viewport, viewData->index(), gl_layer->color_swap_chain(),
+      gl_layer->depth_stencil_swap_chain(), nullptr);
 }
 
 WebGLTexture* XRWebGLBinding::getReflectionCubeMap(
@@ -264,6 +372,183 @@
   return view->GetWebGLDepthInformation(exception_state);
 }
 
+gfx::Rect XRWebGLBinding::GetViewportForView(XRProjectionLayer* layer,
+                                             XRViewData* view) {
+  CHECK(OwnsLayer(layer));
+
+  // If the layer is not side-by-side return the full texture size adjusted by
+  // the viewport scale.
+  if (layer->textureArrayLength() > 1) {
+    return gfx::Rect(0, 0, layer->textureWidth() * view->CurrentViewportScale(),
+                     layer->textureHeight() * view->CurrentViewportScale());
+  }
+
+  // Otherwise the layer is side-by-side, so the viewports should be distributed
+  // across the texture width.
+  uint32_t viewport_width =
+      layer->textureWidth() / session()->array_texture_layers();
+  uint32_t viewport_offset = viewport_width * view->index();
+  return gfx::Rect(viewport_offset, 0,
+                   viewport_width * view->CurrentViewportScale(),
+                   layer->textureHeight() * view->CurrentViewportScale());
+}
+
+bool XRWebGLBinding::CanCreateLayer(ExceptionState& exception_state) {
+  if (session()->ended()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "Cannot create a new layer for an "
+                                      "XRSession which has already ended.");
+    return false;
+  }
+
+  if (webgl_context_->isContextLost()) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "Cannot create a new layer with a lost "
+                                      "WebGL context.");
+    return false;
+  }
+
+  return true;
+}
+
+bool XRWebGLBinding::ValidateLayerColorFormat(GLenum color_format,
+                                              ExceptionState& exception_state) {
+  switch (color_format) {
+    case GL_RGBA:
+    case GL_RGB:
+    case GL_SRGB_EXT:
+    case GL_SRGB_ALPHA_EXT:
+      return true;
+    case GL_RGBA8:
+    case GL_RGB8:
+    case GL_SRGB8:
+    case GL_SRGB8_ALPHA8:
+      if (!webgl2_) {
+        exception_state.ThrowDOMException(
+            DOMExceptionCode::kInvalidStateError,
+            "Specified colorFormat only available with WebGL 2 contexts.");
+        return false;
+      }
+      return true;
+    default:
+      exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                        "Invalid colorFormat.");
+      return false;
+  }
+}
+
+bool XRWebGLBinding::ValidateLayerDepthStencilFormat(
+    GLenum depth_stencil_format,
+    ExceptionState& exception_state) {
+  switch (depth_stencil_format) {
+    case GL_NONE:
+    case GL_DEPTH_COMPONENT:
+    case GL_DEPTH_STENCIL:
+      return true;
+    case GL_DEPTH_COMPONENT24:
+    case GL_DEPTH24_STENCIL8:
+      if (!webgl2_) {
+        exception_state.ThrowDOMException(
+            DOMExceptionCode::kInvalidStateError,
+            "Specified depthFormat only available with WebGL 2 contexts.");
+        return false;
+      }
+      return true;
+    default:
+      exception_state.ThrowDOMException(
+          DOMExceptionCode::kInvalidStateError,
+          "depthFormat must be a valid depth format or GL_NONE.");
+      return false;
+  }
+}
+
+GLenum XRWebGLBinding::FormatForLayerFormat(GLenum layer_format) {
+  switch (layer_format) {
+    case GL_RGBA:
+    case GL_RGBA8:
+      return GL_RGBA;
+
+    case GL_RGB:
+    case GL_RGB8:
+      return GL_RGB;
+
+    case GL_SRGB_EXT:
+    case GL_SRGB8:
+      return GL_SRGB_EXT;
+
+    case GL_SRGB_ALPHA_EXT:
+    case GL_SRGB8_ALPHA8:
+      return GL_SRGB_ALPHA_EXT;
+
+    case GL_DEPTH_COMPONENT:
+    case GL_DEPTH_COMPONENT24:
+      return GL_DEPTH_COMPONENT;
+
+    case GL_DEPTH_STENCIL:
+    case GL_DEPTH24_STENCIL8:
+      return GL_DEPTH_STENCIL;
+
+    default:
+      NOTREACHED();
+  }
+}
+
+GLenum XRWebGLBinding::InternalFormatForLayerFormat(GLenum layer_format) {
+  switch (layer_format) {
+    case GL_RGBA:
+    case GL_RGBA8:
+      return GL_RGBA8;
+
+    case GL_RGB:
+    case GL_RGB8:
+      return GL_RGB8;
+
+    case GL_SRGB_EXT:
+    case GL_SRGB8:
+      return GL_SRGB8;
+
+    case GL_SRGB_ALPHA_EXT:
+    case GL_SRGB8_ALPHA8:
+      return GL_SRGB8_ALPHA8;
+
+    case GL_DEPTH_COMPONENT:
+    case GL_DEPTH_COMPONENT24:
+      return GL_DEPTH_COMPONENT24;
+
+    case GL_DEPTH_STENCIL:
+    case GL_DEPTH24_STENCIL8:
+      return GL_DEPTH24_STENCIL8;
+
+    default:
+      NOTREACHED();
+  }
+}
+
+GLenum XRWebGLBinding::TypeForLayerFormat(GLenum layer_format) {
+  switch (layer_format) {
+    case GL_RGBA:
+    case GL_RGBA8:
+    case GL_RGB:
+    case GL_RGB8:
+    case GL_SRGB_EXT:
+    case GL_SRGB8:
+    case GL_SRGB_ALPHA_EXT:
+    case GL_SRGB8_ALPHA8:
+      return GL_BYTE;
+
+    case GL_DEPTH_COMPONENT:
+    case GL_DEPTH_STENCIL:
+      return GL_UNSIGNED_SHORT;
+
+    case GL_DEPTH_COMPONENT24:
+    case GL_DEPTH24_STENCIL8:
+      return GL_UNSIGNED_INT;
+
+    default:
+      NOTREACHED();
+  }
+}
+
 void XRWebGLBinding::Trace(Visitor* visitor) const {
   visitor->Trace(webgl_context_);
   XRGraphicsBinding::Trace(visitor);
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_binding.h b/third_party/blink/renderer/modules/xr/xr_webgl_binding.h
index 3699c84..d7cf503 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_binding.h
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_binding.h
@@ -52,9 +52,23 @@
   XRWebGLDepthInformation* getDepthInformation(XRView* view,
                                                ExceptionState& exception_state);
 
+  gfx::Rect GetViewportForView(XRProjectionLayer* layer,
+                               XRViewData* view) override;
+
+  WebGLRenderingContextBase* context() const { return webgl_context_.Get(); }
+
   void Trace(Visitor*) const override;
 
  private:
+  bool CanCreateLayer(ExceptionState& exception_state);
+  bool ValidateLayerColorFormat(GLenum color_format,
+                                ExceptionState& exception_state);
+  bool ValidateLayerDepthStencilFormat(GLenum depth_stencil_format,
+                                       ExceptionState& exception_state);
+  GLenum FormatForLayerFormat(GLenum format);
+  GLenum InternalFormatForLayerFormat(GLenum format);
+  GLenum TypeForLayerFormat(GLenum format);
+
   Member<WebGLRenderingContextBase> webgl_context_;
   bool webgl2_;
 };
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer.h b/third_party/blink/renderer/modules/xr/xr_webgl_layer.h
index 5458dab1..2a5f760 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_layer.h
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer.h
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/modules/xr/xr_layer.h"
 #include "third_party/blink/renderer/modules/xr/xr_utils.h"
 #include "third_party/blink/renderer/modules/xr/xr_view.h"
+#include "third_party/blink/renderer/modules/xr/xr_webgl_layer_client.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h"
 #include "third_party/blink/renderer/platform/wtf/ref_counted.h"
@@ -27,7 +28,7 @@
 class XRSession;
 class XRViewport;
 
-class XRWebGLLayer final : public XRLayer {
+class XRWebGLLayer final : public XRLayer, public XRWebGLLayerClient {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
@@ -44,7 +45,12 @@
                               const XRWebGLLayerInit*,
                               ExceptionState&);
 
-  WebGLRenderingContextBase* context() const { return webgl_context_.Get(); }
+  // XRWebGLLayerClient implementation
+  const XRLayer* layer() const override { return this; }
+  WebGLRenderingContextBase* context() const override {
+    return webgl_context_.Get();
+  }
+  scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage() override;
 
   WebGLFramebuffer* framebuffer() const { return framebuffer_.Get(); }
   uint32_t framebufferWidth() const;
@@ -80,8 +86,6 @@
   // mailbox holder and its size respectively.
   void HandleBackgroundImage(const gpu::MailboxHolder&, const gfx::Size&) {}
 
-  scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage();
-
   void Trace(Visitor*) const override;
 
  private:
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_layer_client.h b/third_party/blink/renderer/modules/xr/xr_webgl_layer_client.h
new file mode 100644
index 0000000..892e8ee9
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_layer_client.h
@@ -0,0 +1,27 @@
+// Copyright 2025 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_MODULES_XR_XR_WEBGL_LAYER_CLIENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_LAYER_CLIENT_H_
+
+#include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
+#include "third_party/blink/renderer/platform/wtf/ref_counted.h"
+
+namespace blink {
+
+class WebGLRenderingContextBase;
+class XRLayer;
+
+// Shared interface for XRWebGLLayer and WebGL-based XRProjectionLayers to
+// submit frames with.
+class XRWebGLLayerClient {
+ public:
+  virtual const XRLayer* layer() const = 0;
+  virtual WebGLRenderingContextBase* context() const = 0;
+  virtual scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage() = 0;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_LAYER_CLIENT_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.cc b/third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.cc
new file mode 100644
index 0000000..225834a3
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.cc
@@ -0,0 +1,78 @@
+// Copyright 2025 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/modules/xr/xr_webgl_projection_layer.h"
+
+#include "third_party/blink/renderer/bindings/modules/v8/v8_xr_gpu_projection_layer_init.h"
+#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h"
+#include "third_party/blink/renderer/modules/xr/xr_frame_provider.h"
+#include "third_party/blink/renderer/modules/xr/xr_session.h"
+#include "third_party/blink/renderer/modules/xr/xr_system.h"
+#include "third_party/blink/renderer/modules/xr/xr_webgl_binding.h"
+#include "third_party/blink/renderer/modules/xr/xr_webgl_swap_chain.h"
+#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
+
+namespace blink {
+
+XRWebGLProjectionLayer::XRWebGLProjectionLayer(
+    XRWebGLBinding* binding,
+    XRWebGLSwapChain* color_swap_chain,
+    XRWebGLSwapChain* depth_stencil_swap_chain)
+    : XRProjectionLayer(binding),
+      webgl_context_(binding->context()),
+      color_swap_chain_(color_swap_chain),
+      depth_stencil_swap_chain_(depth_stencil_swap_chain) {
+  CHECK(color_swap_chain_);
+  color_swap_chain_->SetLayer(this);
+  if (depth_stencil_swap_chain_) {
+    depth_stencil_swap_chain_->SetLayer(this);
+  }
+}
+
+uint16_t XRWebGLProjectionLayer::textureWidth() const {
+  return color_swap_chain_->descriptor().width;
+}
+
+uint16_t XRWebGLProjectionLayer::textureHeight() const {
+  return color_swap_chain_->descriptor().height;
+}
+
+uint16_t XRWebGLProjectionLayer::textureArrayLength() const {
+  return color_swap_chain_->descriptor().depth;
+}
+
+void XRWebGLProjectionLayer::OnFrameStart() {
+  color_swap_chain_->OnFrameStart();
+  if (depth_stencil_swap_chain_) {
+    depth_stencil_swap_chain_->OnFrameStart();
+  }
+}
+
+void XRWebGLProjectionLayer::OnFrameEnd() {
+  color_swap_chain_->OnFrameEnd();
+  if (depth_stencil_swap_chain_) {
+    depth_stencil_swap_chain_->OnFrameEnd();
+  }
+
+  XRFrameProvider* frame_provider = session()->xr()->frameProvider();
+
+  if (viewport_updated_) {
+    frame_provider->UpdateLayerViewports(this);
+    viewport_updated_ = false;
+  }
+
+  frame_provider->SubmitWebGLLayer(this,
+                                   color_swap_chain_->texture_was_queried());
+}
+
+void XRWebGLProjectionLayer::OnResize() {}
+
+void XRWebGLProjectionLayer::Trace(Visitor* visitor) const {
+  visitor->Trace(webgl_context_);
+  visitor->Trace(color_swap_chain_);
+  visitor->Trace(depth_stencil_swap_chain_);
+  XRProjectionLayer::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.h b/third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.h
new file mode 100644
index 0000000..238202a89
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_projection_layer.h
@@ -0,0 +1,60 @@
+// Copyright 2025 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_MODULES_XR_XR_WEBGL_PROJECTION_LAYER_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_PROJECTION_LAYER_H_
+
+#include "third_party/blink/renderer/modules/xr/xr_projection_layer.h"
+#include "third_party/blink/renderer/modules/xr/xr_webgl_layer_client.h"
+
+namespace blink {
+
+class WebGLRenderingContextBase;
+class XRWebGLBinding;
+class XRWebGLSwapChain;
+
+class XRWebGLProjectionLayer final : public XRProjectionLayer,
+                                     public XRWebGLLayerClient {
+ public:
+  XRWebGLProjectionLayer(XRWebGLBinding*,
+                         XRWebGLSwapChain* color_swap_chain,
+                         XRWebGLSwapChain* depth_stencil_swap_chain);
+  ~XRWebGLProjectionLayer() override = default;
+
+  // XRWebGLLayerClient implementation
+  const XRLayer* layer() const override { return this; }
+  WebGLRenderingContextBase* context() const override {
+    return webgl_context_.Get();
+  }
+  scoped_refptr<StaticBitmapImage> TransferToStaticBitmapImage() override {
+    return nullptr;
+  }
+
+  uint16_t textureWidth() const override;
+  uint16_t textureHeight() const override;
+  uint16_t textureArrayLength() const override;
+
+  void OnFrameStart() override;
+  void OnFrameEnd() override;
+  void OnResize() override;
+
+  XRWebGLSwapChain* color_swap_chain() const { return color_swap_chain_.Get(); }
+  XRWebGLSwapChain* depth_stencil_swap_chain() const {
+    return depth_stencil_swap_chain_.Get();
+  }
+
+  void MarkViewportUpdated() { viewport_updated_ = true; }
+
+  void Trace(Visitor*) const override;
+
+ private:
+  Member<WebGLRenderingContextBase> webgl_context_;
+  Member<XRWebGLSwapChain> color_swap_chain_;
+  Member<XRWebGLSwapChain> depth_stencil_swap_chain_;
+  bool viewport_updated_ = true;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_PROJECTION_LAYER_H_
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_sub_image.cc b/third_party/blink/renderer/modules/xr/xr_webgl_sub_image.cc
new file mode 100644
index 0000000..1262583
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_sub_image.cc
@@ -0,0 +1,45 @@
+// Copyright 2025 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/modules/xr/xr_webgl_sub_image.h"
+
+#include "third_party/blink/renderer/modules/webgl/webgl_unowned_texture.h"
+
+namespace blink {
+
+XRWebGLSubImage::XRWebGLSubImage(const gfx::Rect& viewport,
+                                 std::optional<uint16_t> image_index,
+                                 XRWebGLSwapChain* color_swap_chain,
+                                 XRWebGLSwapChain* depth_stencil_swap_chain,
+                                 XRWebGLSwapChain* motion_vector_swap_chain)
+    : XRSubImage(viewport), image_index_(image_index) {
+  // Must have color swap chain, depth/stencil and motion vector are optional.
+  CHECK(color_swap_chain);
+  color_texture_ = color_swap_chain->GetCurrentTexture();
+  color_texture_width_ = color_swap_chain->descriptor().width;
+  color_texture_height_ = color_swap_chain->descriptor().height;
+
+  if (depth_stencil_swap_chain) {
+    depth_stencil_texture_ = depth_stencil_swap_chain->GetCurrentTexture();
+    depth_stencil_texture_width_ = depth_stencil_swap_chain->descriptor().width;
+    depth_stencil_texture_height_ =
+        depth_stencil_swap_chain->descriptor().height;
+  }
+
+  if (motion_vector_swap_chain) {
+    motion_vector_texture_ = motion_vector_swap_chain->GetCurrentTexture();
+    motion_vector_texture_width_ = motion_vector_swap_chain->descriptor().width;
+    motion_vector_texture_height_ =
+        motion_vector_swap_chain->descriptor().height;
+  }
+}
+
+void XRWebGLSubImage::Trace(Visitor* visitor) const {
+  visitor->Trace(color_texture_);
+  visitor->Trace(depth_stencil_texture_);
+  visitor->Trace(motion_vector_texture_);
+  XRSubImage::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_sub_image.h b/third_party/blink/renderer/modules/xr/xr_webgl_sub_image.h
index f8374195..2be308e 100644
--- a/third_party/blink/renderer/modules/xr/xr_webgl_sub_image.h
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_sub_image.h
@@ -9,6 +9,7 @@
 
 #include "third_party/blink/renderer/modules/webgl/webgl_texture.h"
 #include "third_party/blink/renderer/modules/xr/xr_sub_image.h"
+#include "third_party/blink/renderer/modules/xr/xr_webgl_swap_chain.h"
 
 namespace blink {
 
@@ -16,7 +17,11 @@
   DEFINE_WRAPPERTYPEINFO();
 
  public:
-  explicit XRWebGLSubImage(const gfx::Rect& viewport) : XRSubImage(viewport) {}
+  XRWebGLSubImage(const gfx::Rect& viewport,
+                  std::optional<uint16_t> image_index,
+                  XRWebGLSwapChain* color_swap_chain,
+                  XRWebGLSwapChain* depth_stencil_swap_chain,
+                  XRWebGLSwapChain* motion_vector_swap_chain);
 
   WebGLTexture* colorTexture() const { return color_texture_.Get(); }
   WebGLTexture* depthStencilTexture() const {
@@ -42,12 +47,7 @@
     return motion_vector_texture_height_;
   }
 
-  void Trace(Visitor* visitor) const override {
-    visitor->Trace(color_texture_);
-    visitor->Trace(depth_stencil_texture_);
-    visitor->Trace(motion_vector_texture_);
-    XRSubImage::Trace(visitor);
-  }
+  void Trace(Visitor* visitor) const override;
 
  private:
   Member<WebGLTexture> color_texture_{nullptr};
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_swap_chain.cc b/third_party/blink/renderer/modules/xr/xr_webgl_swap_chain.cc
new file mode 100644
index 0000000..de8c413
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_swap_chain.cc
@@ -0,0 +1,175 @@
+// Copyright 2025 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/modules/xr/xr_webgl_swap_chain.h"
+
+#include "third_party/blink/renderer/modules/webgl/webgl_framebuffer.h"
+#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h"
+#include "third_party/blink/renderer/modules/webgl/webgl_texture.h"
+#include "third_party/blink/renderer/modules/xr/xr_layer_shared_image_manager.h"
+#include "third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h"
+
+namespace blink {
+
+XRWebGLSwapChain::XRWebGLSwapChain(
+    WebGLRenderingContextBase* context,
+    const XRWebGLSwapChain::Descriptor& descriptor,
+    bool webgl2)
+    : webgl_context_(context), descriptor_(descriptor), webgl2_(webgl2) {
+  CHECK(context);
+}
+
+// Clears the contents of the current texture to transparent black or 0 (for
+// depth/stencil textures).
+void XRWebGLSwapChain::ClearCurrentTexture() {
+  WebGLUnownedTexture* texture = current_texture();
+  if (!texture) {
+    return;
+  }
+
+  gpu::gles2::GLES2Interface* gl = context()->ContextGL();
+  if (!gl) {
+    return;
+  }
+
+  if (!clear_framebuffer_) {
+    clear_framebuffer_ = webgl_context_->createFramebuffer();
+  }
+
+  GLenum attachment = descriptor_.attachment_target;
+  gl->BindFramebuffer(GL_FRAMEBUFFER, clear_framebuffer_->Object());
+  gl->FramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D,
+                           texture->Object(), 0);
+
+  GLbitfield clear_bits = 0;
+  if (attachment == GL_COLOR_ATTACHMENT0) {
+    clear_bits |= GL_COLOR_BUFFER_BIT;
+    gl->ColorMask(true, true, true, true);
+    gl->ClearColor(0, 0, 0, 0);
+  } else if (attachment == GL_DEPTH_ATTACHMENT) {
+    clear_bits |= GL_DEPTH_BUFFER_BIT;
+    gl->DepthMask(true);
+    gl->ClearDepthf(1.0f);
+  } else if (attachment == GL_STENCIL_ATTACHMENT) {
+    clear_bits |= GL_STENCIL_BUFFER_BIT;
+    gl->StencilMaskSeparate(GL_FRONT, true);
+    gl->ClearStencil(0);
+  }
+
+  gl->Disable(GL_SCISSOR_TEST);
+  gl->Clear(clear_bits);
+
+  // WebGLRenderingContextBase inherits from DrawingBuffer::Client, but makes
+  // all the methods private. Downcasting allows us to access them.
+  DrawingBuffer::Client* client =
+      static_cast<DrawingBuffer::Client*>(context());
+
+  client->DrawingBufferClientRestoreScissorTest();
+  client->DrawingBufferClientRestoreMaskAndClearValues();
+  client->DrawingBufferClientRestoreFramebufferBinding();
+}
+
+void XRWebGLSwapChain::Trace(Visitor* visitor) const {
+  visitor->Trace(webgl_context_);
+  visitor->Trace(clear_framebuffer_);
+  XRSwapChain::Trace(visitor);
+}
+
+XRWebGLStaticSwapChain::XRWebGLStaticSwapChain(
+    WebGLRenderingContextBase* context,
+    const XRWebGLSwapChain::Descriptor& descriptor,
+    bool webgl2)
+    : XRWebGLSwapChain(context, descriptor, webgl2) {}
+
+XRWebGLStaticSwapChain::~XRWebGLStaticSwapChain() {
+  if (owned_texture_) {
+    gpu::gles2::GLES2Interface* gl = context()->ContextGL();
+    if (!gl) {
+      return;
+    }
+
+    gl->DeleteTextures(1, &owned_texture_);
+  }
+}
+
+WebGLUnownedTexture* XRWebGLStaticSwapChain::ProduceTexture() {
+  gpu::gles2::GLES2Interface* gl = context()->ContextGL();
+  if (!gl) {
+    return nullptr;
+  }
+
+  gl->GenTextures(1, &owned_texture_);
+  gl->BindTexture(GL_TEXTURE_2D, owned_texture_);
+
+  if (webgl2()) {
+    gl->TexStorage2DEXT(GL_TEXTURE_2D, 1, descriptor().internal_format,
+                        descriptor().width, descriptor().height);
+  } else {
+    gl->TexImage2D(GL_TEXTURE_2D, 0, descriptor().format, descriptor().width,
+                   descriptor().height, 0, descriptor().format,
+                   descriptor().type, nullptr);
+  }
+
+  // WebGLRenderingContextBase inherits from DrawingBuffer::Client, but makes
+  // all the methods private. Downcasting allows us to access them.
+  DrawingBuffer::Client* client =
+      static_cast<DrawingBuffer::Client*>(context());
+  client->DrawingBufferClientRestoreTexture2DBinding();
+
+  return MakeGarbageCollected<WebGLUnownedTexture>(context(), owned_texture_,
+                                                   GL_TEXTURE_2D);
+}
+
+void XRWebGLStaticSwapChain::OnFrameEnd() {
+  ClearCurrentTexture();
+
+  // Intentionally not calling ResetCurrentTexture() here to keep the previously
+  // produced texture for the next frame.
+}
+
+XRWebGLSharedImageSwapChain::XRWebGLSharedImageSwapChain(
+    WebGLRenderingContextBase* context,
+    const XRWebGLSwapChain::Descriptor& descriptor,
+    bool webgl2)
+    : XRWebGLSwapChain(context, descriptor, webgl2) {}
+
+WebGLUnownedTexture* XRWebGLSharedImageSwapChain::ProduceTexture() {
+  gpu::gles2::GLES2Interface* context_gl = context()->ContextGL();
+  if (!context_gl) {
+    return nullptr;
+  }
+
+  const XRLayerSharedImages& shared_images = layer()->GetSharedImages();
+  const XRSharedImageData& content_image_data =
+      shared_images.content_image_data;
+
+  CHECK(content_image_data.shared_image);
+  CHECK(content_image_data.sync_token.HasData());
+
+  // Create a texture backed by the shared image.
+  CHECK(!shared_image_texture_);
+  shared_image_texture_ =
+      content_image_data.shared_image->CreateGLTexture(context_gl);
+  shared_image_scoped_access_ =
+      shared_image_texture_->BeginAccess(content_image_data.sync_token,
+                                         /*readonly=*/false);
+
+  return MakeGarbageCollected<WebGLUnownedTexture>(
+      context(), shared_image_texture_->id(), GL_TEXTURE_2D);
+}
+
+void XRWebGLSharedImageSwapChain::OnFrameEnd() {
+  WebGLUnownedTexture* texture = ResetCurrentTexture();
+  if (texture) {
+    DCHECK(shared_image_texture_);
+    gpu::SharedImageTexture::ScopedAccess::EndAccess(
+        std::move(shared_image_scoped_access_));
+    shared_image_texture_.reset();
+
+    // Notify our WebGLUnownedTexture that we have deleted it.
+    static_cast<WebGLUnownedTexture*>(texture)->OnGLDeleteTextures();
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_webgl_swap_chain.h b/third_party/blink/renderer/modules/xr/xr_webgl_swap_chain.h
new file mode 100644
index 0000000..a5d4810
--- /dev/null
+++ b/third_party/blink/renderer/modules/xr/xr_webgl_swap_chain.h
@@ -0,0 +1,90 @@
+// Copyright 2025 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_MODULES_XR_XR_WEBGL_SWAP_CHAIN_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_SWAP_CHAIN_H_
+
+#include "third_party/blink/renderer/modules/webgl/webgl_rendering_context_base.h"
+#include "third_party/blink/renderer/modules/webgl/webgl_unowned_texture.h"
+#include "third_party/blink/renderer/modules/xr/xr_swap_chain.h"
+#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+
+namespace blink {
+
+class WebGLRenderingContextBase;
+class WebGLUnownedTexture;
+
+class XRWebGLSwapChain : public XRSwapChain<WebGLUnownedTexture> {
+ public:
+  struct Descriptor {
+    GLenum format;
+    GLenum internal_format;
+    GLenum type;
+    GLenum attachment_target;
+    uint16_t width;
+    uint16_t height;
+    uint16_t depth;
+  };
+
+  XRWebGLSwapChain(WebGLRenderingContextBase*,
+                   const XRWebGLSwapChain::Descriptor&,
+                   bool webgl_2);
+  ~XRWebGLSwapChain() override = default;
+
+  WebGLRenderingContextBase* context() { return webgl_context_.Get(); }
+  const XRWebGLSwapChain::Descriptor& descriptor() const { return descriptor_; }
+  bool webgl2() const { return webgl2_; }
+
+  void Trace(Visitor* visitor) const override;
+
+ protected:
+  void ClearCurrentTexture();
+
+ private:
+  Member<WebGLRenderingContextBase> webgl_context_;
+  Member<WebGLFramebuffer> clear_framebuffer_;
+
+  XRWebGLSwapChain::Descriptor descriptor_;
+  bool webgl2_;
+};
+
+// A texture swap chain that is not communicated back to the compositor, used
+// for things like depth/stencil attachments that don't assist reprojection.
+class XRWebGLStaticSwapChain final : public XRWebGLSwapChain {
+ public:
+  XRWebGLStaticSwapChain(WebGLRenderingContextBase*,
+                         const XRWebGLSwapChain::Descriptor&,
+                         bool webgl2);
+  ~XRWebGLStaticSwapChain() override;
+
+  WebGLUnownedTexture* ProduceTexture() override;
+
+  void OnFrameEnd() override;
+
+ private:
+  GLuint owned_texture_;
+};
+
+// A swap chain backed by SharedImages
+class XRWebGLSharedImageSwapChain final : public XRWebGLSwapChain {
+ public:
+  XRWebGLSharedImageSwapChain(WebGLRenderingContextBase*,
+                              const XRWebGLSwapChain::Descriptor&,
+                              bool webgl2);
+  ~XRWebGLSharedImageSwapChain() override = default;
+
+  WebGLUnownedTexture* ProduceTexture() override;
+
+  void OnFrameEnd() override;
+
+ private:
+  std::unique_ptr<gpu::SharedImageTexture> shared_image_texture_;
+  std::unique_ptr<gpu::SharedImageTexture::ScopedAccess>
+      shared_image_scoped_access_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_XR_XR_WEBGL_SWAP_CHAIN_H_
diff --git a/third_party/blink/renderer/platform/bindings/frozen_array_base.cc b/third_party/blink/renderer/platform/bindings/frozen_array_base.cc
index 53271c66..c19ae71 100644
--- a/third_party/blink/renderer/platform/bindings/frozen_array_base.cc
+++ b/third_party/blink/renderer/platform/bindings/frozen_array_base.cc
@@ -25,8 +25,7 @@
     kDOMWrappersTag,
     WrapperTypeInfo::kWrapperTypeNoPrototype,
     WrapperTypeInfo::kNoInternalFieldClassId,
-    WrapperTypeInfo::kNotInheritFromActiveScriptWrappable,
-    WrapperTypeInfo::kCustomWrappableKind,
+    WrapperTypeInfo::kIdlOtherType,
 };
 
 }  // namespace
diff --git a/third_party/blink/renderer/platform/bindings/idl_member_installer.cc b/third_party/blink/renderer/platform/bindings/idl_member_installer.cc
index 5d15cde..66c62ef 100644
--- a/third_party/blink/renderer/platform/bindings/idl_member_installer.cc
+++ b/third_party/blink/renderer/platform/bindings/idl_member_installer.cc
@@ -503,31 +503,6 @@
     v8::Local<v8::Template> prototype_template,
     v8::Local<v8::Template> interface_template,
     v8::Local<v8::Signature> signature,
-    base::span<const ConstantCallbackConfig> configs) {
-  const bool has_prototype_template = !prototype_template.IsEmpty();
-  const v8::PropertyAttribute v8_property_attribute =
-      static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
-  for (const auto& config : configs) {
-    v8::Local<v8::String> name = V8AtomicString(isolate, config.name);
-    if (has_prototype_template) {
-      prototype_template->SetLazyDataProperty(
-          name, config.callback, v8::Local<v8::Value>(), v8_property_attribute,
-          v8::SideEffectType::kHasNoSideEffect);
-    }
-    interface_template->SetLazyDataProperty(
-        name, config.callback, v8::Local<v8::Value>(), v8_property_attribute,
-        v8::SideEffectType::kHasNoSideEffect);
-  }
-}
-
-// static
-void IDLMemberInstaller::InstallConstants(
-    v8::Isolate* isolate,
-    const DOMWrapperWorld& world,
-    v8::Local<v8::Template> instance_template,
-    v8::Local<v8::Template> prototype_template,
-    v8::Local<v8::Template> interface_template,
-    v8::Local<v8::Signature> signature,
     base::span<const ConstantValueConfig> configs) {
   const bool has_prototype_template = !prototype_template.IsEmpty();
   const v8::PropertyAttribute v8_property_attribute =
diff --git a/third_party/blink/renderer/platform/bindings/idl_member_installer.h b/third_party/blink/renderer/platform/bindings/idl_member_installer.h
index b5393ea..cd9c147 100644
--- a/third_party/blink/renderer/platform/bindings/idl_member_installer.h
+++ b/third_party/blink/renderer/platform/bindings/idl_member_installer.h
@@ -107,22 +107,6 @@
       const char* interface_name,
       base::span<const NoAllocDirectCallAttributeConfig> configs);
 
-  // Web IDL constant
-  struct ConstantCallbackConfig {
-    ConstantCallbackConfig& operator=(const ConstantCallbackConfig&) = delete;
-
-    const char* name;
-    v8::AccessorNameGetterCallback callback;
-  };
-  static void InstallConstants(
-      v8::Isolate* isolate,
-      const DOMWrapperWorld& world,
-      v8::Local<v8::Template> instance_template,
-      v8::Local<v8::Template> prototype_template,
-      v8::Local<v8::Template> interface_template,
-      v8::Local<v8::Signature> signature,
-      base::span<const ConstantCallbackConfig> configs);
-
   struct ConstantValueConfig {
     ConstantValueConfig& operator=(const ConstantValueConfig&) = delete;
 
diff --git a/third_party/blink/renderer/platform/bindings/observable_array.cc b/third_party/blink/renderer/platform/bindings/observable_array.cc
index fd03c41a..a05b21e 100644
--- a/third_party/blink/renderer/platform/bindings/observable_array.cc
+++ b/third_party/blink/renderer/platform/bindings/observable_array.cc
@@ -31,8 +31,7 @@
     WrapperTypeInfo::kWrapperTypeNoPrototype,
     // v8::Proxy (without an internal field) is used as a (pseudo) wrapper.
     WrapperTypeInfo::kNoInternalFieldClassId,
-    WrapperTypeInfo::kNotInheritFromActiveScriptWrappable,
-    WrapperTypeInfo::kIdlObservableArray,
+    WrapperTypeInfo::kIdlOtherType,
 };
 
 }  // namespace
diff --git a/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc b/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc
index 0fdfa584..b48fb94 100644
--- a/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc
+++ b/third_party/blink/renderer/platform/bindings/v8_per_context_data.cc
@@ -118,7 +118,7 @@
 
   v8::Local<v8::Function> parent_interface_object;
   if (auto* parent = type->parent_class) {
-    if (parent->is_skipped_in_interface_object_prototype_chain) {
+    if (parent->is_skipped_in_interface_object_prototype_chain) [[unlikely]] {
       // This is a special case for WindowProperties.
       // We need to set up the inheritance of Window as the following:
       //   Window.__proto__ === EventTarget
diff --git a/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc b/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc
index 3a37aec3..817a9f2 100644
--- a/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc
+++ b/third_party/blink/renderer/platform/bindings/wrapper_type_info.cc
@@ -32,19 +32,7 @@
     case kIdlNamespace:
       v8_template = v8::ObjectTemplate::New(isolate);
       break;
-    case kIdlCallbackInterface:
-      v8_template = v8::FunctionTemplate::New(
-          isolate, V8ObjectConstructor::IsValidConstructorMode);
-      break;
-    case kIdlBufferSourceType:
-      NOTREACHED();
-    case kIdlObservableArray:
-      v8_template = v8::FunctionTemplate::New(isolate);
-      break;
-    case kIdlAsyncOrSyncIterator:
-      v8_template = v8::FunctionTemplate::New(isolate);
-      break;
-    case kCustomWrappableKind:
+    case kIdlOtherType:
       v8_template = v8::FunctionTemplate::New(isolate);
       break;
     default:
diff --git a/third_party/blink/renderer/platform/bindings/wrapper_type_info.h b/third_party/blink/renderer/platform/bindings/wrapper_type_info.h
index e14d065..9ca03a32 100644
--- a/third_party/blink/renderer/platform/bindings/wrapper_type_info.h
+++ b/third_party/blink/renderer/platform/bindings/wrapper_type_info.h
@@ -70,19 +70,11 @@
     kCustomWrappableId,
   };
 
-  enum ActiveScriptWrappableInheritance {
-    kNotInheritFromActiveScriptWrappable,
-    kInheritFromActiveScriptWrappable,
-  };
-
   enum IdlDefinitionKind {
-    kIdlInterface,
+    kIdlInterface,  // includes callback interfaces
     kIdlNamespace,
-    kIdlCallbackInterface,
-    kIdlBufferSourceType,
-    kIdlObservableArray,
-    kIdlAsyncOrSyncIterator,
-    kCustomWrappableKind,
+    // iterators, observably arrays, buffer sources, internal script functions.
+    kIdlOtherType,
   };
 
   static const WrapperTypeInfo* Unwrap(v8::Local<v8::Value> type_info_wrapper) {
@@ -111,11 +103,7 @@
   //
   // - kIdlInterface: v8::FunctionTemplate of interface object
   // - kIdlNamespace: v8::ObjectTemplate of namespace object
-  // - kIdlCallbackInterface: v8::FunctionTemplate of legacy callback
-  //       interface object
-  // - kIdlAsyncOrSyncIterator: v8::FunctionTemplate of default (asynchronous
-  //       or synchronous) iterator object
-  // - kCustomWrappableKind: v8::FunctionTemplate
+  // - kIdlOtherType: v8::FunctionTemplate
   v8::Local<v8::Template> GetV8ClassTemplate(
       v8::Isolate* isolate,
       const DOMWrapperWorld& world) const;
@@ -135,11 +123,6 @@
         interface_template, bindings::V8InterfaceBridgeBase::FeatureSelector());
   }
 
-  bool IsActiveScriptWrappable() const {
-    return active_script_wrappable_inheritance ==
-           kInheritFromActiveScriptWrappable;
-  }
-
   static bool HasLegacyInternalFieldsSet(v8::Local<v8::Object> object) {
     for (int i = 0, n = object->InternalFieldCount(); i < n; ++i) {
       if (object->GetAlignedPointerFromInternalField(i)) {
@@ -171,9 +154,7 @@
 
   unsigned wrapper_type_prototype : 2;  // WrapperTypePrototype
   unsigned wrapper_class_id : 2;        // WrapperClassId
-  unsigned                              // ActiveScriptWrappableInheritance
-      active_script_wrappable_inheritance : 1;
-  unsigned idl_definition_kind : 3;  // IdlDefinitionKind
+  unsigned idl_definition_kind : 2;     // IdlDefinitionKind
 
   // This is a special case only used by V8WindowProperties::WrapperTypeInfo().
   // WindowProperties is part of Window's prototype object's prototype chain,
diff --git a/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc b/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc
index 1e625f0b..4a5446e9 100644
--- a/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc
+++ b/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.cc
@@ -184,8 +184,7 @@
   // The SharedBitmapManager is null since software compositing is not supported
   // or used on Android.
   frame_sink_manager_ = std::make_unique<viz::FrameSinkManagerImpl>(
-      viz::FrameSinkManagerImpl::InitParams(
-          /*shared_bitmap_manager=*/nullptr));
+      viz::FrameSinkManagerImpl::InitParams());
 
   if (synthetic_begin_frame_source_) {
     client_->SetBeginFrameSource(synthetic_begin_frame_source_.get());
diff --git a/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.h b/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.h
index 4f4a6b9..a8e23a1 100644
--- a/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.h
+++ b/third_party/blink/renderer/platform/widget/compositing/android_webview/synchronous_layer_tree_frame_sink.h
@@ -28,7 +28,6 @@
 #include "components/viz/common/surfaces/local_surface_id.h"
 #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/service/display/display_client.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -161,11 +160,6 @@
   // Not owned.
   raw_ptr<SynchronousLayerTreeFrameSinkClient> sync_client_ = nullptr;
 
-  // Used to allocate bitmaps in the software Display.
-  // TODO(crbug.com/692814): The Display never sends its resources out of
-  // process so there is no reason for it to use a SharedBitmapManager.
-  viz::ServerSharedBitmapManager shared_bitmap_manager_;
-
   // Only valid (non-null) during a DemandDrawSw() call.
   raw_ptr<SkCanvas> current_sw_canvas_ = nullptr;
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 5f90d9d..b6a6f77 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -1556,6 +1556,9 @@
 crbug.com/335223786 virtual/html-anchor-attribute-disabled/external/wpt/html/semantics/popovers/popover-anchor-nested-display.tentative.html [ Failure ]
 crbug.com/335223786 virtual/html-anchor-attribute-disabled/external/wpt/html/semantics/popovers/popover-anchor-scroll-display.tentative.html [ Failure ]
 
+# Interesttarget related tests
+crbug.com/326681249 external/wpt/html/semantics/the-button-element/interest-target/interesttarget-svg-a-event-dispatch.tentative.html [ Failure ]
+
 crbug.com/1107923 inspector-protocol/debugger/wasm-streaming-url.js [ Failure Pass Timeout ]
 
 crbug.com/350730710 inspector-protocol/css/css-get-position-try.js [ Failure Skip ]
@@ -2729,9 +2732,10 @@
 crbug.com/388319476 [ Win ] virtual/threaded/external/wpt/css/css-view-transitions/nested/compute-explicit-name-nested-vt-names.tentative.html [ Crash ]
 crbug.com/388319476 [ Mac13 ] virtual/threaded/external/wpt/css/css-view-transitions/new-content-captures-clip-path.html [ Crash ]
 crbug.com/388319476 [ Win ] virtual/threaded/external/wpt/css/css-view-transitions/new-content-captures-clip-path.html [ Crash ]
-[ Mac15 ] virtual/threaded/wpt_internal/view-transition/browser-controls-root-offset-old.html [ Crash Failure ]
-[ Mac12 ] virtual/threaded/wpt_internal/view-transition/browser-controls-root-offset-old.html [ Crash Failure ]
-[ Win ] virtual/threaded/wpt_internal/view-transition/browser-controls-root-offset-old.html [ Crash Failure ]
+crbug.com/390459301 [ Mac15 ] virtual/threaded/wpt_internal/view-transition/browser-controls-root-offset-old.html [ Crash Failure ]
+crbug.com/390459301 [ Mac12 ] virtual/threaded/wpt_internal/view-transition/browser-controls-root-offset-old.html [ Crash Failure ]
+crbug.com/390459301 [ Mac14 ] virtual/view-transition-wide-gamut/wpt_internal/view-transition/browser-controls-root-offset-old.html [ Crash Failure Skip ]
+crbug.com/390459301 [ Win ] virtual/threaded/wpt_internal/view-transition/browser-controls-root-offset-old.html [ Crash Failure ]
 crbug.com/388319476 [ Mac15 ] virtual/view-transition-layered-capture/external/wpt/css/css-view-transitions/layered-capture/nested-overflow.tentative.sub.html?overflow=visible&margin=0&contain=paint&radius=0 [ Crash ]
 crbug.com/388319476 [ Win ] virtual/view-transition-layered-capture/external/wpt/css/css-view-transitions/layered-capture/nested-overflow.tentative.sub.html?overflow=visible&margin=0&contain=paint&radius=0 [ Crash ]
 [ Mac15 ] virtual/view-transition-mpa-serialization/wpt_internal/view-transition/browser-controls-root-offset-new.html [ Crash ]
@@ -2763,6 +2767,7 @@
 crbug.com/385918146 [ Win11 ] external/wpt/webrtc-encoded-transform/tentative/RTCEncodedVideoFrame-clone.https.html [ Timeout ]
 crbug.com/386713385 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/line-clamp-content-height-with-dynamic-change.html [ Failure ]
 crbug.com/385993738 [ Mac12 ] external/wpt/css/css-masking/clip-path/clip-path-inline-001.html [ Failure ]
+crbug.com/386128935 [ Mac11 ] external/wpt/css/css-position/position-fixed-scroll-nested-fixed.html [ Failure ]
 crbug.com/386128935 [ Mac13 ] external/wpt/css/css-position/position-fixed-scroll-nested-fixed.html [ Failure ]
 crbug.com/386128935 [ Mac14 ] external/wpt/css/css-position/position-fixed-scroll-nested-fixed.html [ Failure ]
 crbug.com/385918134 [ Linux ] external/wpt/wasm/core/address.wast.js.html [ Crash ]
@@ -2783,6 +2788,7 @@
 crbug.com/384980894 [ Mac12 ] external/wpt/html/semantics/forms/the-select-element/customizable-select/select-mouse-behavior.tentative.html [ Timeout ]
 crbug.com/384980894 [ Mac13 ] external/wpt/html/semantics/forms/the-select-element/customizable-select/select-mouse-behavior.tentative.html [ Timeout ]
 crbug.com/384980894 [ Mac14 ] external/wpt/html/semantics/forms/the-select-element/customizable-select/select-mouse-behavior.tentative.html [ Timeout ]
+crbug.com/384980894 [ Mac14-arm64 ] external/wpt/html/semantics/forms/the-select-element/customizable-select/select-mouse-behavior.tentative.html [ Timeout ]
 crbug.com/384980894 [ Mac15 ] external/wpt/html/semantics/forms/the-select-element/customizable-select/select-mouse-behavior.tentative.html [ Timeout ]
 crbug.com/384980894 [ Win11-arm64 ] external/wpt/html/semantics/forms/the-select-element/customizable-select/select-mouse-behavior.tentative.html [ Timeout ]
 crbug.com/384773801 [ Mac13 ] external/wpt/webrtc/RTCRtpReceiver-jitterBufferTarget.html [ Timeout ]
@@ -9053,10 +9059,6 @@
 # Gardener 2025-01-08
 crbug.com/376320405 [ Debug Linux ] external/wpt/css/css-sizing/keyword-sizes-for-intrinsic-contributions.tentative.html [ Failure ]
 
-# TODO(crrev.com/c/6159568): Re-enable after landing CL.
-crbug.com/388589515 http/tests/devtools/profiler/heap-snapshot-summary-sorting.js [ Failure Skip ]
-crbug.com/388589515 http/tests/devtools/profiler/heap-snapshot-summary-show-ranges.js [ Failure Skip ]
-
 # Gardener 2025-01-10
 crbug.com/389012014 [ Mac13 ] fast/peerconnection/RTCRtpSender-getParameters.html [ Pass Timeout ]
 
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index fe27a8a2..733f544 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -6818,6 +6818,13 @@
        {}
       ]
      ],
+     "remove-all-children-of-documentElement-in-designMode-when-no-body.html": [
+      "28984449ea3eecc37f19ae5688d468dd15b555a7",
+      [
+       null,
+       {}
+      ]
+     ],
      "remove-document-element-of-iframe-having-style-content.html": [
       "b032311fa7552767a2356a181ce0ffb1fb9b3511",
       [
@@ -7831,7 +7838,7 @@
         {}
        ]
       ],
-      "popover-hint-crash.tentative.html": [
+      "popover-hint-crash.html": [
        "3de269952624db643f21260ac9a2cb659f5c212c",
        [
         null,
@@ -109016,7 +109023,7 @@
        ]
       ],
       "content-visibility-auto-svg-text.html": [
-       "93b570999fc7c0834575bbf0f8dc71417ee23c3a",
+       "60aac70f55ca383f61f29b5333e8bfb6693139ae",
        [
         null,
         [
@@ -171549,6 +171556,19 @@
        {}
       ]
      ],
+     "scrollable-overflow-empty-newline-span.html": [
+      "39f72d9fbff4204a09ab9bbfe88fa2ec60da408c",
+      [
+       null,
+       [
+        [
+         "/css/reference/ref-filled-green-100px-square-only.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "scrollable-overflow-input-001.html": [
       "1b58803079f8b60b68eb1c57a0c522962e0a8019",
       [
@@ -210165,6 +210185,32 @@
         {}
        ]
       ],
+      "text-wrap-balance-before-after-001.html": [
+       "9f5741e23492875670f1406e934a1306bdd51973",
+       [
+        null,
+        [
+         [
+          "/css/css-text/white-space/reference/text-wrap-balance-before-after-001-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
+      "text-wrap-balance-before-after-002.html": [
+       "e9d08953fac0963ba2a9639ac9aabb6f3f59764b",
+       [
+        null,
+        [
+         [
+          "/css/css-text/white-space/reference/text-wrap-balance-before-after-002-ref.html",
+          "=="
+         ]
+        ],
+        {}
+       ]
+      ],
       "text-wrap-balance-dynamic-001.html": [
        "76b4cbbb433568b7cc25f7b1fb6e419b9a9570be",
        [
@@ -312768,43 +312814,171 @@
      "9b01ed7dae7fa79886cef7f1c4c1b593f6a52b55",
      []
     ],
-    "generate.py": [
-     "505375d55fede4f613f4c37b938c4f4b9e16e3c9",
-     []
-    ],
-    "generate_test.py": [
-     "6f46ae913a279b23c8debe1bb2d27e558499f37c",
-     []
-    ],
-    "getAvailability": {
-     "reject_opaque_origin.https.html.headers": [
-      "c7e4e7cc5bd3fa25851c1e26c3c04eb95050d94b",
+    "legacy": {
+     "generate.py": [
+      "505375d55fede4f613f4c37b938c4f4b9e16e3c9",
       []
-     ]
-    },
-    "getDevices": {
-     "reject_opaque_origin.https.html.headers": [
-      "c7e4e7cc5bd3fa25851c1e26c3c04eb95050d94b",
+     ],
+     "generate_test.py": [
+      "6f46ae913a279b23c8debe1bb2d27e558499f37c",
       []
-     ]
-    },
-    "idl": {
-     "idlharness.tentative.https.window-expected.txt": [
-      "9840c87492ed100f65c5d31bf2f616fdc9380d44",
-      []
-     ]
-    },
-    "requestDevice": {
-     "reject_opaque_origin.https.html.headers": [
-      "c7e4e7cc5bd3fa25851c1e26c3c04eb95050d94b",
-      []
-     ]
-    },
-    "requestLEScan": {
-     "reject_opaque_origin.https.html.headers": [
-      "c7e4e7cc5bd3fa25851c1e26c3c04eb95050d94b",
-      []
-     ]
+     ],
+     "getAvailability": {
+      "reject_opaque_origin.https.html.headers": [
+       "c7e4e7cc5bd3fa25851c1e26c3c04eb95050d94b",
+       []
+      ]
+     },
+     "getDevices": {
+      "reject_opaque_origin.https.html.headers": [
+       "c7e4e7cc5bd3fa25851c1e26c3c04eb95050d94b",
+       []
+      ]
+     },
+     "idl": {
+      "idlharness.tentative.https.window-expected.txt": [
+       "9840c87492ed100f65c5d31bf2f616fdc9380d44",
+       []
+      ]
+     },
+     "requestDevice": {
+      "reject_opaque_origin.https.html.headers": [
+       "c7e4e7cc5bd3fa25851c1e26c3c04eb95050d94b",
+       []
+      ]
+     },
+     "requestLEScan": {
+      "reject_opaque_origin.https.html.headers": [
+       "c7e4e7cc5bd3fa25851c1e26c3c04eb95050d94b",
+       []
+      ]
+     },
+     "script-tests": {
+      "base_test_js.template": [
+       "04c7a70ba4f3b84a38a91d1be971f0ec0d4843ff",
+       []
+      ],
+      "characteristic": {
+       "characteristic-is-removed.js": [
+        "48aaec3e93015dd656e8c3dac75860875bfcd4cd",
+        []
+       ],
+       "descriptor-get-same-object.js": [
+        "4e6bc3519bc176c293868ab1d92be843e31dea39",
+        []
+       ],
+       "service-is-removed.js": [
+        "2f5824082b1a9b5166e807e9ec9582aa4a9f3330",
+        []
+       ]
+      },
+      "descriptor": {
+       "service-is-removed.js": [
+        "5373364399e9cd30eefbeb04ed3bff3b70b5d155",
+        []
+       ]
+      },
+      "server": {
+       "disconnect-called-before.js": [
+        "57704ee2992689f602cf2990d863129d2dc1bb6e",
+        []
+       ],
+       "disconnect-called-during-error.js": [
+        "edabb07bccb977e8ef9b018c00292579525c9d22",
+        []
+       ],
+       "disconnect-called-during-success.js": [
+        "84157a0693b4ebd6c8e1cacf5f3ead53a4ff55d1",
+        []
+       ],
+       "disconnect-discovery-timeout.js": [
+        "718e290950d25e52faf5dfccc56610b8dda868a8",
+        []
+       ],
+       "disconnect-invalidates-objects.js": [
+        "995fda34411d1534debe9ba1502390fb5cd8a42b",
+        []
+       ],
+       "disconnected-device.js": [
+        "2b6011642b61c5da786a5819be38f5f14f4fe144",
+        []
+       ],
+       "discovery-complete-no-permission-absent-service.js": [
+        "e9e972359a73edc23da95373156f41c9902d828b",
+        []
+       ],
+       "discovery-complete-service-not-found.js": [
+        "6b745d7e2a8296c54e475d312045d77bac126086",
+        []
+       ],
+       "garbage-collection-ran-during-error.js": [
+        "cf508a928ee72fbd97816d2f2f2b66004de9423c",
+        []
+       ],
+       "garbage-collection-ran-during-success.js": [
+        "bb472fcca4ef2437b03bed6762131ebfefa6c2da",
+        []
+       ],
+       "get-different-service-after-reconnection.js": [
+        "e72128a76ff592f08ad888a538fdcecbf95c2c1a",
+        []
+       ],
+       "get-same-object.js": [
+        "3b3bdd19d201f56addef5fa7a50ca570ec7844ae",
+        []
+       ],
+       "invalid-service-name.js": [
+        "52cbb24f4a3f45c2e2718a530d8cde8e1b221cc9",
+        []
+       ],
+       "no-permission-absent-service.js": [
+        "200dab3e937e174170b3ec7879998d95a1369b3e",
+        []
+       ],
+       "no-permission-for-any-service.js": [
+        "60e3ef00806bcef4c7b49be36f062d58c13274c6",
+        []
+       ],
+       "no-permission-present-service.js": [
+        "3257410685ee405bad8cc4a9dad96a10434278b5",
+        []
+       ],
+       "service-not-found.js": [
+        "0fd2dace7871f7edf00251d53532263901ffe86b",
+        []
+       ]
+      },
+      "service": {
+       "blocklisted-characteristic.js": [
+        "b26f039a70684792e0672b7993aae27fe493fc6c",
+        []
+       ],
+       "characteristic-not-found.js": [
+        "366e046774b6a9cbcd22071a3afa6c1d0857bd1e",
+        []
+       ],
+       "garbage-collection-ran-during-error.js": [
+        "7ed4aaa9622313c2479ac7ff9d6a9dd5c0cf6288",
+        []
+       ],
+       "get-same-object.js": [
+        "db9d740c83cd6bd0796a26338f62eaeee374a2b6",
+        []
+       ],
+       "invalid-characteristic-name.js": [
+        "74cba7ec4314bb4ae002470e7c4f7e591a93ab9b",
+        []
+       ],
+       "reconnect-during.js": [
+        "cc71547ac248e8ecdd5c79c1b3e4816f6525f794",
+        []
+       ],
+       "service-is-removed.js": [
+        "aaf0f14436742c04848749b5acf6d52b7bb2e98c",
+        []
+       ]
+      }
+     }
     },
     "resources": {
      "bluetooth-fake-devices.js": [
@@ -312823,132 +312997,6 @@
       "f9f7a6f0d7d9cff3e2c24edba6751292a003f6de",
       []
      ]
-    },
-    "script-tests": {
-     "base_test_js.template": [
-      "04c7a70ba4f3b84a38a91d1be971f0ec0d4843ff",
-      []
-     ],
-     "characteristic": {
-      "characteristic-is-removed.js": [
-       "48aaec3e93015dd656e8c3dac75860875bfcd4cd",
-       []
-      ],
-      "descriptor-get-same-object.js": [
-       "4e6bc3519bc176c293868ab1d92be843e31dea39",
-       []
-      ],
-      "service-is-removed.js": [
-       "2f5824082b1a9b5166e807e9ec9582aa4a9f3330",
-       []
-      ]
-     },
-     "descriptor": {
-      "service-is-removed.js": [
-       "5373364399e9cd30eefbeb04ed3bff3b70b5d155",
-       []
-      ]
-     },
-     "server": {
-      "disconnect-called-before.js": [
-       "57704ee2992689f602cf2990d863129d2dc1bb6e",
-       []
-      ],
-      "disconnect-called-during-error.js": [
-       "edabb07bccb977e8ef9b018c00292579525c9d22",
-       []
-      ],
-      "disconnect-called-during-success.js": [
-       "84157a0693b4ebd6c8e1cacf5f3ead53a4ff55d1",
-       []
-      ],
-      "disconnect-discovery-timeout.js": [
-       "718e290950d25e52faf5dfccc56610b8dda868a8",
-       []
-      ],
-      "disconnect-invalidates-objects.js": [
-       "995fda34411d1534debe9ba1502390fb5cd8a42b",
-       []
-      ],
-      "disconnected-device.js": [
-       "2b6011642b61c5da786a5819be38f5f14f4fe144",
-       []
-      ],
-      "discovery-complete-no-permission-absent-service.js": [
-       "e9e972359a73edc23da95373156f41c9902d828b",
-       []
-      ],
-      "discovery-complete-service-not-found.js": [
-       "6b745d7e2a8296c54e475d312045d77bac126086",
-       []
-      ],
-      "garbage-collection-ran-during-error.js": [
-       "cf508a928ee72fbd97816d2f2f2b66004de9423c",
-       []
-      ],
-      "garbage-collection-ran-during-success.js": [
-       "bb472fcca4ef2437b03bed6762131ebfefa6c2da",
-       []
-      ],
-      "get-different-service-after-reconnection.js": [
-       "e72128a76ff592f08ad888a538fdcecbf95c2c1a",
-       []
-      ],
-      "get-same-object.js": [
-       "3b3bdd19d201f56addef5fa7a50ca570ec7844ae",
-       []
-      ],
-      "invalid-service-name.js": [
-       "52cbb24f4a3f45c2e2718a530d8cde8e1b221cc9",
-       []
-      ],
-      "no-permission-absent-service.js": [
-       "200dab3e937e174170b3ec7879998d95a1369b3e",
-       []
-      ],
-      "no-permission-for-any-service.js": [
-       "60e3ef00806bcef4c7b49be36f062d58c13274c6",
-       []
-      ],
-      "no-permission-present-service.js": [
-       "3257410685ee405bad8cc4a9dad96a10434278b5",
-       []
-      ],
-      "service-not-found.js": [
-       "0fd2dace7871f7edf00251d53532263901ffe86b",
-       []
-      ]
-     },
-     "service": {
-      "blocklisted-characteristic.js": [
-       "b26f039a70684792e0672b7993aae27fe493fc6c",
-       []
-      ],
-      "characteristic-not-found.js": [
-       "366e046774b6a9cbcd22071a3afa6c1d0857bd1e",
-       []
-      ],
-      "garbage-collection-ran-during-error.js": [
-       "7ed4aaa9622313c2479ac7ff9d6a9dd5c0cf6288",
-       []
-      ],
-      "get-same-object.js": [
-       "db9d740c83cd6bd0796a26338f62eaeee374a2b6",
-       []
-      ],
-      "invalid-characteristic-name.js": [
-       "74cba7ec4314bb4ae002470e7c4f7e591a93ab9b",
-       []
-      ],
-      "reconnect-during.js": [
-       "cc71547ac248e8ecdd5c79c1b3e4816f6525f794",
-       []
-      ],
-      "service-is-removed.js": [
-       "aaf0f14436742c04848749b5acf6d52b7bb2e98c",
-       []
-      ]
-     }
     }
    },
    "browsing-topics": {
@@ -313035,6 +313083,10 @@
      []
     ],
     "support": {
+     "clear-site-data-cache.py": [
+      "87107693b46edbd52a4bbc6a100ab1a62e9fc19c",
+      []
+     ],
      "clear-site-data-cookie.py": [
       "eb50cb54ef867844ea741fe1fdb01df970e4deea",
       []
@@ -313689,7 +313741,7 @@
      []
     ],
     "OWNERS": [
-     "0da9e4cde6027cfd7f1d3fa6e60ac64937e6c846",
+     "09f04d1a1f02f18aeffcb57a2bca454e51da0fbb",
      []
     ],
     "WEB_FEATURES.yml": [
@@ -328813,7 +328865,11 @@
        []
       ],
       "at-container-parsing-expected.txt": [
-       "b85a3d39580ad774b01d5d487e94dbd84dc85327",
+       "bcf11af075d1e05ae57cea5f7d77df7b77a4cb07",
+       []
+      ],
+      "at-container-serialization-expected.txt": [
+       "12a5c81a28963b953141106a99c0b9bf93b213cc",
        []
       ],
       "change-display-in-container-ref.html": [
@@ -328924,7 +328980,7 @@
       ],
       "support": {
        "cq-testcommon.js": [
-        "357657870e6c488db9130831e323706a05166300",
+        "8d1e03f2073fd2073bdbd0270ec9aa112030e55d",
         []
        ],
        "test.vtt": [
@@ -346008,12 +346064,8 @@
        "758ef35275eeacc96b6e584f672f86c4b170e0e1",
        []
       ],
-      "clip-path-interpolation-shape-control-points.tentative-expected.txt": [
-       "ce0d856f8d4c798f997a59ff9da72941e1a7bf9c",
-       []
-      ],
       "clip-path-interpolation-shape-expected.txt": [
-       "4fb9216a99530e54216ec12996863f3a76b3d520",
+       "03fe0ea48d1ca056a9d5a52b177ca4b4f6bbabf1",
        []
       ],
       "mask-border-outset-composition-expected.txt": [
@@ -358267,6 +358319,14 @@
         "e522e3711c9d1a68f6a90d97cba248ecb4e47d32",
         []
        ],
+       "text-wrap-balance-before-after-001-ref.html": [
+        "9bf7820c6ab2799a292764c4e209a71e8b299cb5",
+        []
+       ],
+       "text-wrap-balance-before-after-002-ref.html": [
+        "6fbfe89c7d3173a89a1061d83ad578336478a3d2",
+        []
+       ],
        "text-wrap-balance-dynamic-001-ref.html": [
         "43202e4d97f03c1694176f5c6a95b4a48e938a96",
         []
@@ -379797,7 +379857,7 @@
      []
     ],
     "OWNERS": [
-     "49d68bb82b1ca6e4654bf0e8daeda2f8c7fe51df",
+     "92a7265767d3e13f6c5f3dd628ff2f042858719c",
      []
     ],
     "README.md": [
@@ -380960,7 +381020,7 @@
       []
      ],
      "FileSystemDirectoryHandle-removeEntry.js": [
-      "4193839e2c1df72db9218af0e6fb85bb118b5900",
+      "f059ec313f916425cd744e7268b7690ceac0f9c4",
       []
      ],
      "FileSystemDirectoryHandle-resolve.js": [
@@ -385862,32 +385922,6 @@
         []
        ]
       },
-      "fill-and-stroke-styles": {
-       "2d.gradient.interpolate.alpha.png": [
-        "af5ac0f07d64e7598e0ea6a8e37cff2a5c4ea2a0",
-        []
-       ],
-       "2d.gradient.interpolate.color.png": [
-        "af5ac0f07d64e7598e0ea6a8e37cff2a5c4ea2a0",
-        []
-       ],
-       "2d.gradient.interpolate.coloralpha.png": [
-        "552e6ee44b1a3bed7d474e198e7cf1ed3a1be5cb",
-        []
-       ],
-       "2d.gradient.interpolate.multiple.png": [
-        "86122450d3a35ef2db3c2917dfdb919fdfb9c4ec",
-        []
-       ],
-       "2d.gradient.interpolate.overlap.png": [
-        "5c2bb964e0fd379309797343aa869c195f2f742a",
-        []
-       ],
-       "2d.gradient.interpolate.vertical.png": [
-        "37d6a00c6279daf43fa436d201799b8c2e527cb7",
-        []
-       ]
-      },
       "filters": {
        "2d.filter.canvasFilterObject.componentTransfer.discrete.tentative-expected.html": [
         "8b81b134aeab6d4457853556563ce927ccefea5f",
@@ -386209,14 +386243,6 @@
        }
       },
       "path-objects": {
-       "2d.path.fill.overlap.png": [
-        "e2a35d48d4c4363294aec671a38cbd4b39c9a53c",
-        []
-       ],
-       "2d.path.stroke.overlap.png": [
-        "e2a35d48d4c4363294aec671a38cbd4b39c9a53c",
-        []
-       ],
        "2d.path.stroke.prune.arc-expected.txt": [
         "5a66b29c4a3019444e0b629b47713efb6ce1572d",
         []
@@ -386230,12 +386256,6 @@
         []
        ]
       },
-      "pixel-manipulation": {
-       "2d.imageData.put.alpha.png": [
-        "5428c65524eebf083e2772bd66b496eeb1c1745e",
-        []
-       ]
-      },
       "reset": {
        "2d.reset.after-rasterization-expected.html": [
         "3162e16e63d9fa5cc495401ca53d6c0242d3ee65",
@@ -386274,65 +386294,7 @@
         []
        ]
       },
-      "shadows": {
-       "2d.shadow.alpha.2.png": [
-        "8764e89b371d41428f0ba45d9c0391e41b0dd8a2",
-        []
-       ],
-       "2d.shadow.alpha.3.png": [
-        "8764e89b371d41428f0ba45d9c0391e41b0dd8a2",
-        []
-       ],
-       "2d.shadow.alpha.4.png": [
-        "8764e89b371d41428f0ba45d9c0391e41b0dd8a2",
-        []
-       ],
-       "2d.shadow.alpha.5.png": [
-        "8764e89b371d41428f0ba45d9c0391e41b0dd8a2",
-        []
-       ],
-       "2d.shadow.blur.high.png": [
-        "743640b79f8810c193ae9ad86fdfaa76ee5f4943",
-        []
-       ],
-       "2d.shadow.blur.low.png": [
-        "99fb651c21bca0fe563843d987bdf0f0ff9a6db6",
-        []
-       ],
-       "2d.shadow.canvas.alpha.png": [
-        "8764e89b371d41428f0ba45d9c0391e41b0dd8a2",
-        []
-       ],
-       "2d.shadow.gradient.alpha.png": [
-        "8764e89b371d41428f0ba45d9c0391e41b0dd8a2",
-        []
-       ],
-       "2d.shadow.image.alpha.png": [
-        "8764e89b371d41428f0ba45d9c0391e41b0dd8a2",
-        []
-       ],
-       "2d.shadow.pattern.alpha.png": [
-        "8764e89b371d41428f0ba45d9c0391e41b0dd8a2",
-        []
-       ]
-      },
       "text": {
-       "2d.text.draw.fill.basic.png": [
-        "70d7b046cb226cfcb2bfeebe3477d3b580d8270a",
-        []
-       ],
-       "2d.text.draw.fill.maxWidth.large.png": [
-        "70d7b046cb226cfcb2bfeebe3477d3b580d8270a",
-        []
-       ],
-       "2d.text.draw.fill.rtl.png": [
-        "70d7b046cb226cfcb2bfeebe3477d3b580d8270a",
-        []
-       ],
-       "2d.text.draw.stroke.basic.png": [
-        "fb3b5b830d345d2aa858e41673e08f99977baf08",
-        []
-       ],
        "2d.text.drawing.style.reset.fontKerning.none2-expected.html": [
         "ce5874653d76b29c22d6b237f88cb4afe6000ef5",
         []
@@ -386455,7 +386417,7 @@
        []
       ],
       "gentestutilsunion.py": [
-       "c0e09736934a40d7003a164fe558799532f77368",
+       "dbd729c82447fa9c9dfed522a35667b1aa19fc16",
        []
       ],
       "name2dir-canvas.yaml": [
@@ -387529,6 +387491,10 @@
       "bcc6f0734d580474a8bd25f25d4662f175cdc39b",
       []
      ],
+     "reporting-subresource-corp.tentative.https.html.headers": [
+      "bcc6f0734d580474a8bd25f25d4662f175cdc39b",
+      []
+     ],
      "resources": {
       "common.js": [
        "df4bfcfc7d52ac006f84f4ca046cc64b4dd7a171",
@@ -387554,6 +387520,14 @@
        "34b4e9f8ea41fd1aed28b86715df31c1692b88aa",
        []
       ],
+      "reporting-empty-frame-multiple-headers.html.asis": [
+       "5020ad39fee282b2755f020133f09a6c5058ec1f",
+       []
+      ],
+      "reporting-empty-frame.html": [
+       "b1579add2e033e9dfc6c8bb18f9e523b246326ac",
+       []
+      ],
       "shared-worker-fetch.js.py": [
        "bf46cd291d4b955c35082e8f611b772d70267253",
        []
@@ -395912,11 +395886,11 @@
         []
        ],
        "popover-top-layer-nesting.js": [
-        "ace10b3f7bc47f32c04ad1435e1d2eb28f3ec3a0",
+        "affb08ca726c2ed6602385150a025afe0bd3df9f",
         []
        ],
        "popover-utils.js": [
-        "96ac7e03f0e7fc6bdd3c44ef400b243dba8b735d",
+        "eb5dab38245d5a85eb80bbaaf1dce7e5ccbb7728",
         []
        ]
       }
@@ -401420,8 +401394,12 @@
      "a5db8f3c494d61f1c1ae9829debb647e5e8b1e02",
      []
     ],
+    "css-fonts-5.idl": [
+     "06461b0366ca75fc860ab0523c87d5dfb17e5246",
+     []
+    ],
     "css-fonts.idl": [
-     "d5c9dc86705efe3ad28e2ba7904207d021b5e4c7",
+     "9b8034bc6d7a3f2e42a9ad778c240ab75de62f6f",
      []
     ],
     "css-highlight-api.idl": [
@@ -401441,7 +401419,7 @@
      []
     ],
     "css-mixins.idl": [
-     "49806ab54709b29efbad52ca1e1af4d53e00e733",
+     "6629b3861f6a3fc02885081f02abbd548f00a3d2",
      []
     ],
     "css-nav.idl": [
@@ -401533,7 +401511,7 @@
      []
     ],
     "digital-credentials.idl": [
-     "e20079efa14f9a894181d1cafb66129a1634c627",
+     "e4ebb3b3e8621b668a18dcdeed7974e6446234e0",
      []
     ],
     "digital-goods.idl": [
@@ -401581,7 +401559,7 @@
      []
     ],
     "fedcm.idl": [
-     "16b5b2faf10529c9633efe5f2c44431b1fe00229",
+     "07f7955ff648b882ae44c319ff5b8414b877d2fb",
      []
     ],
     "fenced-frame.idl": [
@@ -401665,7 +401643,7 @@
      []
     ],
     "html.idl": [
-     "d4f8b9a7680cb7a5ee124e85a1ea6d7df1100cd7",
+     "3832a6f9b7187a0b6e5bb0b08717f09579ff7c69",
      []
     ],
     "idle-detection.idl": [
@@ -401793,7 +401771,7 @@
      []
     ],
     "mediacapture-surface-control.idl": [
-     "b0bbd22304fd7add21b5910a3377a5100ecb02ec",
+     "964f662da7fa0a488dd776e5e485a9c731607083",
      []
     ],
     "mediacapture-transform.idl": [
@@ -401885,7 +401863,7 @@
      []
     ],
     "permissions-policy.idl": [
-     "5878d8d150a6db19f9f060fe5c60c420eb8ed12b",
+     "806d2eb80f6562300ad4cc664d13a968995e0012",
      []
     ],
     "permissions-request.idl": [
@@ -402049,7 +402027,7 @@
      []
     ],
     "shared-storage.idl": [
-     "b6863eff8eecf597a85183683ea444e93c241858",
+     "6f38d673952f781dd99b6c6f39475c7f8aa39523",
      []
     ],
     "speech-api.idl": [
@@ -402257,7 +402235,7 @@
      []
     ],
     "webnn.idl": [
-     "132280b05f8d3fa6074955a1be2cb8e40706d500",
+     "3e1d9a9f440fd66f413ea628c6d2395fd7d2e8d2",
      []
     ],
     "webrtc-encoded-transform.idl": [
@@ -402780,7 +402758,7 @@
     ]
    },
    "lint.ignore": [
-    "6ee7bfa9b70249e010f02c42b7d68f5ace40b39e",
+    "27728b07fc2af6687ef827e2ba52a0c976db6847",
     []
    ],
    "loading": {
@@ -407137,7 +407115,7 @@
        []
       ],
       "unload-helper.js": [
-       "9739ead69d6d5864ef1753cdaec888a9cae87905",
+       "d1bf0105ce5d6fb8f1afe02a3cea8ac4ab2bbfce",
        []
       ],
       "vertical-scroll-scrollable-content.html": [
@@ -407179,7 +407157,7 @@
      ]
     },
     "idlharness.window-expected.txt": [
-     "f9013562402a9b732c243e18fc890b3ac5103a08",
+     "a49f0943676d1429d1309127703f90a97831ba20",
      []
     ],
     "payment-allowed-by-permissions-policy.https.sub.html.headers": [
@@ -410387,6 +410365,14 @@
       "02c275f37b66e8a0b434c6fa57b347994d523118",
       []
      ],
+     "compressed-data.py": [
+      "8445f762d50c7a80b1ae55b7a960080d4db31eab",
+      []
+     ],
+     "compressed-js.py": [
+      "20207577b975209013ffd767798f4dea79594766",
+      []
+     ],
      "connection-reuse-test.js": [
       "453fbd34051067480139058d40541368bce2207c",
       []
@@ -410439,6 +410425,10 @@
       "167386d7a52d7cb3472a82c894b5868b53f3d049",
       []
      ],
+     "dummy.js": [
+      "cb7fc3481600bd01945267b7f79dd4b6b7244c6c",
+      []
+     ],
      "embed-navigate-back.html": [
       "c9c7340f5307dacfafd67e6f749f56abe2430c87",
       []
@@ -410491,6 +410481,30 @@
       "2ee92b2a5511c0d43359e0db7ee3b64ae5f9115c",
       []
      ],
+     "foo.text.br": [
+      "30cb2f7095e15aca510a4a8cbae00f5d35f0918b",
+      []
+     ],
+     "foo.text.br.headers": [
+      "8c03b823e09e9135244d15c9e82697c30f8da33a",
+      []
+     ],
+     "foo.text.gz": [
+      "05a5cce07b514365d9c468f4a1763b8173cfecfc",
+      []
+     ],
+     "foo.text.gz.headers": [
+      "7def3ddc148d1c2457240a9741ed4a2b8b3a5927",
+      []
+     ],
+     "foo.text.zst": [
+      "a73bbdd22458db57635bf88e97a4e47b4886e722",
+      []
+     ],
+     "foo.text.zst.headers": [
+      "c5974e126a34d876cf69fe69573682ac97f0b977",
+      []
+     ],
      "frame-timing.js": [
       "019bd424b55065451eb4ad0a132d3a6befbbb5fc",
       []
@@ -410516,7 +410530,7 @@
       []
      ],
      "gzip_xml.py": [
-      "7debc9ce3f6cb061be8ca37b73fcf9f2a5396389",
+      "b5a0f72d3d4569ffaff833aebb5a2aa376dc063e",
       []
      ],
      "header-delay.h2.py": [
@@ -419454,7 +419468,7 @@
       []
      ],
      "urlpatterntestdata.json": [
-      "058079bb6d17ace15e7cf6505dc966ea2de0ee45",
+      "1d2ba25ff7d696ab9fafb71164e76d661d50fd17",
       []
      ],
      "urlpatterntests.js": [
@@ -424304,7 +424318,7 @@
       []
      ],
      "utils_validation.js": [
-      "0f07b777def10fcebba8cd0282dea463e0c1b37e",
+      "4ee0a9fd25701881920273238642b89664080e49",
       []
      ]
     },
@@ -450803,526 +450817,12 @@
     ]
    },
    "bluetooth": {
-    "adapter": {
-     "adapter-absent-getAvailability.https.window.js": [
-      "55f4a675da158ac8cd116ea4346c39ff11195f21",
-      [
-       "bluetooth/adapter/adapter-absent-getAvailability.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "adapter-added-getAvailability.https.window.js": [
-      "f8e25b2ac2fc48b865ce7f9e3702a7688f89dbc3",
-      [
-       "bluetooth/adapter/adapter-added-getAvailability.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "adapter-powered-off-getAvailability.https.window.js": [
-      "1ffcd3bb096cebd80c34d5c57cadc8c00a128d2a",
-      [
-       "bluetooth/adapter/adapter-powered-off-getAvailability.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "adapter-powered-on-getAvailability.https.window.js": [
-      "84c7982d2146b8660d098e21c1c30496a189549a",
-      [
-       "bluetooth/adapter/adapter-powered-on-getAvailability.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "adapter-powered-on-off-on-getAvailability.https.window.js": [
-      "c4ba9b5f3a9b13eaba51a3d29cfa62405816753e",
-      [
-       "bluetooth/adapter/adapter-powered-on-off-on-getAvailability.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "adapter-removed-getAvailability.https.window.js": [
-      "ca0b51f47dc6807b79a861d85ea5a7f2549e691a",
-      [
-       "bluetooth/adapter/adapter-removed-getAvailability.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "cross-origin-iframe-getAvailability.sub.https.window.js": [
-      "54abfbb5cefcb364be4c01f34e40a62ca49156d7",
-      [
-       "bluetooth/adapter/cross-origin-iframe-getAvailability.sub.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ]
-    },
-    "characteristic": {
-     "characteristicProperties.https.window.js": [
-      "f7a57a9c4b8922249976d4f980c3e07555b05969",
-      [
-       "bluetooth/characteristic/characteristicProperties.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "getDescriptor": {
-      "detachedIframe.https.window.js": [
-       "de7d0b0b7ce79ffc4391a1aa476b88beb8be2c27",
+    "legacy": {
+     "adapter": {
+      "adapter-absent-getAvailability.https.window.js": [
+       "55f4a675da158ac8cd116ea4346c39ff11195f21",
        [
-        "bluetooth/characteristic/getDescriptor/detachedIframe.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-characteristic-is-removed.https.window.js": [
-       "9e48a7caabc29a610f72fef370cc4ee6571220ea",
-       [
-        "bluetooth/characteristic/getDescriptor/gen-characteristic-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-descriptor-get-same-object.https.window.js": [
-       "708f67da6a8c38738c6ddb2d1990b6440426627f",
-       [
-        "bluetooth/characteristic/getDescriptor/gen-descriptor-get-same-object.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-service-is-removed.https.window.js": [
-       "c256050b0fc4dfe84ad174d1e62c54a0dfa49686",
-       [
-        "bluetooth/characteristic/getDescriptor/gen-service-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "getDescriptors": {
-      "detachedIframe.https.window.js": [
-       "45dd23752f2fd836b751c4700f910ddb1de26e2c",
-       [
-        "bluetooth/characteristic/getDescriptors/detachedIframe.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-characteristic-is-removed-with-uuid.https.window.js": [
-       "a0424c01106ac6f9ab921b0cbea3c2b960c8f938",
-       [
-        "bluetooth/characteristic/getDescriptors/gen-characteristic-is-removed-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-characteristic-is-removed.https.window.js": [
-       "29325c3bb527996a7bbb8b7bdaff17f7dc39e256",
-       [
-        "bluetooth/characteristic/getDescriptors/gen-characteristic-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-descriptor-get-same-object.https.window.js": [
-       "7f1001f3eee795307dfac2899f4dac78e2f2de23",
-       [
-        "bluetooth/characteristic/getDescriptors/gen-descriptor-get-same-object.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-service-is-removed-with-uuid.https.window.js": [
-       "b7c4bff32d91b46e2f70c3181d617c8aac5d5480",
-       [
-        "bluetooth/characteristic/getDescriptors/gen-service-is-removed-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-service-is-removed.https.window.js": [
-       "22dc30f6d825736f0ed6a48e2e794fcb0bca4258",
-       [
-        "bluetooth/characteristic/getDescriptors/gen-service-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "notifications": {
-      "characteristic-is-removed.https.window.js": [
-       "9641ad71e970aeb8b4b53b6b7f1415cc28e889e6",
-       [
-        "bluetooth/characteristic/notifications/characteristic-is-removed.https.window.html",
+        "bluetooth/legacy/adapter/adapter-absent-getAvailability.https.window.html",
         {
          "script_metadata": [
           [
@@ -451345,10 +450845,140 @@
         }
        ]
       ],
-      "service-is-removed.https.window.js": [
-       "a5851fc473323741f9f2a3a6f793287e4b2cd1ab",
+      "adapter-added-getAvailability.https.window.js": [
+       "f8e25b2ac2fc48b865ce7f9e3702a7688f89dbc3",
        [
-        "bluetooth/characteristic/notifications/service-is-removed.https.window.html",
+        "bluetooth/legacy/adapter/adapter-added-getAvailability.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "adapter-powered-off-getAvailability.https.window.js": [
+       "1ffcd3bb096cebd80c34d5c57cadc8c00a128d2a",
+       [
+        "bluetooth/legacy/adapter/adapter-powered-off-getAvailability.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "adapter-powered-on-getAvailability.https.window.js": [
+       "84c7982d2146b8660d098e21c1c30496a189549a",
+       [
+        "bluetooth/legacy/adapter/adapter-powered-on-getAvailability.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "adapter-powered-on-off-on-getAvailability.https.window.js": [
+       "c4ba9b5f3a9b13eaba51a3d29cfa62405816753e",
+       [
+        "bluetooth/legacy/adapter/adapter-powered-on-off-on-getAvailability.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "adapter-removed-getAvailability.https.window.js": [
+       "ca0b51f47dc6807b79a861d85ea5a7f2549e691a",
+       [
+        "bluetooth/legacy/adapter/adapter-removed-getAvailability.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "cross-origin-iframe-getAvailability.sub.https.window.js": [
+       "54abfbb5cefcb364be4c01f34e40a62ca49156d7",
+       [
+        "bluetooth/legacy/adapter/cross-origin-iframe-getAvailability.sub.https.window.html",
         {
          "script_metadata": [
           [
@@ -451372,11 +451002,11 @@
        ]
       ]
      },
-     "readValue": {
-      "add-multiple-event-listeners.https.window.js": [
-       "0eeafd0b79ba30fe7325c48727c13c3cf4fc3fab",
+     "characteristic": {
+      "characteristicProperties.https.window.js": [
+       "f7a57a9c4b8922249976d4f980c3e07555b05969",
        [
-        "bluetooth/characteristic/readValue/add-multiple-event-listeners.https.window.html",
+        "bluetooth/legacy/characteristic/characteristicProperties.https.window.html",
         {
          "script_metadata": [
           [
@@ -451399,10 +451029,591 @@
         }
        ]
       ],
-      "characteristic-is-removed.https.window.js": [
-       "76dc206fb9301915df4cefdc1238b0e486820d92",
+      "getDescriptor": {
+       "detachedIframe.https.window.js": [
+        "de7d0b0b7ce79ffc4391a1aa476b88beb8be2c27",
+        [
+         "bluetooth/legacy/characteristic/getDescriptor/detachedIframe.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-characteristic-is-removed.https.window.js": [
+        "9e48a7caabc29a610f72fef370cc4ee6571220ea",
+        [
+         "bluetooth/legacy/characteristic/getDescriptor/gen-characteristic-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-descriptor-get-same-object.https.window.js": [
+        "708f67da6a8c38738c6ddb2d1990b6440426627f",
+        [
+         "bluetooth/legacy/characteristic/getDescriptor/gen-descriptor-get-same-object.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-service-is-removed.https.window.js": [
+        "c256050b0fc4dfe84ad174d1e62c54a0dfa49686",
+        [
+         "bluetooth/legacy/characteristic/getDescriptor/gen-service-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "getDescriptors": {
+       "detachedIframe.https.window.js": [
+        "45dd23752f2fd836b751c4700f910ddb1de26e2c",
+        [
+         "bluetooth/legacy/characteristic/getDescriptors/detachedIframe.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-characteristic-is-removed-with-uuid.https.window.js": [
+        "a0424c01106ac6f9ab921b0cbea3c2b960c8f938",
+        [
+         "bluetooth/legacy/characteristic/getDescriptors/gen-characteristic-is-removed-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-characteristic-is-removed.https.window.js": [
+        "29325c3bb527996a7bbb8b7bdaff17f7dc39e256",
+        [
+         "bluetooth/legacy/characteristic/getDescriptors/gen-characteristic-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-descriptor-get-same-object.https.window.js": [
+        "7f1001f3eee795307dfac2899f4dac78e2f2de23",
+        [
+         "bluetooth/legacy/characteristic/getDescriptors/gen-descriptor-get-same-object.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-service-is-removed-with-uuid.https.window.js": [
+        "b7c4bff32d91b46e2f70c3181d617c8aac5d5480",
+        [
+         "bluetooth/legacy/characteristic/getDescriptors/gen-service-is-removed-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-service-is-removed.https.window.js": [
+        "22dc30f6d825736f0ed6a48e2e794fcb0bca4258",
+        [
+         "bluetooth/legacy/characteristic/getDescriptors/gen-service-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "notifications": {
+       "characteristic-is-removed.https.window.js": [
+        "9641ad71e970aeb8b4b53b6b7f1415cc28e889e6",
+        [
+         "bluetooth/legacy/characteristic/notifications/characteristic-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "service-is-removed.https.window.js": [
+        "a5851fc473323741f9f2a3a6f793287e4b2cd1ab",
+        [
+         "bluetooth/legacy/characteristic/notifications/service-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "readValue": {
+       "add-multiple-event-listeners.https.window.js": [
+        "0eeafd0b79ba30fe7325c48727c13c3cf4fc3fab",
+        [
+         "bluetooth/legacy/characteristic/readValue/add-multiple-event-listeners.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "characteristic-is-removed.https.window.js": [
+        "76dc206fb9301915df4cefdc1238b0e486820d92",
+        [
+         "bluetooth/legacy/characteristic/readValue/characteristic-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ],
+           [
+            "timeout",
+            "long"
+           ]
+          ],
+          "timeout": "long"
+         }
+        ]
+       ],
+       "detachedIframe.https.window.js": [
+        "6e2510b58d6af5117144fb21b2e7049ad9120d02",
+        [
+         "bluetooth/legacy/characteristic/readValue/detachedIframe.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "event-is-fired.https.window.js": [
+        "52b70e7a080e870c27bf86d9ba591292f0b273bb",
+        [
+         "bluetooth/legacy/characteristic/readValue/event-is-fired.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-characteristic-is-removed.https.window.js": [
+        "5bee6db1076bdbc57462ab9298c50c2542a0f702",
+        [
+         "bluetooth/legacy/characteristic/readValue/gen-characteristic-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "read-succeeds.https.window.js": [
+        "e5ddfb81696c009743622929d3c490a6469e4c4b",
+        [
+         "bluetooth/legacy/characteristic/readValue/read-succeeds.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "read-updates-value.https.window.js": [
+        "bb98aeb18f2dcd068735d8d492dc604ead3970e4",
+        [
+         "bluetooth/legacy/characteristic/readValue/read-updates-value.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "service-is-removed.https.window.js": [
+        "1f699ca25eebcd35e0dc76be4be735d7dfd801b6",
+        [
+         "bluetooth/legacy/characteristic/readValue/service-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "service-same-from-2-characteristics.https.window.js": [
+       "dafd755fd1d49b48bcd0a2623a07a5f6855a8164",
        [
-        "bluetooth/characteristic/readValue/characteristic-is-removed.https.window.html",
+        "bluetooth/legacy/characteristic/service-same-from-2-characteristics.https.window.html",
         {
          "script_metadata": [
           [
@@ -451420,6 +451631,1522 @@
           [
            "script",
            "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "service-same-object.https.window.js": [
+       "01b3a25e35e4a8c2e8a1626e12de90e625dda0a8",
+       [
+        "bluetooth/legacy/characteristic/service-same-object.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "startNotifications": {
+       "detachedIframe.https.window.js": [
+        "f09c52d32822e1dc412e158c18e9cafa0a931e29",
+        [
+         "bluetooth/legacy/characteristic/startNotifications/detachedIframe.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-characteristic-is-removed.https.window.js": [
+        "c2a23d7f44191fc3a64a397aac447801f3c6ce47",
+        [
+         "bluetooth/legacy/characteristic/startNotifications/gen-characteristic-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "stopNotifications": {
+       "detachedIframe.https.window.js": [
+        "a459a5b15d9fa7f87b27fda23f8edaa547584e71",
+        [
+         "bluetooth/legacy/characteristic/stopNotifications/detachedIframe.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "writeValue": {
+       "buffer-is-detached.https.window.js": [
+        "43c50d763a0973a498d861fcb80a2efc24f92daa",
+        [
+         "bluetooth/legacy/characteristic/writeValue/buffer-is-detached.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "characteristic-is-removed.https.window.js": [
+        "6e9da8802c7adf343d0dd9683c2b03afe11d28e2",
+        [
+         "bluetooth/legacy/characteristic/writeValue/characteristic-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "detachedIframe.https.window.js": [
+        "eb243a3508c3de75d29f3df91060040da231e1a6",
+        [
+         "bluetooth/legacy/characteristic/writeValue/detachedIframe.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-characteristic-is-removed.https.window.js": [
+        "5750cb82c7f848dba7fd4f3783f6a340636a3661",
+        [
+         "bluetooth/legacy/characteristic/writeValue/gen-characteristic-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "service-is-removed.https.window.js": [
+        "89c3112475012de82ca637ec09fbd372f76516fc",
+        [
+         "bluetooth/legacy/characteristic/writeValue/service-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "write-succeeds.https.window.js": [
+        "b57fe941d0a88091b08f1f573c7af06c88dc85a6",
+        [
+         "bluetooth/legacy/characteristic/writeValue/write-succeeds.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "writeValueWithResponse": {
+       "buffer-is-detached.https.window.js": [
+        "5fb4aa23553986791279430b44f8bb50efa5abcd",
+        [
+         "bluetooth/legacy/characteristic/writeValueWithResponse/buffer-is-detached.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "characteristic-is-removed.https.window.js": [
+        "9309cd5a3cd325e5df4d9b38260a2fcd4b47ea2b",
+        [
+         "bluetooth/legacy/characteristic/writeValueWithResponse/characteristic-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-characteristic-is-removed.https.window.js": [
+        "e202376da7c64fa3a906e6f169a76e61d1e6a916",
+        [
+         "bluetooth/legacy/characteristic/writeValueWithResponse/gen-characteristic-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "service-is-removed.https.window.js": [
+        "81b2dff44e34080b4088f7a6fd5ed9f58feb6eff",
+        [
+         "bluetooth/legacy/characteristic/writeValueWithResponse/service-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "write-succeeds.https.window.js": [
+        "c87e7ac6aba26b31319becd030d34657506ba57d",
+        [
+         "bluetooth/legacy/characteristic/writeValueWithResponse/write-succeeds.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "writeValueWithoutResponse": {
+       "buffer-is-detached.https.window.js": [
+        "23721380cbdb69c84dce985b26ba92a79f6c4f3e",
+        [
+         "bluetooth/legacy/characteristic/writeValueWithoutResponse/buffer-is-detached.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "characteristic-is-removed.https.window.js": [
+        "8d3ed1f663369bfb11448a3739d3773f55b53c61",
+        [
+         "bluetooth/legacy/characteristic/writeValueWithoutResponse/characteristic-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-characteristic-is-removed.https.window.js": [
+        "b88246aae834f0ea54e72ec9a292c6b93eafb3cc",
+        [
+         "bluetooth/legacy/characteristic/writeValueWithoutResponse/gen-characteristic-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "service-is-removed.https.window.js": [
+        "feb711c64ea6b46d3401d3888de8d757c9a567b2",
+        [
+         "bluetooth/legacy/characteristic/writeValueWithoutResponse/service-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "write-succeeds.https.window.js": [
+        "0dcf8ad0b196adde1941e0c4e31b36ba08741384",
+        [
+         "bluetooth/legacy/characteristic/writeValueWithoutResponse/write-succeeds.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      }
+     },
+     "descriptor": {
+      "readValue": {
+       "detachedIframe.https.window.js": [
+        "47765a13159b7facc753e7951b94377abc8b54b9",
+        [
+         "bluetooth/legacy/descriptor/readValue/detachedIframe.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-service-is-removed.https.window.js": [
+        "d6c73ba60e6a1e5d8d10c80796729baf039ee702",
+        [
+         "bluetooth/legacy/descriptor/readValue/gen-service-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "read-succeeds.https.window.js": [
+        "d81db2f8c0ddb54042a6178677df309a7c6248ef",
+        [
+         "bluetooth/legacy/descriptor/readValue/read-succeeds.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "writeValue": {
+       "buffer-is-detached.https.window.js": [
+        "454b23ea4c2c44e4f011b77520c69dac5d469fb2",
+        [
+         "bluetooth/legacy/descriptor/writeValue/buffer-is-detached.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "detachedIframe.https.window.js": [
+        "aa143ca8e53e993273a6887b67566ff3265992d4",
+        [
+         "bluetooth/legacy/descriptor/writeValue/detachedIframe.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-service-is-removed.https.window.js": [
+        "c7f6d6efe3e3a19f5c954a3ca83aa87f0386c98c",
+        [
+         "bluetooth/legacy/descriptor/writeValue/gen-service-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      }
+     },
+     "device": {
+      "forget": {
+       "connect-after-forget.https.window.js": [
+        "0b15b4d060d9ae8e4795b2a4fbb684db53218f69",
+        [
+         "bluetooth/legacy/device/forget/connect-after-forget.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "detachedIframe.https.window.js": [
+        "f4803542fbb30c2aff401fd0951271e5e39bc999",
+        [
+         "bluetooth/legacy/device/forget/detachedIframe.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "getDevices.https.window.js": [
+        "0594a6f03bf377ea19c5856298c03ed136973656",
+        [
+         "bluetooth/legacy/device/forget/getDevices.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "gattserverdisconnected-event": {
+       "disconnected.https.window.js": [
+        "43a11a88cbf655ee4aab6ac70215ae3fb5f9a541",
+        [
+         "bluetooth/legacy/device/gattserverdisconnected-event/disconnected.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "disconnected_gc.https.window.js": [
+        "0cf4973e2167766ece047cbcac8034d089482acc",
+        [
+         "bluetooth/legacy/device/gattserverdisconnected-event/disconnected_gc.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "one-event-per-disconnection.https.window.js": [
+        "ab273adbc88bcd4d03c9bf5309a34cb30cf3246a",
+        [
+         "bluetooth/legacy/device/gattserverdisconnected-event/one-event-per-disconnection.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "reconnect-during-disconnected-event.https.window.js": [
+        "bdaf47c66198ce70f9a1951358f27b38dcfac4ca",
+        [
+         "bluetooth/legacy/device/gattserverdisconnected-event/reconnect-during-disconnected-event.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "watchAdvertisements": {
+       "abort-before-watchAdvertisements.https.window.js": [
+        "e1ac1fb136902244055e88f859d9d536bc58951c",
+        [
+         "bluetooth/legacy/device/watchAdvertisements/abort-before-watchAdvertisements.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "abort-pending-operation.https.window.js": [
+        "c1022ff4a9c1b8c57703a8ce5b8bdfa309b7d985",
+        [
+         "bluetooth/legacy/device/watchAdvertisements/abort-pending-operation.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "abort-signal-stops-events.https.window.js": [
+        "21b6883fee433a2a04c689ae82c9a8a30aed5c91",
+        [
+         "bluetooth/legacy/device/watchAdvertisements/abort-signal-stops-events.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "abort-subsequent-watchAdvertisements-call-stops-events.https.window.js": [
+        "a5da75012bab1991a762c48f9a76f32906f2db11",
+        [
+         "bluetooth/legacy/device/watchAdvertisements/abort-subsequent-watchAdvertisements-call-stops-events.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "advertisementreceived-event-fired.https.window.js": [
+        "fff18bc47eed8434e3b4ff46cd157b5a5e41ea1a",
+        [
+         "bluetooth/legacy/device/watchAdvertisements/advertisementreceived-event-fired.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "blocklisted-manufacturer-data-filtered-from-event.https.window.js": [
+        "c73e3dbad1b20dd26a02d6ca4326a53b3f668a92",
+        [
+         "bluetooth/legacy/device/watchAdvertisements/blocklisted-manufacturer-data-filtered-from-event.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "concurrent-watchAdvertisements-calls.https.window.js": [
+        "cb6532be68ece2a04f5d70106f7b58869906b086",
+        [
+         "bluetooth/legacy/device/watchAdvertisements/concurrent-watchAdvertisements-calls.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "detachedIframe.https.window.js": [
+        "202a8dab7da3efdefc01089695f402cff7bb3309",
+        [
+         "bluetooth/legacy/device/watchAdvertisements/detachedIframe.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "service-and-manufacturer-data-filtered-from-event.https.window.js": [
+        "f6b93ffb4bfa0f90310f56b1b6d946acdcceb8f2",
+        [
+         "bluetooth/legacy/device/watchAdvertisements/service-and-manufacturer-data-filtered-from-event.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "subsequent-watchAdvertisements-call.https.window.js": [
+        "797bfd1fa0b3ff4375d654895c21ca0d248982ad",
+        [
+         "bluetooth/legacy/device/watchAdvertisements/subsequent-watchAdvertisements-call.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "watching-two-devices-abort-one-watchAdvertisements.https.window.js": [
+        "8be02adb349a2010fea7e8f6424bdcb3d1788d93",
+        [
+         "bluetooth/legacy/device/watchAdvertisements/watching-two-devices-abort-one-watchAdvertisements.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "watching-two-devices.https.window.js": [
+        "32ec89a1eb0eaecf8b95f7c7b8026ad402438fd4",
+        [
+         "bluetooth/legacy/device/watchAdvertisements/watching-two-devices.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      }
+     },
+     "getAvailability": {
+      "reject_opaque_origin.https.html": [
+       "8745fc9551bba2148632563b5f125b21e2d1427c",
+       [
+        null,
+        {}
+       ]
+      ],
+      "sandboxed_iframe.https.window.js": [
+       "0fc520e4aec3648164690de5061b4ce05a0d6c67",
+       [
+        "bluetooth/legacy/getAvailability/sandboxed_iframe.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ]
+     },
+     "getDevices": {
+      "granted-devices-with-services.https.window.js": [
+       "3228543617decdc9a213883d650ba33ffd9b90c1",
+       [
+        "bluetooth/legacy/getDevices/granted-devices-with-services.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "no-granted-devices.https.window.js": [
+       "304aa3820d5b80bff80928feabc982b8b156b591",
+       [
+        "bluetooth/legacy/getDevices/no-granted-devices.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "reject_opaque_origin.https.html": [
+       "64b2808fbce3663ee447b8ecd3d00b6fc07a73f7",
+       [
+        null,
+        {}
+       ]
+      ],
+      "returns-same-bluetooth-device-object.https.window.js": [
+       "81c0f6a97e947c9a469ca74a180d595f493561e2",
+       [
+        "bluetooth/legacy/getDevices/returns-same-bluetooth-device-object.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "sandboxed_iframe.https.window.js": [
+       "b9b0ac93c27a054433d9853b3dced0fa1e6a9d13",
+       [
+        "bluetooth/legacy/getDevices/sandboxed_iframe.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ]
+     },
+     "idl": {
+      "idl-Bluetooth.https.window.js": [
+       "2b40eaff4900f5385b19d27115626fafe386a1cb",
+       [
+        "bluetooth/legacy/idl/idl-Bluetooth.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "idl-BluetoothDevice.https.window.js": [
+       "c715926df3be6615f4b34aa3383b057347325f25",
+       [
+        "bluetooth/legacy/idl/idl-BluetoothDevice.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "idl-BluetoothUUID.window.js": [
+       "cf9c14bb2c5c80d2e693401eb916423f9ca33b98",
+       [
+        "bluetooth/legacy/idl/idl-BluetoothUUID.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "idl-NavigatorBluetooth.https.window.js": [
+       "a087d308962074acb52057f229ff4c31bb6f8f42",
+       [
+        "bluetooth/legacy/idl/idl-NavigatorBluetooth.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "idl-NavigatorBluetooth.window.js": [
+       "db6bf89f9f3396243a2acfdd68cbdb0cb09d6901",
+       [
+        "bluetooth/legacy/idl/idl-NavigatorBluetooth.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "idlharness.tentative.https.window.js": [
+       "a632060e20aefd43a2c6b976e891062ad0c2f7b5",
+       [
+        "bluetooth/legacy/idl/idlharness.tentative.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/WebIDLParser.js"
+          ],
+          [
+           "script",
+           "/resources/idlharness.js"
           ],
           [
            "timeout",
@@ -451429,11 +453156,3209 @@
          "timeout": "long"
         }
        ]
+      ]
+     },
+     "requestDevice": {
+      "acceptAllDevices": {
+       "device-with-empty-name.https.window.js": [
+        "15bde6a9336c9e4cb7a13e6cc3864fc3000d2fbc",
+        [
+         "bluetooth/legacy/requestDevice/acceptAllDevices/device-with-empty-name.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "device-with-name.https.window.js": [
+        "f3373a6bb64f517c57c7a1decd2c3c553478a55e",
+        [
+         "bluetooth/legacy/requestDevice/acceptAllDevices/device-with-name.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "optional-services-missing.https.window.js": [
+        "5226a645a868df080da1a71dcb1305fbbb59158a",
+        [
+         "bluetooth/legacy/requestDevice/acceptAllDevices/optional-services-missing.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "optional-services-present.https.window.js": [
+        "7c200d03f181e7f4593dabb01f30c485e6cbea27",
+        [
+         "bluetooth/legacy/requestDevice/acceptAllDevices/optional-services-present.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "blocklisted-manufacturer-data-in-filter.https.window.js": [
+       "2dae7f4cc654ab0bb3b6093826bf3e4d643755a1",
+       [
+        "bluetooth/legacy/requestDevice/blocklisted-manufacturer-data-in-filter.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
       ],
+      "blocklisted-service-in-filter.https.window.js": [
+       "80eaf14447fba398897bc4ee1530a8afe3de6d5d",
+       [
+        "bluetooth/legacy/requestDevice/blocklisted-service-in-filter.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "blocklisted-service-in-optionalServices.https.window.js": [
+       "4c01974e557b2f359d12e27e68d0949a22111994",
+       [
+        "bluetooth/legacy/requestDevice/blocklisted-service-in-optionalServices.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "canonicalizeFilter": {
+       "data-prefix-and-mask-size.https.window.js": [
+        "fa2645093a6adec4e21da973df84f419a45fca8d",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/data-prefix-and-mask-size.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "dataPrefix-buffer-is-detached.https.window.js": [
+        "f4c1a9f6f9b8a3898b8d2fde3136864a9529d165",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/dataPrefix-buffer-is-detached.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "device-name-longer-than-29-bytes.https.window.js": [
+        "20ed383d397046ece557f6698cf29fe15a874e7c",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/device-name-longer-than-29-bytes.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "empty-dataPrefix.https.window.js": [
+        "75e12219ccdffa3525342ea4f8fc00384c0ac049",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/empty-dataPrefix.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "empty-exclusion-filter.https.window.js": [
+        "0d4b196cc7d22bb574d7baffa1af68251b299245",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/empty-exclusion-filter.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "empty-exclusion-filters-member.https.window.js": [
+        "d380fa0268efff3f24a351f69b82066614eda7f9",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/empty-exclusion-filters-member.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "empty-filter.https.window.js": [
+        "bfe94f2721d9542bd75cb46b4557fc40db41443d",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/empty-filter.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "empty-filters-member.https.window.js": [
+        "3265e54fd880bdf900d73332c74499a81f923015",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/empty-filters-member.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "empty-manufacturerData-member.https.window.js": [
+        "0996137f517d8ef167e86efcec11d5952a52b187",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/empty-manufacturerData-member.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "empty-namePrefix.https.window.js": [
+        "8ce2e649676ee0161c00c61449ed9e9d8ecceca5",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/empty-namePrefix.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "empty-services-member.https.window.js": [
+        "a24611631d360d6ec6ef65648f25d41f7ba916f2",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/empty-services-member.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "exclusion-filters-require-filters.https.window.js": [
+        "d7db260deeb6500cc8eb4809bf7368a0dc59936b",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/exclusion-filters-require-filters.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "filters-xor-acceptAllDevices.https.window.js": [
+        "a6c48f2962a321ccf08c8ad9e3d9bbea2d014f7c",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/filters-xor-acceptAllDevices.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "invalid-companyIdentifier.https.window.js": [
+        "18cdbb4b4a85fa9fc19146d72e5494161b7dffe6",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/invalid-companyIdentifier.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "mask-buffer-is-detached.https.window.js": [
+        "ae3f712db58a9e991fd5660679f8d453c2ab33a2",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/mask-buffer-is-detached.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "max-length-exceeded-name-unicode.https.window.js": [
+        "3458c92b65efa8314a2af6ab00c7084d469a103b",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/max-length-exceeded-name-unicode.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "max-length-exceeded-name.https.window.js": [
+        "f14f78fe7d7ecc8d68ecbd1175c1719c311c0483",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/max-length-exceeded-name.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "max-length-exceeded-namePrefix-unicode.https.window.js": [
+        "b2e6668e4b98a9a0e8a23803327a890f86e593ca",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix-unicode.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "max-length-exceeded-namePrefix.https.window.js": [
+        "5d27629eaa14744e6bb436472b734d3f80cd0a0f",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "max-length-name-unicode.https.window.js": [
+        "6a3cf5bead0eefd561064785f3289797cb0a01e3",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/max-length-name-unicode.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "max-length-name.https.window.js": [
+        "7ede93ce72166a177fa8f1db693bd49c1f7ccc2d",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/max-length-name.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "max-length-namePrefix-unicode.https.window.js": [
+        "2061e9863b4026024a4170fb9794f42692cba0f7",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/max-length-namePrefix-unicode.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "max-length-namePrefix.https.window.js": [
+        "f922bb2f0d0579ed5db7012e58d961fccd9dd7c9",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/max-length-namePrefix.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "no-arguments.https.window.js": [
+        "075a97f1a9478a23dc693c169bea9cef9433f8ed",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/no-arguments.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "same-company-identifier.https.window.js": [
+        "41f851adc5ee046eab4a98be19ff8f4ebea5da27",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/same-company-identifier.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "unicode-valid-length-name-name.https.window.js": [
+        "cd10288ddb72f2cb2989bdf96f640853d29b61ee",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/unicode-valid-length-name-name.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "unicode-valid-length-name-namePrefix.https.window.js": [
+        "494f324ee2480e40d22fe3dc05030955a2f84e83",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/unicode-valid-length-name-namePrefix.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "wrong-service-in-optionalServices-member.https.window.js": [
+        "bfba220f4799db5ac19700df083cd8896d1fa356",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/wrong-service-in-optionalServices-member.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "wrong-service-in-services-member.https.window.js": [
+        "352437d0e5a48f56572c78296ef65b7fcc73dc32",
+        [
+         "bluetooth/legacy/requestDevice/canonicalizeFilter/wrong-service-in-services-member.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "cross-origin-iframe.sub.https.window.js": [
+       "d802a862791e98e5a8694e34bd4fbaf554b9f9b5",
+       [
+        "bluetooth/legacy/requestDevice/cross-origin-iframe.sub.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "discovery-succeeds.https.window.js": [
+       "4941d185cad0508069184bc19c0ad81871e2a301",
+       [
+        "bluetooth/legacy/requestDevice/discovery-succeeds.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "doesnt-consume-user-gesture.https.window.js": [
+       "9c742733e1d793d9becda42d8afd245f7c9b633d",
+       [
+        "bluetooth/legacy/requestDevice/doesnt-consume-user-gesture.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "filter-matches.https.window.js": [
+       "1a0f52ac309b1ff116297ae86c27fc3008060cdb",
+       [
+        "bluetooth/legacy/requestDevice/filter-matches.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "le-not-supported.https.window.js": [
+       "c961ab4492c42e0d62c599580b07e87c3f336040",
+       [
+        "bluetooth/legacy/requestDevice/le-not-supported.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "manufacturer-data-filter-matches.https.window.js": [
+       "c4c0e805328a5aa52ff21025e0ad56e0517cce7e",
+       [
+        "bluetooth/legacy/requestDevice/manufacturer-data-filter-matches.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "name-empty-device-from-name-empty-filter.https.window.js": [
+       "2ff22cb70200809e5cbaf7dc8ac63bbf4f2f3c92",
+       [
+        "bluetooth/legacy/requestDevice/name-empty-device-from-name-empty-filter.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "not-processing-user-gesture.https.window.js": [
+       "a063b61163019336cfe3e776ed14b8dc54aea227",
+       [
+        "bluetooth/legacy/requestDevice/not-processing-user-gesture.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "radio-not-present.https.window.js": [
+       "b55d63c6ff45fa30f973facc43de58f560a2f28c",
+       [
+        "bluetooth/legacy/requestDevice/radio-not-present.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "reject_opaque_origin.https.html": [
+       "df348dd39e0b62cb38605b94e0c98e4e155c1dbc",
+       [
+        null,
+        {}
+       ]
+      ],
+      "request-from-iframe.https.window.js": [
+       "d3f3cf897ff6f4eb97253f63843db888824a54fa",
+       [
+        "bluetooth/legacy/requestDevice/request-from-iframe.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "request-from-sandboxed-iframe.https.window.js": [
+       "2101cf0d6b747836f60d04c07774e3a100efed1f",
+       [
+        "bluetooth/legacy/requestDevice/request-from-sandboxed-iframe.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "same-device.https.window.js": [
+       "41a42cf4c8fd70c10c096c50733d9a517b77f54b",
+       [
+        "bluetooth/legacy/requestDevice/same-device.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "sandboxed_iframe.https.window.js": [
+       "e9192a9305b14763d3cabe7c6ddfb3b38f1015da",
+       [
+        "bluetooth/legacy/requestDevice/sandboxed_iframe.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "single-filter-single-service.https.window.js": [
+       "67afad0b930c7930614f3ed17a0db78e1aa20d69",
+       [
+        "bluetooth/legacy/requestDevice/single-filter-single-service.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ]
+     },
+     "requestLEScan": {
+      "reject_opaque_origin.https.html": [
+       "272c5aa760cf7f5bedc24e2d652d68164ab353d3",
+       [
+        null,
+        {}
+       ]
+      ],
+      "sandboxed_iframe.https.window.js": [
+       "32d1e74b778d9932b5104824b69cf21cf6a3ff6b",
+       [
+        "bluetooth/legacy/requestLEScan/sandboxed_iframe.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ]
+     },
+     "server": {
+      "connect": {
+       "connection-succeeds.https.window.js": [
+        "90b62b9265b1c337f05993f7169c9bd33cf6e962",
+        [
+         "bluetooth/legacy/server/connect/connection-succeeds.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "detachedIframe.https.window.js": [
+        "2332cef7070baddc84573120edb0f0dc1fc83efa",
+        [
+         "bluetooth/legacy/server/connect/detachedIframe.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "garbage-collection-ran-during-success.https.window.js": [
+        "2d2211dec30f87a988acf24fc4a283ed174405f8",
+        [
+         "bluetooth/legacy/server/connect/garbage-collection-ran-during-success.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "get-same-gatt-server.https.window.js": [
+        "59d7243a65382cd3509f425bcf6db9087c49c74f",
+        [
+         "bluetooth/legacy/server/connect/get-same-gatt-server.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "device-same-object.https.window.js": [
+       "f9a66d9b6915cf9b093ffafb6cb7fe11b3febf88",
+       [
+        "bluetooth/legacy/server/device-same-object.https.window.html",
+        {
+         "script_metadata": [
+          [
+           "script",
+           "/resources/testdriver.js"
+          ],
+          [
+           "script",
+           "/resources/testdriver-vendor.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-test.js"
+          ],
+          [
+           "script",
+           "/bluetooth/resources/bluetooth-fake-devices.js"
+          ]
+         ]
+        }
+       ]
+      ],
+      "disconnect": {
+       "connect-disconnect-twice.https.window.js": [
+        "5d9908df4c8ac20d5fbea9966ff56b13a3b19de9",
+        [
+         "bluetooth/legacy/server/disconnect/connect-disconnect-twice.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "detach-gc.https.window.js": [
+        "b934b3797325a255b77e5cf39011ab2803bbb90b",
+        [
+         "bluetooth/legacy/server/disconnect/detach-gc.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "detachedIframe.https.window.js": [
+        "04e0ca0117fcc2974e02bde54802ee8d279e234a",
+        [
+         "bluetooth/legacy/server/disconnect/detachedIframe.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "disconnect-twice-in-a-row.https.window.js": [
+        "acca9796d5751d2e5c3fa73e2cd32bcd8ba31bda",
+        [
+         "bluetooth/legacy/server/disconnect/disconnect-twice-in-a-row.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gc-detach.https.window.js": [
+        "1c062a775903bf0396a33592231851e2da2b7660",
+        [
+         "bluetooth/legacy/server/disconnect/gc-detach.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "getPrimaryService": {
+       "gen-disconnect-called-before.https.window.js": [
+        "631545a38542689762605f53af4ed7a7753a9134",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-disconnect-called-before.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnect-called-during-error.https.window.js": [
+        "bcf19665d5fcf2cd720717f3f602f9d9795a6a33",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-disconnect-called-during-error.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnect-called-during-success.https.window.js": [
+        "0d2fc1044a5bbb4c205741db61a3946d5b37c724",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-disconnect-called-during-success.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnect-discovery-timeout.https.window.js": [
+        "03b0c9d0f382c3462beed1ee282321a7d91d94f1",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-disconnect-discovery-timeout.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnect-invalidates-objects.https.window.js": [
+        "56468b24eabf2d99a3f4a10eef7cdbc0c2594ebc",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-disconnect-invalidates-objects.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnected-device.https.window.js": [
+        "741b2db5ee82e8f268fca71143fd419bda0af87b",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-disconnected-device.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-discovery-complete-no-permission-absent-service.https.window.js": [
+        "e2f5c87630559a7a5fef08bf37b68e84d3356648",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-discovery-complete-no-permission-absent-service.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-discovery-complete-service-not-found.https.window.js": [
+        "8e9166b41a140832ccc121b6ec63415fccb5bbca",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-discovery-complete-service-not-found.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-garbage-collection-ran-during-error.https.window.js": [
+        "df182fe8fff6b3001d355fe4c70b67b9b64bdc56",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-garbage-collection-ran-during-error.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-garbage-collection-ran-during-success.https.window.js": [
+        "8e278af224234f1ab005e52cfff0fafcfe729537",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-garbage-collection-ran-during-success.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-get-different-service-after-reconnection.https.window.js": [
+        "d4557f67535001a64a0b7347e336f95db547fe6c",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-get-different-service-after-reconnection.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-get-same-object.https.window.js": [
+        "b43cefb567f01d0b7cdb8b53bc3da40e785fe007",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-get-same-object.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-invalid-service-name.https.window.js": [
+        "cf4ab6c665d6a9605507b01e1e0c3e1a807376ba",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-invalid-service-name.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-no-permission-absent-service.https.window.js": [
+        "3466ded4f9e910d30c3f5a55dd3e1172addeb3d8",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-no-permission-absent-service.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-no-permission-for-any-service.https.window.js": [
+        "6576ef20a3de120529c4bf2997b13b40d7ce3f26",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-no-permission-for-any-service.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-no-permission-present-service.https.window.js": [
+        "3d0b460bc33b9e246dfa59d30248edefd7d13d6a",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-no-permission-present-service.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-service-not-found.https.window.js": [
+        "6e0d2c446ba90199f1bab1158e6147ed632deda6",
+        [
+         "bluetooth/legacy/server/getPrimaryService/gen-service-not-found.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "service-found.https.window.js": [
+        "b8a930d10c79b345f7a0c6c6ec9d89b095dccec4",
+        [
+         "bluetooth/legacy/server/getPrimaryService/service-found.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "two-iframes-from-same-origin.https.window.js": [
+        "b7f23a14913914d3e3bc2f57b98b2b1cc91a185c",
+        [
+         "bluetooth/legacy/server/getPrimaryService/two-iframes-from-same-origin.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      },
+      "getPrimaryServices": {
+       "blocklisted-services-with-uuid.https.window.js": [
+        "ccc913e5bfdc7df6efe6627dc97899e12f981660",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/blocklisted-services-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "blocklisted-services.https.window.js": [
+        "ae6be9099466b725521e54bec72013bcf175762a",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/blocklisted-services.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "correct-services.https.window.js": [
+        "f3d883dd2ef181ca7c7a88999a9620056a42f046",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/correct-services.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnect-called-before-with-uuid.https.window.js": [
+        "21b561375dfed914f011a4de18148053087a6c4f",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnect-called-before.https.window.js": [
+        "8e5fea83ab4e53299ed6f5c78bd5d0322d2cf410",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-disconnect-called-before.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnect-called-during-error-with-uuid.https.window.js": [
+        "5c28716b90bf32e0e25e5e16ac37fc085f50fb9a",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnect-called-during-error.https.window.js": [
+        "ddc31247911d64f1995cdae41af9da15a11bb890",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-disconnect-called-during-error.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnect-called-during-success-with-uuid.https.window.js": [
+        "13e3806d313e40ffc1d69503ad9ebb18cf4630ba",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-disconnect-called-during-success-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnect-called-during-success.https.window.js": [
+        "d6b31936c60a9a17af6a4764ac16b7580b3649a9",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-disconnect-called-during-success.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnect-discovery-timeout-with-uuid.https.window.js": [
+        "77f7bc81d99cc2f0b7f72e5a5866d0d89e625dce",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-disconnect-discovery-timeout-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnect-discovery-timeout.https.window.js": [
+        "ea55b7b495259c58027ac24be4b918585cb44fed",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-disconnect-discovery-timeout.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnect-invalidates-objects-with-uuid.https.window.js": [
+        "8cdb83e3ad7a2fea327c45b67388082c4e58e09e",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnect-invalidates-objects.https.window.js": [
+        "9fd536f05161e0582e88ecbf2f5987a92214a4ba",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-disconnect-invalidates-objects.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnected-device-with-uuid.https.window.js": [
+        "e0393d5e69ca867e09b049576c33efbbc397866d",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-disconnected-device-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-disconnected-device.https.window.js": [
+        "87d74c6ab14083fdd6ed32aa74d01e5b1206b53d",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-disconnected-device.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-discovery-complete-no-permission-absent-service-with-uuid.https.window.js": [
+        "6e179dc5d9a373a6a10c17a8083116e464264658",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-discovery-complete-no-permission-absent-service-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-discovery-complete-service-not-found-with-uuid.https.window.js": [
+        "66cfb491c078d7ca378cb4418d399a0b33a9d07b",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-discovery-complete-service-not-found-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-garbage-collection-ran-during-error-with-uuid.https.window.js": [
+        "a235cf5d189bebdaf2d8e6b04e5796075fb34098",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-garbage-collection-ran-during-error.https.window.js": [
+        "f174d4aef98dbe1fb3fa148992cab34b8a475263",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-garbage-collection-ran-during-error.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-garbage-collection-ran-during-success-with-uuid.https.window.js": [
+        "cf5dfb246fbd12ef3a4597926cdbf877bae2cc4a",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-garbage-collection-ran-during-success.https.window.js": [
+        "f1c080a9463b79462bab70dbe611ee01ea6880b8",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-garbage-collection-ran-during-success.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-get-different-service-after-reconnection-with-uuid.https.window.js": [
+        "2e40d580f3399ddccd7b84d60f9ffa87b0fb2d51",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-get-different-service-after-reconnection.https.window.js": [
+        "ee1fc971bfa942e4f0c204acb80b055f311dc611",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-get-different-service-after-reconnection.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-get-same-object-with-uuid.https.window.js": [
+        "b589056a23a9e67ad9673713bab2cd2e93c1b8f9",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-get-same-object-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-get-same-object.https.window.js": [
+        "63739add912351f1da86a4cfdcd02d4cfd8567fa",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-get-same-object.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-invalid-service-name.https.window.js": [
+        "a9b1262e6a181d1017ead79b1972ecd85e942098",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-invalid-service-name.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-no-permission-absent-service-with-uuid.https.window.js": [
+        "27ad9f008ee0d296dcbcf1c27199ce11a45e6fdd",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-no-permission-absent-service-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-no-permission-for-any-service-with-uuid.https.window.js": [
+        "d5f06c23da6660566fbbbdbf8a5dccaaa14904fe",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-no-permission-for-any-service.https.window.js": [
+        "8aa730d2ed2d8de1868d705e026a6a8a2376ee27",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-no-permission-for-any-service.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-no-permission-present-service-with-uuid.https.window.js": [
+        "a2047a0e8f0e2236cc968b309aa5340ee5b97a90",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "gen-service-not-found-with-uuid.https.window.js": [
+        "a2db1edc4b9f30d73b39eb530894983323135090",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/gen-service-not-found-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "services-found-with-uuid.https.window.js": [
+        "972e6a75caa3f21935abe9b504d0bccf5249c6a1",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/services-found-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "services-found.https.window.js": [
+        "46861175c605d4e2d966df8751d709530f411f02",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/services-found.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ],
+       "services-not-found.https.window.js": [
+        "63503282411b80eba908b91e72ea7a0946945019",
+        [
+         "bluetooth/legacy/server/getPrimaryServices/services-not-found.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
+        ]
+       ]
+      }
+     },
+     "service": {
       "detachedIframe.https.window.js": [
-       "6e2510b58d6af5117144fb21b2e7049ad9120d02",
+       "f75fc225a791b57bff086fa8acad026521e22ce4",
        [
-        "bluetooth/characteristic/readValue/detachedIframe.https.window.html",
+        "bluetooth/legacy/service/detachedIframe.https.window.html",
         {
          "script_metadata": [
           [
@@ -451460,10 +456385,10 @@
         }
        ]
       ],
-      "event-is-fired.https.window.js": [
-       "52b70e7a080e870c27bf86d9ba591292f0b273bb",
+      "device-same-from-2-services.https.window.js": [
+       "5b2ba310d356d3eb4f7c0f76ac992bb2f8056034",
        [
-        "bluetooth/characteristic/readValue/event-is-fired.https.window.html",
+        "bluetooth/legacy/service/device-same-from-2-services.https.window.html",
         {
          "script_metadata": [
           [
@@ -451486,40 +456411,10 @@
         }
        ]
       ],
-      "gen-characteristic-is-removed.https.window.js": [
-       "5bee6db1076bdbc57462ab9298c50c2542a0f702",
+      "device-same-object.https.window.js": [
+       "97da769a9eac25b55e72ac9e44d6c3cf1442ec3a",
        [
-        "bluetooth/characteristic/readValue/gen-characteristic-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "read-succeeds.https.window.js": [
-       "e5ddfb81696c009743622929d3c490a6469e4c4b",
-       [
-        "bluetooth/characteristic/readValue/read-succeeds.https.window.html",
+        "bluetooth/legacy/service/device-same-object.https.window.html",
         {
          "script_metadata": [
           [
@@ -451542,5589 +456437,710 @@
         }
        ]
       ],
-      "read-updates-value.https.window.js": [
-       "bb98aeb18f2dcd068735d8d492dc604ead3970e4",
-       [
-        "bluetooth/characteristic/readValue/read-updates-value.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
+      "getCharacteristic": {
+       "characteristic-found.https.window.js": [
+        "807852ae133c0f024925faaee88309b9f585ea47",
+        [
+         "bluetooth/legacy/service/getCharacteristic/characteristic-found.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
           ]
-         ]
-        }
-       ]
-      ],
-      "service-is-removed.https.window.js": [
-       "1f699ca25eebcd35e0dc76be4be735d7dfd801b6",
-       [
-        "bluetooth/characteristic/readValue/service-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "service-same-from-2-characteristics.https.window.js": [
-      "dafd755fd1d49b48bcd0a2623a07a5f6855a8164",
-      [
-       "bluetooth/characteristic/service-same-from-2-characteristics.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+         }
         ]
-       }
-      ]
-     ],
-     "service-same-object.https.window.js": [
-      "01b3a25e35e4a8c2e8a1626e12de90e625dda0a8",
-      [
-       "bluetooth/characteristic/service-same-object.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "detachedIframe.https.window.js": [
+        "ea8c96160ff9dfa2498fd9d51f167ddc80e4aa6a",
+        [
+         "bluetooth/legacy/service/getCharacteristic/detachedIframe.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "startNotifications": {
-      "detachedIframe.https.window.js": [
-       "f09c52d32822e1dc412e158c18e9cafa0a931e29",
-       [
-        "bluetooth/characteristic/startNotifications/detachedIframe.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
+       ],
+       "gen-blocklisted-characteristic.https.window.js": [
+        "cce302d6504f51db4d67553d8f57b915d5ffc645",
+        [
+         "bluetooth/legacy/service/getCharacteristic/gen-blocklisted-characteristic.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
           ]
-         ]
-        }
-       ]
-      ],
-      "gen-characteristic-is-removed.https.window.js": [
-       "c2a23d7f44191fc3a64a397aac447801f3c6ce47",
-       [
-        "bluetooth/characteristic/startNotifications/gen-characteristic-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "stopNotifications": {
-      "detachedIframe.https.window.js": [
-       "a459a5b15d9fa7f87b27fda23f8edaa547584e71",
-       [
-        "bluetooth/characteristic/stopNotifications/detachedIframe.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "writeValue": {
-      "buffer-is-detached.https.window.js": [
-       "43c50d763a0973a498d861fcb80a2efc24f92daa",
-       [
-        "bluetooth/characteristic/writeValue/buffer-is-detached.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "characteristic-is-removed.https.window.js": [
-       "6e9da8802c7adf343d0dd9683c2b03afe11d28e2",
-       [
-        "bluetooth/characteristic/writeValue/characteristic-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "detachedIframe.https.window.js": [
-       "eb243a3508c3de75d29f3df91060040da231e1a6",
-       [
-        "bluetooth/characteristic/writeValue/detachedIframe.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-characteristic-is-removed.https.window.js": [
-       "5750cb82c7f848dba7fd4f3783f6a340636a3661",
-       [
-        "bluetooth/characteristic/writeValue/gen-characteristic-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "service-is-removed.https.window.js": [
-       "89c3112475012de82ca637ec09fbd372f76516fc",
-       [
-        "bluetooth/characteristic/writeValue/service-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "write-succeeds.https.window.js": [
-       "b57fe941d0a88091b08f1f573c7af06c88dc85a6",
-       [
-        "bluetooth/characteristic/writeValue/write-succeeds.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "writeValueWithResponse": {
-      "buffer-is-detached.https.window.js": [
-       "5fb4aa23553986791279430b44f8bb50efa5abcd",
-       [
-        "bluetooth/characteristic/writeValueWithResponse/buffer-is-detached.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "characteristic-is-removed.https.window.js": [
-       "9309cd5a3cd325e5df4d9b38260a2fcd4b47ea2b",
-       [
-        "bluetooth/characteristic/writeValueWithResponse/characteristic-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-characteristic-is-removed.https.window.js": [
-       "e202376da7c64fa3a906e6f169a76e61d1e6a916",
-       [
-        "bluetooth/characteristic/writeValueWithResponse/gen-characteristic-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "service-is-removed.https.window.js": [
-       "81b2dff44e34080b4088f7a6fd5ed9f58feb6eff",
-       [
-        "bluetooth/characteristic/writeValueWithResponse/service-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "write-succeeds.https.window.js": [
-       "c87e7ac6aba26b31319becd030d34657506ba57d",
-       [
-        "bluetooth/characteristic/writeValueWithResponse/write-succeeds.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "writeValueWithoutResponse": {
-      "buffer-is-detached.https.window.js": [
-       "23721380cbdb69c84dce985b26ba92a79f6c4f3e",
-       [
-        "bluetooth/characteristic/writeValueWithoutResponse/buffer-is-detached.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "characteristic-is-removed.https.window.js": [
-       "8d3ed1f663369bfb11448a3739d3773f55b53c61",
-       [
-        "bluetooth/characteristic/writeValueWithoutResponse/characteristic-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-characteristic-is-removed.https.window.js": [
-       "b88246aae834f0ea54e72ec9a292c6b93eafb3cc",
-       [
-        "bluetooth/characteristic/writeValueWithoutResponse/gen-characteristic-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "service-is-removed.https.window.js": [
-       "feb711c64ea6b46d3401d3888de8d757c9a567b2",
-       [
-        "bluetooth/characteristic/writeValueWithoutResponse/service-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "write-succeeds.https.window.js": [
-       "0dcf8ad0b196adde1941e0c4e31b36ba08741384",
-       [
-        "bluetooth/characteristic/writeValueWithoutResponse/write-succeeds.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     }
-    },
-    "descriptor": {
-     "readValue": {
-      "detachedIframe.https.window.js": [
-       "47765a13159b7facc753e7951b94377abc8b54b9",
-       [
-        "bluetooth/descriptor/readValue/detachedIframe.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-service-is-removed.https.window.js": [
-       "d6c73ba60e6a1e5d8d10c80796729baf039ee702",
-       [
-        "bluetooth/descriptor/readValue/gen-service-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "read-succeeds.https.window.js": [
-       "d81db2f8c0ddb54042a6178677df309a7c6248ef",
-       [
-        "bluetooth/descriptor/readValue/read-succeeds.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "writeValue": {
-      "buffer-is-detached.https.window.js": [
-       "454b23ea4c2c44e4f011b77520c69dac5d469fb2",
-       [
-        "bluetooth/descriptor/writeValue/buffer-is-detached.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "detachedIframe.https.window.js": [
-       "aa143ca8e53e993273a6887b67566ff3265992d4",
-       [
-        "bluetooth/descriptor/writeValue/detachedIframe.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-service-is-removed.https.window.js": [
-       "c7f6d6efe3e3a19f5c954a3ca83aa87f0386c98c",
-       [
-        "bluetooth/descriptor/writeValue/gen-service-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     }
-    },
-    "device": {
-     "forget": {
-      "connect-after-forget.https.window.js": [
-       "0b15b4d060d9ae8e4795b2a4fbb684db53218f69",
-       [
-        "bluetooth/device/forget/connect-after-forget.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "detachedIframe.https.window.js": [
-       "f4803542fbb30c2aff401fd0951271e5e39bc999",
-       [
-        "bluetooth/device/forget/detachedIframe.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "getDevices.https.window.js": [
-       "0594a6f03bf377ea19c5856298c03ed136973656",
-       [
-        "bluetooth/device/forget/getDevices.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "gattserverdisconnected-event": {
-      "disconnected.https.window.js": [
-       "43a11a88cbf655ee4aab6ac70215ae3fb5f9a541",
-       [
-        "bluetooth/device/gattserverdisconnected-event/disconnected.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "disconnected_gc.https.window.js": [
-       "0cf4973e2167766ece047cbcac8034d089482acc",
-       [
-        "bluetooth/device/gattserverdisconnected-event/disconnected_gc.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "one-event-per-disconnection.https.window.js": [
-       "ab273adbc88bcd4d03c9bf5309a34cb30cf3246a",
-       [
-        "bluetooth/device/gattserverdisconnected-event/one-event-per-disconnection.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "reconnect-during-disconnected-event.https.window.js": [
-       "bdaf47c66198ce70f9a1951358f27b38dcfac4ca",
-       [
-        "bluetooth/device/gattserverdisconnected-event/reconnect-during-disconnected-event.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "watchAdvertisements": {
-      "abort-before-watchAdvertisements.https.window.js": [
-       "e1ac1fb136902244055e88f859d9d536bc58951c",
-       [
-        "bluetooth/device/watchAdvertisements/abort-before-watchAdvertisements.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "abort-pending-operation.https.window.js": [
-       "c1022ff4a9c1b8c57703a8ce5b8bdfa309b7d985",
-       [
-        "bluetooth/device/watchAdvertisements/abort-pending-operation.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "abort-signal-stops-events.https.window.js": [
-       "21b6883fee433a2a04c689ae82c9a8a30aed5c91",
-       [
-        "bluetooth/device/watchAdvertisements/abort-signal-stops-events.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "abort-subsequent-watchAdvertisements-call-stops-events.https.window.js": [
-       "a5da75012bab1991a762c48f9a76f32906f2db11",
-       [
-        "bluetooth/device/watchAdvertisements/abort-subsequent-watchAdvertisements-call-stops-events.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "advertisementreceived-event-fired.https.window.js": [
-       "fff18bc47eed8434e3b4ff46cd157b5a5e41ea1a",
-       [
-        "bluetooth/device/watchAdvertisements/advertisementreceived-event-fired.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "blocklisted-manufacturer-data-filtered-from-event.https.window.js": [
-       "c73e3dbad1b20dd26a02d6ca4326a53b3f668a92",
-       [
-        "bluetooth/device/watchAdvertisements/blocklisted-manufacturer-data-filtered-from-event.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "concurrent-watchAdvertisements-calls.https.window.js": [
-       "cb6532be68ece2a04f5d70106f7b58869906b086",
-       [
-        "bluetooth/device/watchAdvertisements/concurrent-watchAdvertisements-calls.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "detachedIframe.https.window.js": [
-       "202a8dab7da3efdefc01089695f402cff7bb3309",
-       [
-        "bluetooth/device/watchAdvertisements/detachedIframe.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "service-and-manufacturer-data-filtered-from-event.https.window.js": [
-       "f6b93ffb4bfa0f90310f56b1b6d946acdcceb8f2",
-       [
-        "bluetooth/device/watchAdvertisements/service-and-manufacturer-data-filtered-from-event.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "subsequent-watchAdvertisements-call.https.window.js": [
-       "797bfd1fa0b3ff4375d654895c21ca0d248982ad",
-       [
-        "bluetooth/device/watchAdvertisements/subsequent-watchAdvertisements-call.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "watching-two-devices-abort-one-watchAdvertisements.https.window.js": [
-       "8be02adb349a2010fea7e8f6424bdcb3d1788d93",
-       [
-        "bluetooth/device/watchAdvertisements/watching-two-devices-abort-one-watchAdvertisements.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "watching-two-devices.https.window.js": [
-       "32ec89a1eb0eaecf8b95f7c7b8026ad402438fd4",
-       [
-        "bluetooth/device/watchAdvertisements/watching-two-devices.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     }
-    },
-    "getAvailability": {
-     "reject_opaque_origin.https.html": [
-      "8745fc9551bba2148632563b5f125b21e2d1427c",
-      [
-       null,
-       {}
-      ]
-     ],
-     "sandboxed_iframe.https.window.js": [
-      "0fc520e4aec3648164690de5061b4ce05a0d6c67",
-      [
-       "bluetooth/getAvailability/sandboxed_iframe.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+         }
         ]
-       }
-      ]
-     ]
-    },
-    "getDevices": {
-     "granted-devices-with-services.https.window.js": [
-      "3228543617decdc9a213883d650ba33ffd9b90c1",
-      [
-       "bluetooth/getDevices/granted-devices-with-services.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-characteristic-not-found.https.window.js": [
+        "2ed48eb5c68798e402193ca195c74c86dbf8909e",
+        [
+         "bluetooth/legacy/service/getCharacteristic/gen-characteristic-not-found.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "no-granted-devices.https.window.js": [
-      "304aa3820d5b80bff80928feabc982b8b156b591",
-      [
-       "bluetooth/getDevices/no-granted-devices.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-garbage-collection-ran-during-error.https.window.js": [
+        "1fd70c8fad4b454cb0687d48645117edb801c4de",
+        [
+         "bluetooth/legacy/service/getCharacteristic/gen-garbage-collection-ran-during-error.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "reject_opaque_origin.https.html": [
-      "64b2808fbce3663ee447b8ecd3d00b6fc07a73f7",
-      [
-       null,
-       {}
-      ]
-     ],
-     "returns-same-bluetooth-device-object.https.window.js": [
-      "81c0f6a97e947c9a469ca74a180d595f493561e2",
-      [
-       "bluetooth/getDevices/returns-same-bluetooth-device-object.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-get-same-object.https.window.js": [
+        "c5176cdc5e0a7a310c60e1d4ed15b07dbeb5e7bb",
+        [
+         "bluetooth/legacy/service/getCharacteristic/gen-get-same-object.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "sandboxed_iframe.https.window.js": [
-      "b9b0ac93c27a054433d9853b3dced0fa1e6a9d13",
-      [
-       "bluetooth/getDevices/sandboxed_iframe.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-invalid-characteristic-name.https.window.js": [
+        "da0f5bda28080f7486e95a3f14ee1f8838c3bc62",
+        [
+         "bluetooth/legacy/service/getCharacteristic/gen-invalid-characteristic-name.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ]
-    },
-    "idl": {
-     "idl-Bluetooth.https.window.js": [
-      "2b40eaff4900f5385b19d27115626fafe386a1cb",
-      [
-       "bluetooth/idl/idl-Bluetooth.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ]
+       ],
+       "gen-reconnect-during.https.window.js": [
+        "8801c152e931ef3e631cc7ec649c83254c59b3a2",
+        [
+         "bluetooth/legacy/service/getCharacteristic/gen-reconnect-during.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "idl-BluetoothDevice.https.window.js": [
-      "c715926df3be6615f4b34aa3383b057347325f25",
-      [
-       "bluetooth/idl/idl-BluetoothDevice.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-service-is-removed.https.window.js": [
+        "bfeb318c46ea54136137f390adaae0a7322dd827",
+        [
+         "bluetooth/legacy/service/getCharacteristic/gen-service-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "idl-BluetoothUUID.window.js": [
-      "cf9c14bb2c5c80d2e693401eb916423f9ca33b98",
-      [
-       "bluetooth/idl/idl-BluetoothUUID.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ]
+       ]
+      },
+      "getCharacteristics": {
+       "blocklisted-characteristics.https.window.js": [
+        "408943585ab86eb3ed73454f82b862820a469aa8",
+        [
+         "bluetooth/legacy/service/getCharacteristics/blocklisted-characteristics.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "idl-NavigatorBluetooth.https.window.js": [
-      "a087d308962074acb52057f229ff4c31bb6f8f42",
-      [
-       "bluetooth/idl/idl-NavigatorBluetooth.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ]
+       ],
+       "characteristics-found-with-uuid.https.window.js": [
+        "f11c69c92e7aed8b177c255a071ef9b3af918a4e",
+        [
+         "bluetooth/legacy/service/getCharacteristics/characteristics-found-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "idl-NavigatorBluetooth.window.js": [
-      "db6bf89f9f3396243a2acfdd68cbdb0cb09d6901",
-      [
-       "bluetooth/idl/idl-NavigatorBluetooth.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ]
+       ],
+       "characteristics-found.https.window.js": [
+        "3244dd3e173d233b65db3ede450464fbc763a531",
+        [
+         "bluetooth/legacy/service/getCharacteristics/characteristics-found.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "idlharness.tentative.https.window.js": [
-      "a632060e20aefd43a2c6b976e891062ad0c2f7b5",
-      [
-       "bluetooth/idl/idlharness.tentative.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/WebIDLParser.js"
-         ],
-         [
-          "script",
-          "/resources/idlharness.js"
-         ],
-         [
-          "timeout",
-          "long"
-         ]
-        ],
-        "timeout": "long"
-       }
-      ]
-     ]
-    },
-    "requestDevice": {
-     "acceptAllDevices": {
-      "device-with-empty-name.https.window.js": [
-       "15bde6a9336c9e4cb7a13e6cc3864fc3000d2fbc",
-       [
-        "bluetooth/requestDevice/acceptAllDevices/device-with-empty-name.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
+       ],
+       "characteristics-not-found.https.window.js": [
+        "5b0c1896d64f01b83349a9152cc561ac0b435438",
+        [
+         "bluetooth/legacy/service/getCharacteristics/characteristics-not-found.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
           ]
-         ]
-        }
-       ]
-      ],
-      "device-with-name.https.window.js": [
-       "f3373a6bb64f517c57c7a1decd2c3c553478a55e",
-       [
-        "bluetooth/requestDevice/acceptAllDevices/device-with-name.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "optional-services-missing.https.window.js": [
-       "5226a645a868df080da1a71dcb1305fbbb59158a",
-       [
-        "bluetooth/requestDevice/acceptAllDevices/optional-services-missing.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "optional-services-present.https.window.js": [
-       "7c200d03f181e7f4593dabb01f30c485e6cbea27",
-       [
-        "bluetooth/requestDevice/acceptAllDevices/optional-services-present.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "blocklisted-manufacturer-data-in-filter.https.window.js": [
-      "2dae7f4cc654ab0bb3b6093826bf3e4d643755a1",
-      [
-       "bluetooth/requestDevice/blocklisted-manufacturer-data-in-filter.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+         }
         ]
-       }
-      ]
-     ],
-     "blocklisted-service-in-filter.https.window.js": [
-      "80eaf14447fba398897bc4ee1530a8afe3de6d5d",
-      [
-       "bluetooth/requestDevice/blocklisted-service-in-filter.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-blocklisted-characteristic-with-uuid.https.window.js": [
+        "79cd01032b580debd840d885c350d44aa7b5e974",
+        [
+         "bluetooth/legacy/service/getCharacteristics/gen-blocklisted-characteristic-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "blocklisted-service-in-optionalServices.https.window.js": [
-      "4c01974e557b2f359d12e27e68d0949a22111994",
-      [
-       "bluetooth/requestDevice/blocklisted-service-in-optionalServices.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-characteristic-not-found-with-uuid.https.window.js": [
+        "8a5e2ab4e4ac8e1534f0ccb3666fe5ee71645ec9",
+        [
+         "bluetooth/legacy/service/getCharacteristics/gen-characteristic-not-found-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "canonicalizeFilter": {
-      "data-prefix-and-mask-size.https.window.js": [
-       "fa2645093a6adec4e21da973df84f419a45fca8d",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/data-prefix-and-mask-size.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
+       ],
+       "gen-garbage-collection-ran-during-error-with-uuid.https.window.js": [
+        "683b93e352d0e59fce29c48585d419febd850fb6",
+        [
+         "bluetooth/legacy/service/getCharacteristics/gen-garbage-collection-ran-during-error-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
           ]
-         ]
-        }
-       ]
-      ],
-      "dataPrefix-buffer-is-detached.https.window.js": [
-       "f4c1a9f6f9b8a3898b8d2fde3136864a9529d165",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/dataPrefix-buffer-is-detached.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "device-name-longer-than-29-bytes.https.window.js": [
-       "20ed383d397046ece557f6698cf29fe15a874e7c",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/device-name-longer-than-29-bytes.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "empty-dataPrefix.https.window.js": [
-       "75e12219ccdffa3525342ea4f8fc00384c0ac049",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/empty-dataPrefix.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "empty-exclusion-filter.https.window.js": [
-       "0d4b196cc7d22bb574d7baffa1af68251b299245",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/empty-exclusion-filter.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "empty-exclusion-filters-member.https.window.js": [
-       "d380fa0268efff3f24a351f69b82066614eda7f9",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/empty-exclusion-filters-member.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "empty-filter.https.window.js": [
-       "bfe94f2721d9542bd75cb46b4557fc40db41443d",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/empty-filter.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "empty-filters-member.https.window.js": [
-       "3265e54fd880bdf900d73332c74499a81f923015",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/empty-filters-member.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "empty-manufacturerData-member.https.window.js": [
-       "0996137f517d8ef167e86efcec11d5952a52b187",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/empty-manufacturerData-member.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "empty-namePrefix.https.window.js": [
-       "8ce2e649676ee0161c00c61449ed9e9d8ecceca5",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/empty-namePrefix.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "empty-services-member.https.window.js": [
-       "a24611631d360d6ec6ef65648f25d41f7ba916f2",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/empty-services-member.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "exclusion-filters-require-filters.https.window.js": [
-       "d7db260deeb6500cc8eb4809bf7368a0dc59936b",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/exclusion-filters-require-filters.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "filters-xor-acceptAllDevices.https.window.js": [
-       "a6c48f2962a321ccf08c8ad9e3d9bbea2d014f7c",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/filters-xor-acceptAllDevices.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "invalid-companyIdentifier.https.window.js": [
-       "18cdbb4b4a85fa9fc19146d72e5494161b7dffe6",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/invalid-companyIdentifier.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "mask-buffer-is-detached.https.window.js": [
-       "ae3f712db58a9e991fd5660679f8d453c2ab33a2",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/mask-buffer-is-detached.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "max-length-exceeded-name-unicode.https.window.js": [
-       "3458c92b65efa8314a2af6ab00c7084d469a103b",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name-unicode.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "max-length-exceeded-name.https.window.js": [
-       "f14f78fe7d7ecc8d68ecbd1175c1719c311c0483",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-name.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "max-length-exceeded-namePrefix-unicode.https.window.js": [
-       "b2e6668e4b98a9a0e8a23803327a890f86e593ca",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix-unicode.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "max-length-exceeded-namePrefix.https.window.js": [
-       "5d27629eaa14744e6bb436472b734d3f80cd0a0f",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/max-length-exceeded-namePrefix.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "max-length-name-unicode.https.window.js": [
-       "6a3cf5bead0eefd561064785f3289797cb0a01e3",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/max-length-name-unicode.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "max-length-name.https.window.js": [
-       "7ede93ce72166a177fa8f1db693bd49c1f7ccc2d",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/max-length-name.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "max-length-namePrefix-unicode.https.window.js": [
-       "2061e9863b4026024a4170fb9794f42692cba0f7",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix-unicode.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "max-length-namePrefix.https.window.js": [
-       "f922bb2f0d0579ed5db7012e58d961fccd9dd7c9",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/max-length-namePrefix.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "no-arguments.https.window.js": [
-       "075a97f1a9478a23dc693c169bea9cef9433f8ed",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/no-arguments.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "same-company-identifier.https.window.js": [
-       "41f851adc5ee046eab4a98be19ff8f4ebea5da27",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/same-company-identifier.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "unicode-valid-length-name-name.https.window.js": [
-       "cd10288ddb72f2cb2989bdf96f640853d29b61ee",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-name.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "unicode-valid-length-name-namePrefix.https.window.js": [
-       "494f324ee2480e40d22fe3dc05030955a2f84e83",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/unicode-valid-length-name-namePrefix.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "wrong-service-in-optionalServices-member.https.window.js": [
-       "bfba220f4799db5ac19700df083cd8896d1fa356",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-optionalServices-member.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "wrong-service-in-services-member.https.window.js": [
-       "352437d0e5a48f56572c78296ef65b7fcc73dc32",
-       [
-        "bluetooth/requestDevice/canonicalizeFilter/wrong-service-in-services-member.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "cross-origin-iframe.sub.https.window.js": [
-      "d802a862791e98e5a8694e34bd4fbaf554b9f9b5",
-      [
-       "bluetooth/requestDevice/cross-origin-iframe.sub.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+         }
         ]
-       }
-      ]
-     ],
-     "discovery-succeeds.https.window.js": [
-      "4941d185cad0508069184bc19c0ad81871e2a301",
-      [
-       "bluetooth/requestDevice/discovery-succeeds.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-garbage-collection-ran-during-error.https.window.js": [
+        "c964781ab44634e0677e5ce46ac5d5b816dff0be",
+        [
+         "bluetooth/legacy/service/getCharacteristics/gen-garbage-collection-ran-during-error.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "doesnt-consume-user-gesture.https.window.js": [
-      "9c742733e1d793d9becda42d8afd245f7c9b633d",
-      [
-       "bluetooth/requestDevice/doesnt-consume-user-gesture.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-get-same-object-with-uuid.https.window.js": [
+        "64b53f4eb31f0740e0f5c8c591c183ded92ffa79",
+        [
+         "bluetooth/legacy/service/getCharacteristics/gen-get-same-object-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "filter-matches.https.window.js": [
-      "1a0f52ac309b1ff116297ae86c27fc3008060cdb",
-      [
-       "bluetooth/requestDevice/filter-matches.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-get-same-object.https.window.js": [
+        "6aad17c1e689e7f910698a84f3505226dc77deff",
+        [
+         "bluetooth/legacy/service/getCharacteristics/gen-get-same-object.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "le-not-supported.https.window.js": [
-      "c961ab4492c42e0d62c599580b07e87c3f336040",
-      [
-       "bluetooth/requestDevice/le-not-supported.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-invalid-characteristic-name.https.window.js": [
+        "c7d439e13affcbac37a872023cd76738a922dd13",
+        [
+         "bluetooth/legacy/service/getCharacteristics/gen-invalid-characteristic-name.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "manufacturer-data-filter-matches.https.window.js": [
-      "c4c0e805328a5aa52ff21025e0ad56e0517cce7e",
-      [
-       "bluetooth/requestDevice/manufacturer-data-filter-matches.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-reconnect-during-with-uuid.https.window.js": [
+        "db373fbca1581019eada6740e49c0e8fc624ea94",
+        [
+         "bluetooth/legacy/service/getCharacteristics/gen-reconnect-during-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "name-empty-device-from-name-empty-filter.https.window.js": [
-      "2ff22cb70200809e5cbaf7dc8ac63bbf4f2f3c92",
-      [
-       "bluetooth/requestDevice/name-empty-device-from-name-empty-filter.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-reconnect-during.https.window.js": [
+        "8b3ba7cc6baa1f6f3a37536aeb51fd066a682da0",
+        [
+         "bluetooth/legacy/service/getCharacteristics/gen-reconnect-during.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "not-processing-user-gesture.https.window.js": [
-      "a063b61163019336cfe3e776ed14b8dc54aea227",
-      [
-       "bluetooth/requestDevice/not-processing-user-gesture.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-service-is-removed-with-uuid.https.window.js": [
+        "2d4db52822d7e44e50a38876747b989a4b5c5017",
+        [
+         "bluetooth/legacy/service/getCharacteristics/gen-service-is-removed-with-uuid.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "radio-not-present.https.window.js": [
-      "b55d63c6ff45fa30f973facc43de58f560a2f28c",
-      [
-       "bluetooth/requestDevice/radio-not-present.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
+       ],
+       "gen-service-is-removed.https.window.js": [
+        "f922b45cdcca6844772681663fb303f3ba430051",
+        [
+         "bluetooth/legacy/service/getCharacteristics/gen-service-is-removed.https.window.html",
+         {
+          "script_metadata": [
+           [
+            "script",
+            "/resources/testdriver.js"
+           ],
+           [
+            "script",
+            "/resources/testdriver-vendor.js"
+           ],
+           [
+            "script",
+            "/common/gc.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-test.js"
+           ],
+           [
+            "script",
+            "/bluetooth/resources/bluetooth-fake-devices.js"
+           ]
+          ]
+         }
         ]
-       }
-      ]
-     ],
-     "reject_opaque_origin.https.html": [
-      "df348dd39e0b62cb38605b94e0c98e4e155c1dbc",
-      [
-       null,
-       {}
-      ]
-     ],
-     "request-from-iframe.https.window.js": [
-      "d3f3cf897ff6f4eb97253f63843db888824a54fa",
-      [
-       "bluetooth/requestDevice/request-from-iframe.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "request-from-sandboxed-iframe.https.window.js": [
-      "2101cf0d6b747836f60d04c07774e3a100efed1f",
-      [
-       "bluetooth/requestDevice/request-from-sandboxed-iframe.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "same-device.https.window.js": [
-      "41a42cf4c8fd70c10c096c50733d9a517b77f54b",
-      [
-       "bluetooth/requestDevice/same-device.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "sandboxed_iframe.https.window.js": [
-      "e9192a9305b14763d3cabe7c6ddfb3b38f1015da",
-      [
-       "bluetooth/requestDevice/sandboxed_iframe.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "single-filter-single-service.https.window.js": [
-      "67afad0b930c7930614f3ed17a0db78e1aa20d69",
-      [
-       "bluetooth/requestDevice/single-filter-single-service.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ]
-    },
-    "requestLEScan": {
-     "reject_opaque_origin.https.html": [
-      "272c5aa760cf7f5bedc24e2d652d68164ab353d3",
-      [
-       null,
-       {}
-      ]
-     ],
-     "sandboxed_iframe.https.window.js": [
-      "32d1e74b778d9932b5104824b69cf21cf6a3ff6b",
-      [
-       "bluetooth/requestLEScan/sandboxed_iframe.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ]
-    },
-    "server": {
-     "connect": {
-      "connection-succeeds.https.window.js": [
-       "90b62b9265b1c337f05993f7169c9bd33cf6e962",
-       [
-        "bluetooth/server/connect/connection-succeeds.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
        ]
-      ],
-      "detachedIframe.https.window.js": [
-       "2332cef7070baddc84573120edb0f0dc1fc83efa",
-       [
-        "bluetooth/server/connect/detachedIframe.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "garbage-collection-ran-during-success.https.window.js": [
-       "2d2211dec30f87a988acf24fc4a283ed174405f8",
-       [
-        "bluetooth/server/connect/garbage-collection-ran-during-success.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "get-same-gatt-server.https.window.js": [
-       "59d7243a65382cd3509f425bcf6db9087c49c74f",
-       [
-        "bluetooth/server/connect/get-same-gatt-server.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "device-same-object.https.window.js": [
-      "f9a66d9b6915cf9b093ffafb6cb7fe11b3febf88",
-      [
-       "bluetooth/server/device-same-object.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "disconnect": {
-      "connect-disconnect-twice.https.window.js": [
-       "5d9908df4c8ac20d5fbea9966ff56b13a3b19de9",
-       [
-        "bluetooth/server/disconnect/connect-disconnect-twice.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "detach-gc.https.window.js": [
-       "b934b3797325a255b77e5cf39011ab2803bbb90b",
-       [
-        "bluetooth/server/disconnect/detach-gc.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "detachedIframe.https.window.js": [
-       "04e0ca0117fcc2974e02bde54802ee8d279e234a",
-       [
-        "bluetooth/server/disconnect/detachedIframe.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "disconnect-twice-in-a-row.https.window.js": [
-       "acca9796d5751d2e5c3fa73e2cd32bcd8ba31bda",
-       [
-        "bluetooth/server/disconnect/disconnect-twice-in-a-row.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gc-detach.https.window.js": [
-       "1c062a775903bf0396a33592231851e2da2b7660",
-       [
-        "bluetooth/server/disconnect/gc-detach.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "getPrimaryService": {
-      "gen-disconnect-called-before.https.window.js": [
-       "631545a38542689762605f53af4ed7a7753a9134",
-       [
-        "bluetooth/server/getPrimaryService/gen-disconnect-called-before.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnect-called-during-error.https.window.js": [
-       "bcf19665d5fcf2cd720717f3f602f9d9795a6a33",
-       [
-        "bluetooth/server/getPrimaryService/gen-disconnect-called-during-error.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnect-called-during-success.https.window.js": [
-       "0d2fc1044a5bbb4c205741db61a3946d5b37c724",
-       [
-        "bluetooth/server/getPrimaryService/gen-disconnect-called-during-success.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnect-discovery-timeout.https.window.js": [
-       "03b0c9d0f382c3462beed1ee282321a7d91d94f1",
-       [
-        "bluetooth/server/getPrimaryService/gen-disconnect-discovery-timeout.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnect-invalidates-objects.https.window.js": [
-       "56468b24eabf2d99a3f4a10eef7cdbc0c2594ebc",
-       [
-        "bluetooth/server/getPrimaryService/gen-disconnect-invalidates-objects.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnected-device.https.window.js": [
-       "741b2db5ee82e8f268fca71143fd419bda0af87b",
-       [
-        "bluetooth/server/getPrimaryService/gen-disconnected-device.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-discovery-complete-no-permission-absent-service.https.window.js": [
-       "e2f5c87630559a7a5fef08bf37b68e84d3356648",
-       [
-        "bluetooth/server/getPrimaryService/gen-discovery-complete-no-permission-absent-service.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-discovery-complete-service-not-found.https.window.js": [
-       "8e9166b41a140832ccc121b6ec63415fccb5bbca",
-       [
-        "bluetooth/server/getPrimaryService/gen-discovery-complete-service-not-found.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-garbage-collection-ran-during-error.https.window.js": [
-       "df182fe8fff6b3001d355fe4c70b67b9b64bdc56",
-       [
-        "bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-error.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-garbage-collection-ran-during-success.https.window.js": [
-       "8e278af224234f1ab005e52cfff0fafcfe729537",
-       [
-        "bluetooth/server/getPrimaryService/gen-garbage-collection-ran-during-success.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-get-different-service-after-reconnection.https.window.js": [
-       "d4557f67535001a64a0b7347e336f95db547fe6c",
-       [
-        "bluetooth/server/getPrimaryService/gen-get-different-service-after-reconnection.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-get-same-object.https.window.js": [
-       "b43cefb567f01d0b7cdb8b53bc3da40e785fe007",
-       [
-        "bluetooth/server/getPrimaryService/gen-get-same-object.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-invalid-service-name.https.window.js": [
-       "cf4ab6c665d6a9605507b01e1e0c3e1a807376ba",
-       [
-        "bluetooth/server/getPrimaryService/gen-invalid-service-name.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-no-permission-absent-service.https.window.js": [
-       "3466ded4f9e910d30c3f5a55dd3e1172addeb3d8",
-       [
-        "bluetooth/server/getPrimaryService/gen-no-permission-absent-service.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-no-permission-for-any-service.https.window.js": [
-       "6576ef20a3de120529c4bf2997b13b40d7ce3f26",
-       [
-        "bluetooth/server/getPrimaryService/gen-no-permission-for-any-service.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-no-permission-present-service.https.window.js": [
-       "3d0b460bc33b9e246dfa59d30248edefd7d13d6a",
-       [
-        "bluetooth/server/getPrimaryService/gen-no-permission-present-service.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-service-not-found.https.window.js": [
-       "6e0d2c446ba90199f1bab1158e6147ed632deda6",
-       [
-        "bluetooth/server/getPrimaryService/gen-service-not-found.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "service-found.https.window.js": [
-       "b8a930d10c79b345f7a0c6c6ec9d89b095dccec4",
-       [
-        "bluetooth/server/getPrimaryService/service-found.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "two-iframes-from-same-origin.https.window.js": [
-       "b7f23a14913914d3e3bc2f57b98b2b1cc91a185c",
-       [
-        "bluetooth/server/getPrimaryService/two-iframes-from-same-origin.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "getPrimaryServices": {
-      "blocklisted-services-with-uuid.https.window.js": [
-       "ccc913e5bfdc7df6efe6627dc97899e12f981660",
-       [
-        "bluetooth/server/getPrimaryServices/blocklisted-services-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "blocklisted-services.https.window.js": [
-       "ae6be9099466b725521e54bec72013bcf175762a",
-       [
-        "bluetooth/server/getPrimaryServices/blocklisted-services.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "correct-services.https.window.js": [
-       "f3d883dd2ef181ca7c7a88999a9620056a42f046",
-       [
-        "bluetooth/server/getPrimaryServices/correct-services.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnect-called-before-with-uuid.https.window.js": [
-       "21b561375dfed914f011a4de18148053087a6c4f",
-       [
-        "bluetooth/server/getPrimaryServices/gen-disconnect-called-before-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnect-called-before.https.window.js": [
-       "8e5fea83ab4e53299ed6f5c78bd5d0322d2cf410",
-       [
-        "bluetooth/server/getPrimaryServices/gen-disconnect-called-before.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnect-called-during-error-with-uuid.https.window.js": [
-       "5c28716b90bf32e0e25e5e16ac37fc085f50fb9a",
-       [
-        "bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnect-called-during-error.https.window.js": [
-       "ddc31247911d64f1995cdae41af9da15a11bb890",
-       [
-        "bluetooth/server/getPrimaryServices/gen-disconnect-called-during-error.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnect-called-during-success-with-uuid.https.window.js": [
-       "13e3806d313e40ffc1d69503ad9ebb18cf4630ba",
-       [
-        "bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnect-called-during-success.https.window.js": [
-       "d6b31936c60a9a17af6a4764ac16b7580b3649a9",
-       [
-        "bluetooth/server/getPrimaryServices/gen-disconnect-called-during-success.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnect-discovery-timeout-with-uuid.https.window.js": [
-       "77f7bc81d99cc2f0b7f72e5a5866d0d89e625dce",
-       [
-        "bluetooth/server/getPrimaryServices/gen-disconnect-discovery-timeout-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnect-discovery-timeout.https.window.js": [
-       "ea55b7b495259c58027ac24be4b918585cb44fed",
-       [
-        "bluetooth/server/getPrimaryServices/gen-disconnect-discovery-timeout.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnect-invalidates-objects-with-uuid.https.window.js": [
-       "8cdb83e3ad7a2fea327c45b67388082c4e58e09e",
-       [
-        "bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnect-invalidates-objects.https.window.js": [
-       "9fd536f05161e0582e88ecbf2f5987a92214a4ba",
-       [
-        "bluetooth/server/getPrimaryServices/gen-disconnect-invalidates-objects.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnected-device-with-uuid.https.window.js": [
-       "e0393d5e69ca867e09b049576c33efbbc397866d",
-       [
-        "bluetooth/server/getPrimaryServices/gen-disconnected-device-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-disconnected-device.https.window.js": [
-       "87d74c6ab14083fdd6ed32aa74d01e5b1206b53d",
-       [
-        "bluetooth/server/getPrimaryServices/gen-disconnected-device.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-discovery-complete-no-permission-absent-service-with-uuid.https.window.js": [
-       "6e179dc5d9a373a6a10c17a8083116e464264658",
-       [
-        "bluetooth/server/getPrimaryServices/gen-discovery-complete-no-permission-absent-service-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-discovery-complete-service-not-found-with-uuid.https.window.js": [
-       "66cfb491c078d7ca378cb4418d399a0b33a9d07b",
-       [
-        "bluetooth/server/getPrimaryServices/gen-discovery-complete-service-not-found-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-garbage-collection-ran-during-error-with-uuid.https.window.js": [
-       "a235cf5d189bebdaf2d8e6b04e5796075fb34098",
-       [
-        "bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-garbage-collection-ran-during-error.https.window.js": [
-       "f174d4aef98dbe1fb3fa148992cab34b8a475263",
-       [
-        "bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-error.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-garbage-collection-ran-during-success-with-uuid.https.window.js": [
-       "cf5dfb246fbd12ef3a4597926cdbf877bae2cc4a",
-       [
-        "bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-garbage-collection-ran-during-success.https.window.js": [
-       "f1c080a9463b79462bab70dbe611ee01ea6880b8",
-       [
-        "bluetooth/server/getPrimaryServices/gen-garbage-collection-ran-during-success.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-get-different-service-after-reconnection-with-uuid.https.window.js": [
-       "2e40d580f3399ddccd7b84d60f9ffa87b0fb2d51",
-       [
-        "bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-get-different-service-after-reconnection.https.window.js": [
-       "ee1fc971bfa942e4f0c204acb80b055f311dc611",
-       [
-        "bluetooth/server/getPrimaryServices/gen-get-different-service-after-reconnection.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-get-same-object-with-uuid.https.window.js": [
-       "b589056a23a9e67ad9673713bab2cd2e93c1b8f9",
-       [
-        "bluetooth/server/getPrimaryServices/gen-get-same-object-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-get-same-object.https.window.js": [
-       "63739add912351f1da86a4cfdcd02d4cfd8567fa",
-       [
-        "bluetooth/server/getPrimaryServices/gen-get-same-object.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-invalid-service-name.https.window.js": [
-       "a9b1262e6a181d1017ead79b1972ecd85e942098",
-       [
-        "bluetooth/server/getPrimaryServices/gen-invalid-service-name.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-no-permission-absent-service-with-uuid.https.window.js": [
-       "27ad9f008ee0d296dcbcf1c27199ce11a45e6fdd",
-       [
-        "bluetooth/server/getPrimaryServices/gen-no-permission-absent-service-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-no-permission-for-any-service-with-uuid.https.window.js": [
-       "d5f06c23da6660566fbbbdbf8a5dccaaa14904fe",
-       [
-        "bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-no-permission-for-any-service.https.window.js": [
-       "8aa730d2ed2d8de1868d705e026a6a8a2376ee27",
-       [
-        "bluetooth/server/getPrimaryServices/gen-no-permission-for-any-service.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-no-permission-present-service-with-uuid.https.window.js": [
-       "a2047a0e8f0e2236cc968b309aa5340ee5b97a90",
-       [
-        "bluetooth/server/getPrimaryServices/gen-no-permission-present-service-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-service-not-found-with-uuid.https.window.js": [
-       "a2db1edc4b9f30d73b39eb530894983323135090",
-       [
-        "bluetooth/server/getPrimaryServices/gen-service-not-found-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "services-found-with-uuid.https.window.js": [
-       "972e6a75caa3f21935abe9b504d0bccf5249c6a1",
-       [
-        "bluetooth/server/getPrimaryServices/services-found-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "services-found.https.window.js": [
-       "46861175c605d4e2d966df8751d709530f411f02",
-       [
-        "bluetooth/server/getPrimaryServices/services-found.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "services-not-found.https.window.js": [
-       "63503282411b80eba908b91e72ea7a0946945019",
-       [
-        "bluetooth/server/getPrimaryServices/services-not-found.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     }
-    },
-    "service": {
-     "detachedIframe.https.window.js": [
-      "f75fc225a791b57bff086fa8acad026521e22ce4",
-      [
-       "bluetooth/service/detachedIframe.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/common/gc.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "device-same-from-2-services.https.window.js": [
-      "5b2ba310d356d3eb4f7c0f76ac992bb2f8056034",
-      [
-       "bluetooth/service/device-same-from-2-services.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "device-same-object.https.window.js": [
-      "97da769a9eac25b55e72ac9e44d6c3cf1442ec3a",
-      [
-       "bluetooth/service/device-same-object.https.window.html",
-       {
-        "script_metadata": [
-         [
-          "script",
-          "/resources/testdriver.js"
-         ],
-         [
-          "script",
-          "/resources/testdriver-vendor.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-test.js"
-         ],
-         [
-          "script",
-          "/bluetooth/resources/bluetooth-fake-devices.js"
-         ]
-        ]
-       }
-      ]
-     ],
-     "getCharacteristic": {
-      "characteristic-found.https.window.js": [
-       "807852ae133c0f024925faaee88309b9f585ea47",
-       [
-        "bluetooth/service/getCharacteristic/characteristic-found.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "detachedIframe.https.window.js": [
-       "ea8c96160ff9dfa2498fd9d51f167ddc80e4aa6a",
-       [
-        "bluetooth/service/getCharacteristic/detachedIframe.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-blocklisted-characteristic.https.window.js": [
-       "cce302d6504f51db4d67553d8f57b915d5ffc645",
-       [
-        "bluetooth/service/getCharacteristic/gen-blocklisted-characteristic.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-characteristic-not-found.https.window.js": [
-       "2ed48eb5c68798e402193ca195c74c86dbf8909e",
-       [
-        "bluetooth/service/getCharacteristic/gen-characteristic-not-found.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-garbage-collection-ran-during-error.https.window.js": [
-       "1fd70c8fad4b454cb0687d48645117edb801c4de",
-       [
-        "bluetooth/service/getCharacteristic/gen-garbage-collection-ran-during-error.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-get-same-object.https.window.js": [
-       "c5176cdc5e0a7a310c60e1d4ed15b07dbeb5e7bb",
-       [
-        "bluetooth/service/getCharacteristic/gen-get-same-object.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-invalid-characteristic-name.https.window.js": [
-       "da0f5bda28080f7486e95a3f14ee1f8838c3bc62",
-       [
-        "bluetooth/service/getCharacteristic/gen-invalid-characteristic-name.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-reconnect-during.https.window.js": [
-       "8801c152e931ef3e631cc7ec649c83254c59b3a2",
-       [
-        "bluetooth/service/getCharacteristic/gen-reconnect-during.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-service-is-removed.https.window.js": [
-       "bfeb318c46ea54136137f390adaae0a7322dd827",
-       [
-        "bluetooth/service/getCharacteristic/gen-service-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
-     },
-     "getCharacteristics": {
-      "blocklisted-characteristics.https.window.js": [
-       "408943585ab86eb3ed73454f82b862820a469aa8",
-       [
-        "bluetooth/service/getCharacteristics/blocklisted-characteristics.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "characteristics-found-with-uuid.https.window.js": [
-       "f11c69c92e7aed8b177c255a071ef9b3af918a4e",
-       [
-        "bluetooth/service/getCharacteristics/characteristics-found-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "characteristics-found.https.window.js": [
-       "3244dd3e173d233b65db3ede450464fbc763a531",
-       [
-        "bluetooth/service/getCharacteristics/characteristics-found.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "characteristics-not-found.https.window.js": [
-       "5b0c1896d64f01b83349a9152cc561ac0b435438",
-       [
-        "bluetooth/service/getCharacteristics/characteristics-not-found.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-blocklisted-characteristic-with-uuid.https.window.js": [
-       "79cd01032b580debd840d885c350d44aa7b5e974",
-       [
-        "bluetooth/service/getCharacteristics/gen-blocklisted-characteristic-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-characteristic-not-found-with-uuid.https.window.js": [
-       "8a5e2ab4e4ac8e1534f0ccb3666fe5ee71645ec9",
-       [
-        "bluetooth/service/getCharacteristics/gen-characteristic-not-found-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-garbage-collection-ran-during-error-with-uuid.https.window.js": [
-       "683b93e352d0e59fce29c48585d419febd850fb6",
-       [
-        "bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-garbage-collection-ran-during-error.https.window.js": [
-       "c964781ab44634e0677e5ce46ac5d5b816dff0be",
-       [
-        "bluetooth/service/getCharacteristics/gen-garbage-collection-ran-during-error.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-get-same-object-with-uuid.https.window.js": [
-       "64b53f4eb31f0740e0f5c8c591c183ded92ffa79",
-       [
-        "bluetooth/service/getCharacteristics/gen-get-same-object-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-get-same-object.https.window.js": [
-       "6aad17c1e689e7f910698a84f3505226dc77deff",
-       [
-        "bluetooth/service/getCharacteristics/gen-get-same-object.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-invalid-characteristic-name.https.window.js": [
-       "c7d439e13affcbac37a872023cd76738a922dd13",
-       [
-        "bluetooth/service/getCharacteristics/gen-invalid-characteristic-name.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-reconnect-during-with-uuid.https.window.js": [
-       "db373fbca1581019eada6740e49c0e8fc624ea94",
-       [
-        "bluetooth/service/getCharacteristics/gen-reconnect-during-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-reconnect-during.https.window.js": [
-       "8b3ba7cc6baa1f6f3a37536aeb51fd066a682da0",
-       [
-        "bluetooth/service/getCharacteristics/gen-reconnect-during.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-service-is-removed-with-uuid.https.window.js": [
-       "2d4db52822d7e44e50a38876747b989a4b5c5017",
-       [
-        "bluetooth/service/getCharacteristics/gen-service-is-removed-with-uuid.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ],
-      "gen-service-is-removed.https.window.js": [
-       "f922b45cdcca6844772681663fb303f3ba430051",
-       [
-        "bluetooth/service/getCharacteristics/gen-service-is-removed.https.window.html",
-        {
-         "script_metadata": [
-          [
-           "script",
-           "/resources/testdriver.js"
-          ],
-          [
-           "script",
-           "/resources/testdriver-vendor.js"
-          ],
-          [
-           "script",
-           "/common/gc.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-test.js"
-          ],
-          [
-           "script",
-           "/bluetooth/resources/bluetooth-fake-devices.js"
-          ]
-         ]
-        }
-       ]
-      ]
+      }
      }
     }
    },
@@ -457263,6 +457279,15 @@
     ]
    },
    "clear-site-data": {
+    "clear-cache.https.html": [
+     "8cdaf2a55271987943dfb25a37d98295068b57b2",
+     [
+      null,
+      {
+       "testdriver": true
+      }
+     ]
+    ],
     "executionContexts.sub.html": [
      "b3ae17576a3b09fe576c24d8d454b5ef504832a8",
      [
@@ -470625,7 +470650,7 @@
      ]
     ],
     "cookieListItem_attributes.https.any.js": [
-     "200cbd06928ff27d2574103fd82f0ae610dff5a4",
+     "6716d91788db746765593a2a95d34e1ecbb4e3a5",
      [
       "cookie-store/cookieListItem_attributes.https.any.html",
       {
@@ -470781,7 +470806,7 @@
      ]
     ],
     "cookieStore_delete_arguments.https.any.js": [
-     "ddae23888f4f2a7ff203b4189afeb1ced11b0a3d",
+     "37a551b3a9f2511d915bb06b4d2311e3e7979ae2",
      [
       "cookie-store/cookieStore_delete_arguments.https.any.html",
       {
@@ -470973,6 +470998,24 @@
       }
      ]
     ],
+    "cookieStore_getAll_set_creation_url.https.any.js": [
+     "d09c4aad4f87712afd70faf8111ee21f88d5f1a8",
+     [
+      "cookie-store/cookieStore_getAll_set_creation_url.https.any.html",
+      {
+       "script_metadata": [
+        [
+         "title",
+         "Cookie Store API: cookieStore.set()/getAll() with Document URL changing"
+        ],
+        [
+         "global",
+         "window"
+        ]
+       ]
+      }
+     ]
+    ],
     "cookieStore_get_arguments.https.any.js": [
      "d6ea0edef6a3d4e0cc69d4455d6672ddd433deda",
      [
@@ -471086,6 +471129,24 @@
       }
      ]
     ],
+    "cookieStore_get_set_creation_url.https.any.js": [
+     "71f8108095f0e78518f73f5086eb0d2d4dee1253",
+     [
+      "cookie-store/cookieStore_get_set_creation_url.https.any.html",
+      {
+       "script_metadata": [
+        [
+         "title",
+         "Cookie Store API: cookieStore.set()/get() with Document URL changing"
+        ],
+        [
+         "global",
+         "window"
+        ]
+       ]
+      }
+     ]
+    ],
     "cookieStore_get_set_creation_url.sub.https.html": [
      "216885298a58829d64653d7e1947fe3e6ec52012",
      [
@@ -471141,7 +471202,7 @@
      ]
     ],
     "cookieStore_set_arguments.https.any.js": [
-     "8ff66bf7772f22076360fda0b735a7894ed6ad8d",
+     "064fcc5de529f42c78b853f66f1a5e2f56e6ac01",
      [
       "cookie-store/cookieStore_set_arguments.https.any.html",
       {
@@ -479998,14 +480059,14 @@
        ]
       ],
       "at-container-parsing.html": [
-       "79250dfde066dd96362b773b944a8a4fde2a2906",
+       "f76e7988a3dee2aee8d20ba2e5cb747218f5cde4",
        [
         null,
         {}
        ]
       ],
       "at-container-serialization.html": [
-       "dbe9ea023e885c3446ba05063f0a005542ea05ed",
+       "b0c9746e0cc1359f8eea031399374d310f7ec140",
        [
         null,
         {}
@@ -613459,6 +613520,15 @@
        {}
       ]
      ],
+     "reporting-subresource-corp.tentative.https.html": [
+      "0530956080bb3d027fc1f88ed23572f0e7b9ba02",
+      [
+       null,
+       {
+        "timeout": "long"
+       }
+      ]
+     ],
      "service-worker-coep-credentialless-proxy.https.tentative.window.js": [
       "e12c660f7b42f16604bbd3394386876069a4998c",
       [
@@ -626491,6 +626561,15 @@
           }
          ]
         ],
+        "select-click-drag-option.tentative.html": [
+         "748c4a92304df58be415a9992c1ac3cd6ca11176",
+         [
+          null,
+          {
+           "testdriver": true
+          }
+         ]
+        ],
         "select-datalist-options-idl.tentative.html": [
          "e00fb1951d499a276f15f74c6bd288d79333fa7a",
          [
@@ -628146,7 +628225,7 @@
       ]
      },
      "popovers": {
-      "button-type-reset-popovertarget.tentative.html": [
+      "button-type-reset-popovertarget.html": [
        "975eab0d66eabd7216c51a999d87a0cd145898b4",
        [
         null,
@@ -628240,7 +628319,7 @@
        ]
       ],
       "popover-attribute-basic.html": [
-       "f13a05288c3e03acf219810022f623ba27793998",
+       "067ce18f6a0b8ccae75c8a774c5a70243a555b32",
        [
         null,
         {
@@ -628382,7 +628461,7 @@
         }
        ]
       ],
-      "popover-invoking-attribute-hint.tentative.html": [
+      "popover-invoking-attribute-hint.html": [
        "b531ddc460e83ac57e6716686b1ffae353625d5f",
        [
         null,
@@ -628420,7 +628499,7 @@
         }
        ]
       ],
-      "popover-light-dismiss-hint.tentative.html": [
+      "popover-light-dismiss-hint.html": [
        "f07363115fba687ef97308fe515abf1c373497ab",
        [
         null,
@@ -628614,7 +628693,7 @@
         }
        ]
       ],
-      "popover-top-layer-nesting-hints.tentative.html": [
+      "popover-top-layer-nesting-hints.html": [
        "4ec1f49bda9fbd2f61a1e0bc7a719de1143699e8",
        [
         null,
@@ -628632,7 +628711,7 @@
         }
        ]
       ],
-      "popover-types-with-hints.tentative.html": [
+      "popover-types-with-hints.html": [
        "07f0e26fce71b922385d804e5ce5d56ac3af95b3",
        [
         null,
@@ -653858,6 +653937,13 @@
       {}
      ]
     ],
+    "content-encoding.https.html": [
+     "5450b5913bd561613a210885284e5d1531ae0793",
+     [
+      "navigation-timing/content-encoding.https.html?pipe=gzip",
+      {}
+     ]
+    ],
     "dom-interactive-image-document.html": [
      "36742f0eff6d07c7c0694b066dfa017f0d1042be",
      [
@@ -655789,9 +655875,9 @@
      ]
     ],
     "partitioned-popins.partitions.tentative.https.window.js": [
-     "0f1a7c0a6e0c91793d015150124bc0059400a791",
+     "ed664f71e105a14b7cc3f002340796ca2928d936",
      [
-      "partitioned-popins/partitioned-popins.partitions.tentative.https.window.html",
+      "partitioned-popins/partitioned-popins.partitions.tentative.https.window.html?include=variant-1-test",
       {
        "script_metadata": [
         [
@@ -655816,9 +655902,897 @@
         ],
         [
          "script",
+         "/common/subset-tests-by-key.js"
+        ],
+        [
+         "script",
          "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
         ],
         [
+         "variant",
+         "?include=variant-1-test"
+        ],
+        [
+         "variant",
+         "?include=variant-2-test"
+        ],
+        [
+         "variant",
+         "?include=variant-3-test"
+        ],
+        [
+         "variant",
+         "?include=variant-4-test"
+        ],
+        [
+         "variant",
+         "?include=variant-5-test"
+        ],
+        [
+         "variant",
+         "?include=variant-6-test"
+        ],
+        [
+         "variant",
+         "?include=variant-7-test"
+        ],
+        [
+         "variant",
+         "?include=variant-8-test"
+        ],
+        [
+         "variant",
+         "?include=variant-9-test"
+        ],
+        [
+         "variant",
+         "?include=variant-10-test"
+        ],
+        [
+         "variant",
+         "?include=variant-11-test"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ],
+     [
+      "partitioned-popins/partitioned-popins.partitions.tentative.https.window.html?include=variant-10-test",
+      {
+       "script_metadata": [
+        [
+         "script",
+         "/resources/testdriver.js"
+        ],
+        [
+         "script",
+         "/resources/testdriver-vendor.js"
+        ],
+        [
+         "script",
+         "/common/dispatcher/dispatcher.js"
+        ],
+        [
+         "script",
+         "/common/get-host-info.sub.js"
+        ],
+        [
+         "script",
+         "/common/utils.js"
+        ],
+        [
+         "script",
+         "/common/subset-tests-by-key.js"
+        ],
+        [
+         "script",
+         "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+        ],
+        [
+         "variant",
+         "?include=variant-1-test"
+        ],
+        [
+         "variant",
+         "?include=variant-2-test"
+        ],
+        [
+         "variant",
+         "?include=variant-3-test"
+        ],
+        [
+         "variant",
+         "?include=variant-4-test"
+        ],
+        [
+         "variant",
+         "?include=variant-5-test"
+        ],
+        [
+         "variant",
+         "?include=variant-6-test"
+        ],
+        [
+         "variant",
+         "?include=variant-7-test"
+        ],
+        [
+         "variant",
+         "?include=variant-8-test"
+        ],
+        [
+         "variant",
+         "?include=variant-9-test"
+        ],
+        [
+         "variant",
+         "?include=variant-10-test"
+        ],
+        [
+         "variant",
+         "?include=variant-11-test"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ],
+     [
+      "partitioned-popins/partitioned-popins.partitions.tentative.https.window.html?include=variant-11-test",
+      {
+       "script_metadata": [
+        [
+         "script",
+         "/resources/testdriver.js"
+        ],
+        [
+         "script",
+         "/resources/testdriver-vendor.js"
+        ],
+        [
+         "script",
+         "/common/dispatcher/dispatcher.js"
+        ],
+        [
+         "script",
+         "/common/get-host-info.sub.js"
+        ],
+        [
+         "script",
+         "/common/utils.js"
+        ],
+        [
+         "script",
+         "/common/subset-tests-by-key.js"
+        ],
+        [
+         "script",
+         "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+        ],
+        [
+         "variant",
+         "?include=variant-1-test"
+        ],
+        [
+         "variant",
+         "?include=variant-2-test"
+        ],
+        [
+         "variant",
+         "?include=variant-3-test"
+        ],
+        [
+         "variant",
+         "?include=variant-4-test"
+        ],
+        [
+         "variant",
+         "?include=variant-5-test"
+        ],
+        [
+         "variant",
+         "?include=variant-6-test"
+        ],
+        [
+         "variant",
+         "?include=variant-7-test"
+        ],
+        [
+         "variant",
+         "?include=variant-8-test"
+        ],
+        [
+         "variant",
+         "?include=variant-9-test"
+        ],
+        [
+         "variant",
+         "?include=variant-10-test"
+        ],
+        [
+         "variant",
+         "?include=variant-11-test"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ],
+     [
+      "partitioned-popins/partitioned-popins.partitions.tentative.https.window.html?include=variant-2-test",
+      {
+       "script_metadata": [
+        [
+         "script",
+         "/resources/testdriver.js"
+        ],
+        [
+         "script",
+         "/resources/testdriver-vendor.js"
+        ],
+        [
+         "script",
+         "/common/dispatcher/dispatcher.js"
+        ],
+        [
+         "script",
+         "/common/get-host-info.sub.js"
+        ],
+        [
+         "script",
+         "/common/utils.js"
+        ],
+        [
+         "script",
+         "/common/subset-tests-by-key.js"
+        ],
+        [
+         "script",
+         "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+        ],
+        [
+         "variant",
+         "?include=variant-1-test"
+        ],
+        [
+         "variant",
+         "?include=variant-2-test"
+        ],
+        [
+         "variant",
+         "?include=variant-3-test"
+        ],
+        [
+         "variant",
+         "?include=variant-4-test"
+        ],
+        [
+         "variant",
+         "?include=variant-5-test"
+        ],
+        [
+         "variant",
+         "?include=variant-6-test"
+        ],
+        [
+         "variant",
+         "?include=variant-7-test"
+        ],
+        [
+         "variant",
+         "?include=variant-8-test"
+        ],
+        [
+         "variant",
+         "?include=variant-9-test"
+        ],
+        [
+         "variant",
+         "?include=variant-10-test"
+        ],
+        [
+         "variant",
+         "?include=variant-11-test"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ],
+     [
+      "partitioned-popins/partitioned-popins.partitions.tentative.https.window.html?include=variant-3-test",
+      {
+       "script_metadata": [
+        [
+         "script",
+         "/resources/testdriver.js"
+        ],
+        [
+         "script",
+         "/resources/testdriver-vendor.js"
+        ],
+        [
+         "script",
+         "/common/dispatcher/dispatcher.js"
+        ],
+        [
+         "script",
+         "/common/get-host-info.sub.js"
+        ],
+        [
+         "script",
+         "/common/utils.js"
+        ],
+        [
+         "script",
+         "/common/subset-tests-by-key.js"
+        ],
+        [
+         "script",
+         "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+        ],
+        [
+         "variant",
+         "?include=variant-1-test"
+        ],
+        [
+         "variant",
+         "?include=variant-2-test"
+        ],
+        [
+         "variant",
+         "?include=variant-3-test"
+        ],
+        [
+         "variant",
+         "?include=variant-4-test"
+        ],
+        [
+         "variant",
+         "?include=variant-5-test"
+        ],
+        [
+         "variant",
+         "?include=variant-6-test"
+        ],
+        [
+         "variant",
+         "?include=variant-7-test"
+        ],
+        [
+         "variant",
+         "?include=variant-8-test"
+        ],
+        [
+         "variant",
+         "?include=variant-9-test"
+        ],
+        [
+         "variant",
+         "?include=variant-10-test"
+        ],
+        [
+         "variant",
+         "?include=variant-11-test"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ],
+     [
+      "partitioned-popins/partitioned-popins.partitions.tentative.https.window.html?include=variant-4-test",
+      {
+       "script_metadata": [
+        [
+         "script",
+         "/resources/testdriver.js"
+        ],
+        [
+         "script",
+         "/resources/testdriver-vendor.js"
+        ],
+        [
+         "script",
+         "/common/dispatcher/dispatcher.js"
+        ],
+        [
+         "script",
+         "/common/get-host-info.sub.js"
+        ],
+        [
+         "script",
+         "/common/utils.js"
+        ],
+        [
+         "script",
+         "/common/subset-tests-by-key.js"
+        ],
+        [
+         "script",
+         "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+        ],
+        [
+         "variant",
+         "?include=variant-1-test"
+        ],
+        [
+         "variant",
+         "?include=variant-2-test"
+        ],
+        [
+         "variant",
+         "?include=variant-3-test"
+        ],
+        [
+         "variant",
+         "?include=variant-4-test"
+        ],
+        [
+         "variant",
+         "?include=variant-5-test"
+        ],
+        [
+         "variant",
+         "?include=variant-6-test"
+        ],
+        [
+         "variant",
+         "?include=variant-7-test"
+        ],
+        [
+         "variant",
+         "?include=variant-8-test"
+        ],
+        [
+         "variant",
+         "?include=variant-9-test"
+        ],
+        [
+         "variant",
+         "?include=variant-10-test"
+        ],
+        [
+         "variant",
+         "?include=variant-11-test"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ],
+     [
+      "partitioned-popins/partitioned-popins.partitions.tentative.https.window.html?include=variant-5-test",
+      {
+       "script_metadata": [
+        [
+         "script",
+         "/resources/testdriver.js"
+        ],
+        [
+         "script",
+         "/resources/testdriver-vendor.js"
+        ],
+        [
+         "script",
+         "/common/dispatcher/dispatcher.js"
+        ],
+        [
+         "script",
+         "/common/get-host-info.sub.js"
+        ],
+        [
+         "script",
+         "/common/utils.js"
+        ],
+        [
+         "script",
+         "/common/subset-tests-by-key.js"
+        ],
+        [
+         "script",
+         "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+        ],
+        [
+         "variant",
+         "?include=variant-1-test"
+        ],
+        [
+         "variant",
+         "?include=variant-2-test"
+        ],
+        [
+         "variant",
+         "?include=variant-3-test"
+        ],
+        [
+         "variant",
+         "?include=variant-4-test"
+        ],
+        [
+         "variant",
+         "?include=variant-5-test"
+        ],
+        [
+         "variant",
+         "?include=variant-6-test"
+        ],
+        [
+         "variant",
+         "?include=variant-7-test"
+        ],
+        [
+         "variant",
+         "?include=variant-8-test"
+        ],
+        [
+         "variant",
+         "?include=variant-9-test"
+        ],
+        [
+         "variant",
+         "?include=variant-10-test"
+        ],
+        [
+         "variant",
+         "?include=variant-11-test"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ],
+     [
+      "partitioned-popins/partitioned-popins.partitions.tentative.https.window.html?include=variant-6-test",
+      {
+       "script_metadata": [
+        [
+         "script",
+         "/resources/testdriver.js"
+        ],
+        [
+         "script",
+         "/resources/testdriver-vendor.js"
+        ],
+        [
+         "script",
+         "/common/dispatcher/dispatcher.js"
+        ],
+        [
+         "script",
+         "/common/get-host-info.sub.js"
+        ],
+        [
+         "script",
+         "/common/utils.js"
+        ],
+        [
+         "script",
+         "/common/subset-tests-by-key.js"
+        ],
+        [
+         "script",
+         "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+        ],
+        [
+         "variant",
+         "?include=variant-1-test"
+        ],
+        [
+         "variant",
+         "?include=variant-2-test"
+        ],
+        [
+         "variant",
+         "?include=variant-3-test"
+        ],
+        [
+         "variant",
+         "?include=variant-4-test"
+        ],
+        [
+         "variant",
+         "?include=variant-5-test"
+        ],
+        [
+         "variant",
+         "?include=variant-6-test"
+        ],
+        [
+         "variant",
+         "?include=variant-7-test"
+        ],
+        [
+         "variant",
+         "?include=variant-8-test"
+        ],
+        [
+         "variant",
+         "?include=variant-9-test"
+        ],
+        [
+         "variant",
+         "?include=variant-10-test"
+        ],
+        [
+         "variant",
+         "?include=variant-11-test"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ],
+     [
+      "partitioned-popins/partitioned-popins.partitions.tentative.https.window.html?include=variant-7-test",
+      {
+       "script_metadata": [
+        [
+         "script",
+         "/resources/testdriver.js"
+        ],
+        [
+         "script",
+         "/resources/testdriver-vendor.js"
+        ],
+        [
+         "script",
+         "/common/dispatcher/dispatcher.js"
+        ],
+        [
+         "script",
+         "/common/get-host-info.sub.js"
+        ],
+        [
+         "script",
+         "/common/utils.js"
+        ],
+        [
+         "script",
+         "/common/subset-tests-by-key.js"
+        ],
+        [
+         "script",
+         "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+        ],
+        [
+         "variant",
+         "?include=variant-1-test"
+        ],
+        [
+         "variant",
+         "?include=variant-2-test"
+        ],
+        [
+         "variant",
+         "?include=variant-3-test"
+        ],
+        [
+         "variant",
+         "?include=variant-4-test"
+        ],
+        [
+         "variant",
+         "?include=variant-5-test"
+        ],
+        [
+         "variant",
+         "?include=variant-6-test"
+        ],
+        [
+         "variant",
+         "?include=variant-7-test"
+        ],
+        [
+         "variant",
+         "?include=variant-8-test"
+        ],
+        [
+         "variant",
+         "?include=variant-9-test"
+        ],
+        [
+         "variant",
+         "?include=variant-10-test"
+        ],
+        [
+         "variant",
+         "?include=variant-11-test"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ],
+     [
+      "partitioned-popins/partitioned-popins.partitions.tentative.https.window.html?include=variant-8-test",
+      {
+       "script_metadata": [
+        [
+         "script",
+         "/resources/testdriver.js"
+        ],
+        [
+         "script",
+         "/resources/testdriver-vendor.js"
+        ],
+        [
+         "script",
+         "/common/dispatcher/dispatcher.js"
+        ],
+        [
+         "script",
+         "/common/get-host-info.sub.js"
+        ],
+        [
+         "script",
+         "/common/utils.js"
+        ],
+        [
+         "script",
+         "/common/subset-tests-by-key.js"
+        ],
+        [
+         "script",
+         "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+        ],
+        [
+         "variant",
+         "?include=variant-1-test"
+        ],
+        [
+         "variant",
+         "?include=variant-2-test"
+        ],
+        [
+         "variant",
+         "?include=variant-3-test"
+        ],
+        [
+         "variant",
+         "?include=variant-4-test"
+        ],
+        [
+         "variant",
+         "?include=variant-5-test"
+        ],
+        [
+         "variant",
+         "?include=variant-6-test"
+        ],
+        [
+         "variant",
+         "?include=variant-7-test"
+        ],
+        [
+         "variant",
+         "?include=variant-8-test"
+        ],
+        [
+         "variant",
+         "?include=variant-9-test"
+        ],
+        [
+         "variant",
+         "?include=variant-10-test"
+        ],
+        [
+         "variant",
+         "?include=variant-11-test"
+        ],
+        [
+         "timeout",
+         "long"
+        ]
+       ],
+       "timeout": "long"
+      }
+     ],
+     [
+      "partitioned-popins/partitioned-popins.partitions.tentative.https.window.html?include=variant-9-test",
+      {
+       "script_metadata": [
+        [
+         "script",
+         "/resources/testdriver.js"
+        ],
+        [
+         "script",
+         "/resources/testdriver-vendor.js"
+        ],
+        [
+         "script",
+         "/common/dispatcher/dispatcher.js"
+        ],
+        [
+         "script",
+         "/common/get-host-info.sub.js"
+        ],
+        [
+         "script",
+         "/common/utils.js"
+        ],
+        [
+         "script",
+         "/common/subset-tests-by-key.js"
+        ],
+        [
+         "script",
+         "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+        ],
+        [
+         "variant",
+         "?include=variant-1-test"
+        ],
+        [
+         "variant",
+         "?include=variant-2-test"
+        ],
+        [
+         "variant",
+         "?include=variant-3-test"
+        ],
+        [
+         "variant",
+         "?include=variant-4-test"
+        ],
+        [
+         "variant",
+         "?include=variant-5-test"
+        ],
+        [
+         "variant",
+         "?include=variant-6-test"
+        ],
+        [
+         "variant",
+         "?include=variant-7-test"
+        ],
+        [
+         "variant",
+         "?include=variant-8-test"
+        ],
+        [
+         "variant",
+         "?include=variant-9-test"
+        ],
+        [
+         "variant",
+         "?include=variant-10-test"
+        ],
+        [
+         "variant",
+         "?include=variant-11-test"
+        ],
+        [
          "timeout",
          "long"
         ]
@@ -658175,6 +659149,181 @@
        }
       ]
      ],
+     "unload-allowed-headerless.tentative.window.js": [
+      "12c9e1ee79a005140a7d8c5e0ab5498bc895acfd",
+      [
+       "permissions-policy/experimental-features/unload-allowed-headerless.tentative.window.html?urlType=blank",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "'unload' Policy : allowed in headerless doc when allowed in main frame."
+         ],
+         [
+          "script",
+          "/common/dispatcher/dispatcher.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+         ],
+         [
+          "script",
+          "./resources/unload-helper.js"
+         ],
+         [
+          "variant",
+          "?urlType=srcdoc"
+         ],
+         [
+          "variant",
+          "?urlType=data"
+         ],
+         [
+          "variant",
+          "?urlType=blob"
+         ],
+         [
+          "variant",
+          "?urlType=blank"
+         ]
+        ]
+       }
+      ],
+      [
+       "permissions-policy/experimental-features/unload-allowed-headerless.tentative.window.html?urlType=blob",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "'unload' Policy : allowed in headerless doc when allowed in main frame."
+         ],
+         [
+          "script",
+          "/common/dispatcher/dispatcher.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+         ],
+         [
+          "script",
+          "./resources/unload-helper.js"
+         ],
+         [
+          "variant",
+          "?urlType=srcdoc"
+         ],
+         [
+          "variant",
+          "?urlType=data"
+         ],
+         [
+          "variant",
+          "?urlType=blob"
+         ],
+         [
+          "variant",
+          "?urlType=blank"
+         ]
+        ]
+       }
+      ],
+      [
+       "permissions-policy/experimental-features/unload-allowed-headerless.tentative.window.html?urlType=data",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "'unload' Policy : allowed in headerless doc when allowed in main frame."
+         ],
+         [
+          "script",
+          "/common/dispatcher/dispatcher.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+         ],
+         [
+          "script",
+          "./resources/unload-helper.js"
+         ],
+         [
+          "variant",
+          "?urlType=srcdoc"
+         ],
+         [
+          "variant",
+          "?urlType=data"
+         ],
+         [
+          "variant",
+          "?urlType=blob"
+         ],
+         [
+          "variant",
+          "?urlType=blank"
+         ]
+        ]
+       }
+      ],
+      [
+       "permissions-policy/experimental-features/unload-allowed-headerless.tentative.window.html?urlType=srcdoc",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "'unload' Policy : allowed in headerless doc when allowed in main frame."
+         ],
+         [
+          "script",
+          "/common/dispatcher/dispatcher.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+         ],
+         [
+          "script",
+          "./resources/unload-helper.js"
+         ],
+         [
+          "variant",
+          "?urlType=srcdoc"
+         ],
+         [
+          "variant",
+          "?urlType=data"
+         ],
+         [
+          "variant",
+          "?urlType=blob"
+         ],
+         [
+          "variant",
+          "?urlType=blank"
+         ]
+        ]
+       }
+      ]
+     ],
      "unload-allowed-object.tentative.window.js": [
       "376bb4cb1d06633a77df7b4c8248162455e3979b",
       [
@@ -658280,6 +659429,181 @@
        }
       ]
      ],
+     "unload-disallowed-headerless.tentative.window.js": [
+      "76af95f4257de168873ec14fe8bfe15deb97564b",
+      [
+       "permissions-policy/experimental-features/unload-disallowed-headerless.tentative.window.html?urlType=blank",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "'unload' Policy : disallowed in headerless doc when disallowed in main frame."
+         ],
+         [
+          "script",
+          "/common/dispatcher/dispatcher.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+         ],
+         [
+          "script",
+          "./resources/unload-helper.js"
+         ],
+         [
+          "variant",
+          "?urlType=srcdoc"
+         ],
+         [
+          "variant",
+          "?urlType=data"
+         ],
+         [
+          "variant",
+          "?urlType=blob"
+         ],
+         [
+          "variant",
+          "?urlType=blank"
+         ]
+        ]
+       }
+      ],
+      [
+       "permissions-policy/experimental-features/unload-disallowed-headerless.tentative.window.html?urlType=blob",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "'unload' Policy : disallowed in headerless doc when disallowed in main frame."
+         ],
+         [
+          "script",
+          "/common/dispatcher/dispatcher.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+         ],
+         [
+          "script",
+          "./resources/unload-helper.js"
+         ],
+         [
+          "variant",
+          "?urlType=srcdoc"
+         ],
+         [
+          "variant",
+          "?urlType=data"
+         ],
+         [
+          "variant",
+          "?urlType=blob"
+         ],
+         [
+          "variant",
+          "?urlType=blank"
+         ]
+        ]
+       }
+      ],
+      [
+       "permissions-policy/experimental-features/unload-disallowed-headerless.tentative.window.html?urlType=data",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "'unload' Policy : disallowed in headerless doc when disallowed in main frame."
+         ],
+         [
+          "script",
+          "/common/dispatcher/dispatcher.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+         ],
+         [
+          "script",
+          "./resources/unload-helper.js"
+         ],
+         [
+          "variant",
+          "?urlType=srcdoc"
+         ],
+         [
+          "variant",
+          "?urlType=data"
+         ],
+         [
+          "variant",
+          "?urlType=blob"
+         ],
+         [
+          "variant",
+          "?urlType=blank"
+         ]
+        ]
+       }
+      ],
+      [
+       "permissions-policy/experimental-features/unload-disallowed-headerless.tentative.window.html?urlType=srcdoc",
+       {
+        "script_metadata": [
+         [
+          "title",
+          "'unload' Policy : disallowed in headerless doc when disallowed in main frame."
+         ],
+         [
+          "script",
+          "/common/dispatcher/dispatcher.js"
+         ],
+         [
+          "script",
+          "/common/utils.js"
+         ],
+         [
+          "script",
+          "/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"
+         ],
+         [
+          "script",
+          "./resources/unload-helper.js"
+         ],
+         [
+          "variant",
+          "?urlType=srcdoc"
+         ],
+         [
+          "variant",
+          "?urlType=data"
+         ],
+         [
+          "variant",
+          "?urlType=blob"
+         ],
+         [
+          "variant",
+          "?urlType=blank"
+         ]
+        ]
+       }
+      ]
+     ],
      "unload-disallowed-object.tentative.window.js": [
       "33c23239e6d6fd3f7f578295da93adcdd07d9941",
       [
@@ -674872,6 +676196,13 @@
       {}
      ]
     ],
+    "content-encoding.https.html": [
+     "58ea76ed22b2c1655681c74b303b8f9d33b67a73",
+     [
+      null,
+      {}
+     ]
+    ],
     "content-type-minimization.html": [
      "d7dbc2cc9f3b6ae593ca39188f4a5848a4fcb56e",
      [
@@ -732299,7 +733630,7 @@
      ]
     ],
     "per-frame-qp-encoding.https.any.js": [
-     "8ec96c3f7035d4d02953dea5a2185fe63844df9a",
+     "c4c49d81e55bab3a4a9009a8970aeea6224cdd5c",
      [
       "webcodecs/per-frame-qp-encoding.https.any.html?av1",
       {
@@ -758650,7 +759981,7 @@
       ]
      ],
      "tensor.https.any.js": [
-      "582ecdb9d68bddf2905414827622ba91799b4943",
+      "0906ae96ba2c6ae5eb391c8484e8a42e2dbe0426",
       [
        "webnn/conformance_tests/tensor.https.any.html?cpu",
        {
@@ -760397,7 +761728,7 @@
       ]
      ],
      "cast.https.any.js": [
-      "0dee2f0f07104071ddb8baa959b09b488c4aeeee",
+      "2ad62125746159cdf0520873a9029edf06eea883",
       [
        "webnn/validation_tests/cast.https.any.html?cpu",
        {
@@ -760775,7 +762106,7 @@
       ]
      ],
      "concat.https.any.js": [
-      "6ccac4e7e8b5a311adc0b47cd85844bd2f7bae34",
+      "7a55e4f672f0480278c7fc376aa140f9dcd7fb99",
       [
        "webnn/validation_tests/concat.https.any.html?cpu",
        {
@@ -762098,7 +763429,7 @@
       ]
      ],
      "dequantizeLinear.https.any.js": [
-      "672fc4ac72456bfe8ca05cf908dba457819830b3",
+      "84c55ad8fe47a9c1aabec078c51b12427fcbc271",
       [
        "webnn/validation_tests/dequantizeLinear.https.any.html?cpu",
        {
@@ -762677,7 +764008,7 @@
       ]
      ],
      "elementwise-binary.https.any.js": [
-      "32777f84681a9e621993d78f4395a31b47a6272e",
+      "bdaf0939033102f7f7bbf213befb42bd1baa4c37",
       [
        "webnn/validation_tests/elementwise-binary.https.any.html?cpu",
        {
@@ -763433,7 +764764,7 @@
       ]
      ],
      "expand.https.any.js": [
-      "c9c60358917f8b961ea45adfa251744458b8fe51",
+      "c49bcac6583e70ca0b7c779362aa12823838d09d",
       [
        "webnn/validation_tests/expand.https.any.html?cpu",
        {
@@ -763622,7 +764953,7 @@
       ]
      ],
      "gather.https.any.js": [
-      "e4a46a90d4b363783bc9020074d8d261ff59a28b",
+      "f8ec307c89764cd4fe53a54e951cc7c072681cc3",
       [
        "webnn/validation_tests/gather.https.any.html?cpu",
        {
@@ -764000,7 +765331,7 @@
       ]
      ],
      "gatherND.https.any.js": [
-      "697dda00776404cbe8bba31de89907cabb389a64",
+      "41e9b6b7968fe499186ac0a5fe8541e0ff69bd8d",
       [
        "webnn/validation_tests/gatherND.https.any.html?cpu",
        {
@@ -764378,7 +765709,7 @@
       ]
      ],
      "gemm.https.any.js": [
-      "6115003d0392bbee3fb396052534f106bc6d86f2",
+      "4c106bc0180cb7fd91c38b881b5eb150c2874506",
       [
        "webnn/validation_tests/gemm.https.any.html?cpu",
        {
@@ -765323,7 +766654,7 @@
       ]
      ],
      "input.https.any.js": [
-      "cc60c44567046b2bf41ae50eb1d0fd732364dfb5",
+      "54f50b146cb33dfef1f87a1169f29d17b3ea6008",
       [
        "webnn/validation_tests/input.https.any.html?cpu",
        {
@@ -766835,7 +768166,7 @@
       ]
      ],
      "matmul.https.any.js": [
-      "86440e2b0f1c97585186fab868468abdc2762d47",
+      "8aba871f275ed17f63d4af82d27a54cbac647ae7",
       [
        "webnn/validation_tests/matmul.https.any.html?cpu",
        {
@@ -767024,7 +768355,7 @@
       ]
      ],
      "pad.https.any.js": [
-      "ca11bd2c9695a4783b1cda24ed9dc96b38523ea3",
+      "16d8afc1a1f3e62d5b334a2ba3e97c708941578f",
       [
        "webnn/validation_tests/pad.https.any.html?cpu",
        {
@@ -767834,7 +769165,7 @@
       ]
      ],
      "quantizeLinear.https.any.js": [
-      "8516e9cf8e0b4745d5837e9dc3f8a437b9d3301a",
+      "237960162448274335946dd8a683074892015a57",
       [
        "webnn/validation_tests/quantizeLinear.https.any.html?cpu",
        {
@@ -768401,7 +769732,7 @@
       ]
      ],
      "resample2d.https.any.js": [
-      "1278143d232f0594d876f17b2ac359946ba76a6c",
+      "d5cd1a4823dcb240b66add15c3acf5fca75135f8",
       [
        "webnn/validation_tests/resample2d.https.any.html?cpu",
        {
@@ -770669,7 +772000,7 @@
       ]
      ],
      "tile.https.any.js": [
-      "067335409178db5dc46e22b64f327b00a952bd68",
+      "1e5ac04e7c619d8abaa07720ce880be1dc6d63c9",
       [
        "webnn/validation_tests/tile.https.any.html?cpu",
        {
@@ -771455,7 +772786,7 @@
       ]
      ],
      "where.https.any.js": [
-      "424d080c009d325384c134d6095228ce79a80b31",
+      "9e2a10f6e0019859a06f41321d1ca08affdc47f7",
       [
        "webnn/validation_tests/where.https.any.html?cpu",
        {
@@ -773363,7 +774694,7 @@
      ]
     ],
     "RTCRtpScriptTransform-encoded-transform.https.html": [
-     "c935644f0c6f7e71b50cd241bebdea9cc92826e7",
+     "9eb7020a781c2d2715fcac3583e7142b50b1da2f",
      [
       null,
       {
diff --git a/third_party/blink/web_tests/external/wpt/clear-site-data/clear-cache.https.html b/third_party/blink/web_tests/external/wpt/clear-site-data/clear-cache.https.html
new file mode 100644
index 0000000..8cdaf2a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/clear-site-data/clear-cache.https.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script>
+
+// Here's the set-up for this test:
+// Step 1 (main window) Open first window with first url putting some resource
+//                       into the cache and maybe receiving clear-site-data header
+// Step 2 (first window) Message first window with uuid
+// Step 3 (main window) Open second window with second url
+// Step 4 (second window) Message first window with uuid (either cached or non-cached)
+// Optional Step 5 (main window) Open third window with third url
+// Optional Step 6 (third window) Message first window with uuid (either cached or non-cached)
+// Step 7 (main window): Assert first and last uuid not equal due to `clear-site-data: "cache"` header
+function test_cache_clear(test, params1, params2, params3) {
+    let cache_helper = "cache_helper=" + self.crypto.randomUUID() + "&";
+    let firstUrl = "/clear-site-data/support/clear-site-data-cache.py?" + cache_helper + params1
+    let secondUrl = "/clear-site-data/support/clear-site-data-cache.py?" + cache_helper + params2;
+    let thirdUrl = params3 ? "/clear-site-data/support/clear-site-data-cache.py?" + cache_helper + params3 : null;
+
+    return new Promise(resolve => {
+        window.addEventListener("message", test.step_func(e => {
+            // Result Step 2
+            let firstUuid = e.data;
+
+            window.addEventListener("message", test.step_func(e => {
+                // Result Step 4
+                let secondUuid = e.data;
+
+                if (thirdUrl === null) {
+                    // Step 7, skipping the optional step 5 and 6
+                    assert_not_equals(firstUuid, secondUuid);
+                    resolve();
+                } else {
+                    window.addEventListener("message", test.step_func(e => {
+                        // Result Step 6
+                        let thirdUuid = e.data;
+
+                        // Step 7
+                        assert_not_equals(firstUuid, thirdUuid);
+                        resolve();
+
+                    }), {once: true});
+
+                    // Step 5
+                    let third_window = window.open(thirdUrl);
+                    test.add_cleanup(third_window.close);
+                }
+
+            }), {once: true});
+
+            // Step 3
+            let second_window = window.open(secondUrl);
+            test.add_cleanup(second_window.close);
+        }), {once: true});
+
+        // Step 1
+        let first_window = window.open(firstUrl);
+        test.add_cleanup(first_window.close);
+        // tests are using cookies to differentiate between requests
+        test.add_cleanup(test_driver.delete_all_cookies)
+    });
+}
+
+promise_test(t => {
+    return test_cache_clear(t, "response=single_html&cache&clear_first=cache", "response=single_html&cache&clear_first=cache");
+}, "clear cache: Document with clear-cache header doesn't get cached");
+
+promise_test(t => {
+    return test_cache_clear(t, "response=single_html&cache&clear_first=all", "response=single_html&cache&clear_first=all");
+}, "clear all: Document with clear-cache header doesn't get cached");
+
+promise_test(t => {
+    return test_cache_clear(t, "response=html_embed_json&clear=cache", "response=html_embed_json&clear=cache");
+}, "clear cache: Fetch on docment with clear-cache header doesn't get cached");
+
+promise_test(t => {
+    return test_cache_clear(t, "response=html_embed_json&clear=all", "response=html_embed_json&clear=all");
+}, "clear all: Fetch on docment with clear-cache header doesn't get cached");
+
+promise_test(t => {
+    return test_cache_clear(t, "response=html_embed_json", "response=html_embed_json&clear=cache");
+}, "clear cache: Previously cached fetch gets cleared");
+
+promise_test(t => {
+    return test_cache_clear(t, "response=html_embed_json", "response=html_embed_json&clear=all");
+}, "clear all: Previously cached fetch gets cleared");
+
+promise_test(t => {
+    return test_cache_clear(t, "response=html_embed_json", "response=single_html&clear=cache", "response=html_embed_json");
+}, "clear cache: Clear fetch on intermediate navigation");
+
+promise_test(t => {
+    return test_cache_clear(t, "response=html_embed_json", "response=single_html&clear=all", "response=html_embed_json");
+}, "clear all: Clear fetch on intermediate navigation");
+
+promise_test(t => {
+    return test_cache_clear(t, "response=single_html&cache", "response=single_html&clear=cache", "response=single_html&cache");
+}, "clear cache: Clear document in intermediate load");
+
+promise_test(t => {
+    return test_cache_clear(t, "response=single_html&cache", "response=single_html&clear=all", "response=single_html&cache");
+}, "clear all: Clear document in intermediate load");
+
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/clear-site-data/support/clear-site-data-cache.py b/third_party/blink/web_tests/external/wpt/clear-site-data/support/clear-site-data-cache.py
new file mode 100644
index 0000000..87107693
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/clear-site-data/support/clear-site-data-cache.py
@@ -0,0 +1,69 @@
+"""
+Loaded in Step 2/4/Optional 6 (/clear-site-data/clear-cache.https.html)
+Sending Message for Step 3/5/Optional 7 (/clear-site-data/clear-cache.https.html)
+"""
+import uuid
+
+def main(request, response):
+    # type of response:
+    #  - "single_html": Main page html file with a different postMessage uuid on each response
+    #  - "json": Json that always responds with a different uuid in a single-element array
+    #  - "html_embed_json": Main page html that embeds a cachable version of the above json
+    response_type = request.GET.first(b"response")
+
+    cache_helper = request.GET.first(b"cache_helper")
+
+    # force enable caching when present or force disable if not
+    cache = b"cache" in request.GET
+    clear = None
+    if b"clear" in request.GET:
+        clear = request.GET.first(b"clear")
+    if b"clear_first" in request.GET:
+        if request.server.stash.take(cache_helper) is None:
+            clear = request.GET.first(b"clear_first")
+            request.server.stash.put(cache_helper, ())
+
+    headers = []
+    if response_type == b"json":
+        headers += [(b"Content-Type", b"application/json")]
+    else:
+        headers += [(b"Content-Type", b"text/html")]
+
+    if cache:
+        headers += [(b"cache-control", b"public, max-age=31536000, immutable")]
+    else:
+        headers += [(b"cache-control", b"no-store")]
+
+    if clear is not None:
+        if clear == b"all":
+            headers += [(b"Clear-Site-Data", b'"*"')]
+        else:
+            headers += [(b"Clear-Site-Data", b'"' + clear + b'"')]
+
+    if response_type == b"single_html":
+        # send unique UUID. Cache got cleared when uuids don't match.
+        content = f'''
+            <script>
+                window.opener.postMessage("{uuid.uuid4()}" , "*");
+            </script>
+            <body>
+                {request.url}
+            </body>'''
+    elif response_type == b"json":
+        # send unique UUID. helper for below "html_embed_json"
+        content = f'''["{uuid.uuid4()}"]'''
+    elif response_type == b"html_embed_json":
+        url = request.url_parts.path + "?response=json&cache&cache_helper=" + cache_helper.decode()
+        content = f'''
+            <script>
+                fetch("{url}")
+                    .then(response => response.json())
+                    .then(uuid => window.opener.postMessage(uuid[0], "*"));
+            </script>
+            <body>
+                {request.url}<br>
+                {url}
+            </body>'''
+
+
+    return 200, headers, content
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieListItem_attributes.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieListItem_attributes.https.any.js
index 200cbd06..6716d91 100644
--- a/third_party/blink/web_tests/external/wpt/cookie-store/cookieListItem_attributes.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/cookie-store/cookieListItem_attributes.https.any.js
@@ -195,3 +195,15 @@
   }, `CookieListItem - cookieStore.set with sameSite set to ${sameSiteValue}`);
 
 });
+
+promise_test(async testCase => {
+  await cookieStore.delete('cookie-name');
+
+  await cookieStore.set('cookie-name', 'cookie-value');
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete('cookie-name');
+  });
+
+  const cookie = await cookieStore.get('cookie-name');
+  assert_equals(cookie.secure, true);
+}, 'CookieListItem - secure defaults to true');
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_arguments.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_arguments.https.any.js
index ddae238..37a551b3 100644
--- a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_arguments.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_delete_arguments.https.any.js
@@ -143,7 +143,7 @@
   assert_equals(cookie_attributes.name, 'cookie-name');
   assert_equals(cookie_attributes.value, 'cookie-value');
 
-  await cookieStore.delete(cookie_attributes);
+  await cookieStore.delete(cookie_attributes.name);
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie, null);
 }, 'cookieStore.delete with get result');
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_set_creation_url.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_set_creation_url.https.any.js
new file mode 100644
index 0000000..d09c4aa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_getAll_set_creation_url.https.any.js
@@ -0,0 +1,33 @@
+// META: title=Cookie Store API: cookieStore.set()/getAll() with Document URL changing
+// META: global=window
+
+'use strict';
+
+promise_test(async testCase => {
+  const currentUrl = new URL(self.location.href);
+  const currentPath = currentUrl.pathname;
+  const currentDirectory =
+      currentPath.substr(0, currentPath.lastIndexOf('/') + 1);
+
+  await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
+
+  await cookieStore.set(
+    { name: 'cookie-name', value: 'cookie-value', path: currentDirectory });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
+  });
+
+  // This changes the Document's current URL to this different URL.
+  // The Document's creation URL does not change.
+  // If set() and getAll() use Document's current URL, the cookie will be set
+  // using the original URL above, and the get below will fail since it looks
+  // for cookies with this different URL. If they both use the creation URL,
+  // the get will succeed since it won't use this different URL to search.
+  let different_url = `${self.location.protocol}//${self.location.host}/different/path`;
+  history.pushState({}, "", different_url);
+
+  const cookies = await cookieStore.getAll();
+  assert_equals(cookies.length, 1);
+  assert_equals(cookies[0].name, 'cookie-name');
+  assert_equals(cookies[0].value, 'cookie-value');
+}, 'cookieStore.set and cookieStore.getAll use the creation url');
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_creation_url.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_creation_url.https.any.js
new file mode 100644
index 0000000..71f8108
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_get_set_creation_url.https.any.js
@@ -0,0 +1,32 @@
+// META: title=Cookie Store API: cookieStore.set()/get() with Document URL changing
+// META: global=window
+
+'use strict';
+
+promise_test(async testCase => {
+  const currentUrl = new URL(self.location.href);
+  const currentPath = currentUrl.pathname;
+  const currentDirectory =
+      currentPath.substr(0, currentPath.lastIndexOf('/') + 1);
+
+  await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
+
+  await cookieStore.set(
+      { name: 'cookie-name', value: 'cookie-value', path: currentDirectory });
+  testCase.add_cleanup(async () => {
+    await cookieStore.delete({ name: 'cookie-name', path: currentDirectory });
+  });
+
+  // This changes the Document's current URL to this different URL.
+  // The Document's creation URL does not change.
+  // If set() and get() use Document's current URL, the cookie will be set
+  // using the original URL above, and the get below will fail since it looks
+  // for cookies with this different URL. If they both use the creation URL,
+  // the get will succeed since it won't use this different URL to search.
+  let different_url = `${self.location.protocol}//${self.location.host}/different/path`;
+  history.pushState({}, "", different_url);
+
+  const cookie = await cookieStore.get('cookie-name');
+  assert_equals(cookie.name, 'cookie-name');
+  assert_equals(cookie.value, 'cookie-value');
+}, 'cookieStore.set and cookieStore.get use the creation url');
diff --git a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_set_arguments.https.any.js b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_set_arguments.https.any.js
index 8ff66bf77..064fcc5 100644
--- a/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_set_arguments.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/cookie-store/cookieStore_set_arguments.https.any.js
@@ -30,6 +30,11 @@
 
 promise_test(async testCase => {
   await promise_rejects_js(testCase, TypeError,
+      cookieStore.set('', ''));
+}, "cookieStore.set fails with empty name and empty value");
+
+promise_test(async testCase => {
+  await promise_rejects_js(testCase, TypeError,
       cookieStore.set('', 'suspicious-value=resembles-name-and-value'));
 }, "cookieStore.set with empty name and an '=' in value");
 
@@ -44,6 +49,28 @@
   assert_equals(cookie.value, 'suspicious-value=resembles-name-and-value');
 }, "cookieStore.set with normal name and an '=' in value");
 
+let invalidCharacters = [ '\u0000', '\u0001', '\u0002'
+                        , '\u0003', '\u0004', '\u0005'
+                        , '\u0006', '\u0007', '\u0008'
+                                  , '\u0010', '\u0011'
+                        , '\u0012', '\u0013', '\u0014'
+                        , '\u0015', '\u0016', '\u0017'
+                        , '\u0018', '\u0019', '\u001A'
+                        , '\u001B', '\u001C', '\u001D'
+                        , '\u001E', '\u001F'
+                        , '\u003B', '\u007F'];
+
+invalidCharacters.forEach(invalidCharacter => {
+  let invalidCookieName = 'cookie' + invalidCharacter + 'name';
+  let invalidCookieValue = 'cookie' + invalidCharacter + 'value';
+  promise_test(async testCase => {
+    await promise_rejects_js(testCase, TypeError,
+        cookieStore.set(invalidCookieName, 'cookie-value'));
+    await promise_rejects_js(testCase, TypeError,
+      cookieStore.set('cookie-name', invalidCookieValue));
+  }, `cookieStore.set checks if name or value contain invalid character U+${invalidCharacter.charCodeAt(0).toString(16).padStart(4, "0").toUpperCase()}`);
+});
+
 promise_test(async testCase => {
   const tenYears = 10 * 365 * 24 * 60 * 60 * 1000;
   const tenYearsFromNow = Date.now() + tenYears;
@@ -280,7 +307,7 @@
   assert_equals(cookie_attributes.value, 'old-cookie-value');
 
   cookie_attributes.value = 'new-cookie-value';
-  await cookieStore.set(cookie_attributes);
+  await cookieStore.set(cookie_attributes.name, cookie_attributes.value);
   const cookie = await cookieStore.get('cookie-name');
   assert_equals(cookie.name, 'cookie-name');
   assert_equals(cookie.value, 'new-cookie-value');
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/content-visibility-auto-svg-text.html b/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/content-visibility-auto-svg-text.html
index 93b5709..60aac70 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/content-visibility-auto-svg-text.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-contain/content-visibility/content-visibility-auto-svg-text.html
@@ -12,7 +12,7 @@
 </style>
 </head>
 <body>
-<p>Test passes if there is a filled green square.</p>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
 <div style="content-visibility: auto;">
 <svg>
   <text fill="green" x="0" y="80">x</text>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-fonts/idlharness-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-fonts/idlharness-expected.txt
index 47cdf69..c1bb365 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-fonts/idlharness-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-fonts/idlharness-expected.txt
@@ -1,77 +1,4 @@
 This is a testharness.js-based test.
-Found 45 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] CSSFontFaceDescriptors interface: existence and properties of interface object
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface object length
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface object name
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute src
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute fontFamily
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute font-family
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute fontStyle
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute font-style
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute fontWeight
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute font-weight
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute fontStretch
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute font-stretch
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute fontWidth
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute font-width
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute unicodeRange
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute unicode-range
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute fontFeatureSettings
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute font-feature-settings
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute fontVariationSettings
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute font-variation-settings
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute fontNamedInstance
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute font-named-instance
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute fontDisplay
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute font-display
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute fontLanguageOverride
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute font-language-override
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute ascentOverride
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute ascent-override
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute descentOverride
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute descent-override
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute lineGapOverride
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceDescriptors interface: attribute line-gap-override
-  assert_own_property: self does not have own property "CSSFontFaceDescriptors" expected property "CSSFontFaceDescriptors" missing
-[FAIL] CSSFontFaceRule interface: attribute style
-  assert_equals: setter must be function for PutForwards, Replaceable, or non-readonly attributes expected "function" but got "undefined"
 [FAIL] CSSFontFeatureValuesRule interface: attribute historicalForms
   assert_true: The prototype object must have a property "historicalForms" expected true got false
 [FAIL] CSSFontFeatureValuesMap interface: existence and properties of interface object
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollable-overflow-empty-newline-span.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollable-overflow-empty-newline-span.html
new file mode 100644
index 0000000..39f72d9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/scrollable-overflow-empty-newline-span.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<link rel="author" title="David Shin" href="dshin@mozilla.com">
+<link rel="help" href="bugzilla.mozilla.org/show_bug.cgi?id=1940938">
+<link rel="match" href="../reference/ref-filled-green-100px-square-only.html">
+<p>Test passes if there is a filled green square.</p>
+<div id="dut" style="overflow: hidden; height: 100px; width: 100px; background: red;">
+  <div style="width: 100px; height: 100px; background: green;"></div>
+  <div style="font-size: 200px;"><span>
+  </span></div>
+</div>
+<script>
+onload = function () {
+  dut.scrollTop = 100;
+  document.documentElement.className = "";
+}
+</script>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/text-wrap-balance-before-after-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/text-wrap-balance-before-after-001-ref.html
new file mode 100644
index 0000000..9bf7820
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/text-wrap-balance-before-after-001-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<style>
+.container {
+  font: 16px monospace;
+  margin: 1em;
+  width: 60ch;
+}
+
+.text1 {
+  text-wrap: balance;
+  margin-bottom: 1em;
+  outline: 1px dashed gray;
+}
+</style>
+
+<p>The two paragraphs should look identical, both having balanced lines:</p>
+
+<div class="container">
+<div class="text1">
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+</div>
+<div class="text1">
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/text-wrap-balance-before-after-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/text-wrap-balance-before-after-002-ref.html
new file mode 100644
index 0000000..6fbfe89
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/reference/text-wrap-balance-before-after-002-ref.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<style>
+.container {
+  font: 16px monospace;
+  margin: 1em;
+  width: 60ch;
+  outline: 1px dashed gray;
+}
+
+.text {
+  text-wrap: balance;
+}
+
+.text-before {
+  letter-spacing: -0.15ch;
+  margin-bottom: 1em;
+  text-wrap: balance;
+}
+
+.text-after {
+  text-wrap-style: stable;
+  margin-top: 1em;
+}
+</style>
+
+<p>The first two paragraphs should be separately balanced; the third should not use balancing:</p>
+
+<div class="container">
+<div class="text-before">
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+</div>
+<div class="text">
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+</div>
+<div class="text-after">
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/text-wrap-balance-before-after-001.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/text-wrap-balance-before-after-001.html
new file mode 100644
index 0000000..9f5741e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/text-wrap-balance-before-after-001.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-text-4/#valdef-text-wrap-balance">
+<link rel="match" href="reference/text-wrap-balance-before-after-001-ref.html">
+<style>
+.container {
+  font: 16px monospace;
+  margin: 1em;
+  width: 60ch;
+}
+
+.text1, .text2 {
+  text-wrap: balance;
+  margin-bottom: 1em;
+  outline: 1px dashed gray;
+}
+
+/* :before and :after pseudos with display:block and no content should not affect
+   the layout of the paragraph content */
+.text2:before, .text2:after {
+  content: "";
+  display: block;
+}
+</style>
+
+<p>The two paragraphs should look identical, both having balanced lines:</p>
+
+<div class="container">
+<div class="text1">
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+</div>
+<div class="text2">
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/white-space/text-wrap-balance-before-after-002.html b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/text-wrap-balance-before-after-002.html
new file mode 100644
index 0000000..e9d08953
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-text/white-space/text-wrap-balance-before-after-002.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-text-4/#valdef-text-wrap-balance">
+<link rel="match" href="reference/text-wrap-balance-before-after-002-ref.html">
+<style>
+.container {
+  font: 16px monospace;
+  margin: 1em;
+  width: 60ch;
+}
+
+.text {
+  text-wrap: balance;
+  outline: 1px dashed gray;
+}
+
+.text:before {
+  content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
+  letter-spacing: -0.15ch;
+  display: block;
+  margin-bottom: 1em;
+}
+
+.text:after {
+  content: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
+  text-wrap-style: stable;
+  display: block;
+  margin-top: 1em;
+}
+</style>
+
+<p>The first two paragraphs should be separately balanced; the third should not use balancing:</p>
+
+<div class="container">
+<div class="text">
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+</div>
+</div>
diff --git a/third_party/blink/web_tests/external/wpt/dom/nodes/moveBefore/tentative/Node-moveBefore.html b/third_party/blink/web_tests/external/wpt/dom/nodes/moveBefore/tentative/Node-moveBefore.html
index 6ea8cfe..a1147c4 100644
--- a/third_party/blink/web_tests/external/wpt/dom/nodes/moveBefore/tentative/Node-moveBefore.html
+++ b/third_party/blink/web_tests/external/wpt/dom/nodes/moveBefore/tentative/Node-moveBefore.html
@@ -339,4 +339,11 @@
   const div = outer.appendChild(document.createElement('div'));
   assert_throws_dom("HIERARCHY_REQUEST_ERR", () => div.moveBefore(outer, null));
 }, "Invalid node hierarchy with null old parent does not crash");
+
+test(t => {
+  const outerDiv = document.createElement('div');
+  const innerDiv = outerDiv.appendChild(document.createElement('div'));
+  const iframe = innerDiv.appendChild(document.createElement('iframe'));
+  outerDiv.moveBefore(iframe, null);
+}, "Move disconnected iframe does not crash");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/editing/crashtests/remove-all-children-of-documentElement-in-designMode-when-no-body.html b/third_party/blink/web_tests/external/wpt/editing/crashtests/remove-all-children-of-documentElement-in-designMode-when-no-body.html
new file mode 100644
index 0000000..2898444
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/editing/crashtests/remove-all-children-of-documentElement-in-designMode-when-no-body.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script>
+document.addEventListener("DOMContentLoaded", () => {
+  document.designMode = "on";
+  document.documentElement.innerText = "abc def\n"; // causes removing the implicit <body> element
+  let child;
+  while ((child = document.documentElement.lastChild)) {
+    child.remove();
+  }
+}, {once: true});
+</script>
+</head>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/fledge/tentative/server-response.https.window.js b/third_party/blink/web_tests/external/wpt/fledge/tentative/server-response.https.window.js
index d3c41498..5374adb 100644
--- a/third_party/blink/web_tests/external/wpt/fledge/tentative/server-response.https.window.js
+++ b/third_party/blink/web_tests/external/wpt/fledge/tentative/server-response.https.window.js
@@ -521,103 +521,50 @@
   expectNoWinner(auctionResult);
 }, 'Basic B&A auction - Wrong request Id');
 
-// Runs responseMutator on a minimal correct server response, and expects
-// either success/failure based on expectWin.
-// TODO(qingxinwu): Remove this and use the one in ba-fledge-util.sub.js.
-async function testWithMutatedServerResponse(
-    test, expectWin, responseMutator, igMutator = undefined) {
-  const uuid = generateUuid(test);
-  const adA = createTrackerURL(window.location.origin, uuid, 'track_get', 'a');
-  const adB = createTrackerURL(window.location.origin, uuid, 'track_get', 'b');
-  const adsArray =
-      [{renderURL: adA, adRenderId: 'a'}, {renderURL: adB, adRenderId: 'b'}];
-  let ig = {ads: adsArray};
-  if (igMutator) {
-    igMutator(ig, uuid);
-  }
-  await joinInterestGroup(test, uuid, ig);
-
-  const result = await navigator.getInterestGroupAdAuctionData({
-    coordinatorOrigin: await BA.configureCoordinator(),
-    seller: window.location.origin
-  });
-  assert_true(result.requestId !== null);
-  assert_true(result.request.length > 0);
-
-  let decoded = await BA.decodeInterestGroupData(result.request);
-
-  let serverResponseMsg = {
-    'biddingGroups': {},
-    'adRenderURL': ig.ads[0].renderURL,
-    'interestGroupName': DEFAULT_INTEREST_GROUP_NAME,
-    'interestGroupOwner': window.location.origin,
-  };
-  serverResponseMsg.biddingGroups[window.location.origin] = [0];
-  await responseMutator(serverResponseMsg, uuid);
-
-  let serverResponse =
-      await BA.encodeServerResponse(serverResponseMsg, decoded);
-
-  let hashString = await BA.payloadHash(serverResponse);
-  await BA.authorizeServerResponseHashes([hashString]);
-
-  let auctionResult = await navigator.runAdAuction({
-    'seller': window.location.origin,
-    'interestGroupBuyers': [window.location.origin],
-    'requestId': result.requestId,
-    'serverResponse': serverResponse,
-    'resolveToConfig': true,
-  });
-  if (expectWin) {
-    expectSuccess(auctionResult);
-    return auctionResult;
-  } else {
-    expectNoWinner(auctionResult);
-  }
-}
-
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(
+  await BA.testWithMutatedServerResponse(
       test, /*expectSuccess=*/ false, msg => {msg.error = {}});
 }, 'Basic B&A auction - response marked as error');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ true, msg => {
+  await BA.testWithMutatedServerResponse(test, /*expectSuccess=*/ true, msg => {
     msg.error = 4;
   });
 }, 'Basic B&A auction - nonsense error field');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ false, msg => {
-    msg.error = {message: 'oh no'};
-  });
+  await BA.testWithMutatedServerResponse(
+      test, /*expectSuccess=*/ false, msg => {
+        msg.error = {message: 'oh no'};
+      });
 }, 'Basic B&A auction - response marked as error, with message');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ false, msg => {
-    msg.error = {message: {}};
-  });
+  await BA.testWithMutatedServerResponse(
+      test, /*expectSuccess=*/ false, msg => {
+        msg.error = {message: {}};
+      });
 }, 'Basic B&A auction - response marked as error, with bad message');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(
+  await BA.testWithMutatedServerResponse(
       test, /*expectSuccess=*/ false, msg => {msg.isChaff = true});
 }, 'Basic B&A auction - response marked as chaff');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(
+  await BA.testWithMutatedServerResponse(
       test, /*expectSuccess=*/ true, msg => {msg.isChaff = false});
 }, 'Basic B&A auction - response marked as non-chaff');
 
 // Disabled while spec clarifying expected behavior is in-progress.
 //
 // subsetTest(promise_test, async test => {
-//   await testWithMutatedServerResponse(
+//   await BA.testWithMutatedServerResponse(
 //       test, /*expectSuccess=*/ true, msg => {msg.isChaff = 'yes'});
 // }, 'Basic B&A auction - response marked as chaff incorrectly');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(
+  await BA.testWithMutatedServerResponse(
       test, /*expectSuccess=*/ false,
       msg => {msg.topLevelSeller = 'https://example.org/'});
 }, 'Basic B&A auction - incorrectly includes topLevelSeller');
@@ -625,12 +572,12 @@
 // Disabled while spec clarifying expected behavior is in-progress.
 //
 // subsetTest(promise_test, async test => {
-//   await testWithMutatedServerResponse(
+//   await BA.testWithMutatedServerResponse(
 //       test, /*expectSuccess=*/ true, msg => {msg.topLevelSeller = 1});
 // }, 'Basic B&A auction - non-string top-level seller ignored');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(
+  await BA.testWithMutatedServerResponse(
       test, /*expectSuccess=*/ false,
       msg => {msg.topLevelSeller = 'http://example.org/'});
 }, 'Basic B&A auction - http:// topLevelSeller is bad, too');
@@ -638,118 +585,135 @@
 // Disabled while spec clarifying expected behavior is in-progress.
 //
 // subsetTest(promise_test, async test => {
-//   await testWithMutatedServerResponse(
+//   await BA.testWithMutatedServerResponse(
 //       test, /*expectSuccess=*/ true, msg => {msg.bid = '10 cents'});
 // }, 'Basic B&A auction - non-number bid is ignored');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(
+  await BA.testWithMutatedServerResponse(
       test, /*expectSuccess=*/ true, msg => {msg.bid = 50});
 }, 'Basic B&A auction - positive bid is good');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(
+  await BA.testWithMutatedServerResponse(
       test, /*expectSuccess=*/ false, msg => {msg.bid = -50});
 }, 'Basic B&A auction - negative bid is bad');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(
+  await BA.testWithMutatedServerResponse(
       test, /*expectSuccess=*/ false, msg => {msg.bid = 0});
 }, 'Basic B&A auction - zero bid is bad');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(
+  await BA.testWithMutatedServerResponse(
       test, /*expectSuccess=*/ false,
       msg => {msg.biddingGroups[window.location.origin] = []});
 }, 'Basic B&A auction - winning group did not bid');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(
+  await BA.testWithMutatedServerResponse(
       test, /*expectSuccess=*/ false,
       msg => {msg.biddingGroups[window.location.origin] = [-1, 0]});
 }, 'Basic B&A auction - negative bidding group index');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(
+  await BA.testWithMutatedServerResponse(
       test, /*expectSuccess=*/ false,
       msg => {msg.biddingGroups[window.location.origin] = [0, 1]});
 }, 'Basic B&A auction - too large bidding group index');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ false, msg => {
-    msg.interestGroupName += 'not';
-  });
+  await BA.testWithMutatedServerResponse(
+      test, /*expectSuccess=*/ false, msg => {
+        msg.interestGroupName += 'not';
+      });
 }, 'Basic B&A auction - wrong IG name');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(
+  await BA.testWithMutatedServerResponse(
       test, /*expectSuccess=*/ false, async msg => {
         await leaveInterestGroup();
       });
 }, 'Basic B&A auction - left IG in the middle');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ false, msg => {
-    msg.adRenderURL += 'not';
-  });
+  await BA.testWithMutatedServerResponse(
+      test, /*expectSuccess=*/ false, msg => {
+        msg.adRenderURL += 'not';
+      });
 }, 'Basic B&A auction - ad URL not in ad');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ false, msg => {
-    msg.buyerReportingId = 'bid1';
-  });
+  await BA.testWithMutatedServerResponse(
+      test, /*expectSuccess=*/ false, msg => {
+        msg.buyerReportingId = 'bid1';
+      });
 }, 'Basic B&A auction - buyerReportingId not in ad');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ true, msg => {
-    msg.buyerReportingId = 'bid1';
-  }, ig => {
-    ig.ads[0].buyerReportingId = 'bid1';
-    ig.ads[1].buyerReportingId = 'bid2';
-  });
+  await BA.testWithMutatedServerResponse(
+      test, /*expectSuccess=*/ true,
+      msg => {
+        msg.buyerReportingId = 'bid1';
+      },
+      ig => {
+        ig.ads[0].buyerReportingId = 'bid1';
+        ig.ads[1].buyerReportingId = 'bid2';
+      });
 }, 'Basic B&A auction - buyerReportingId in ad');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ false, msg => {
-    msg.buyerReportingId = 'bid2';
-  }, ig => {
-    ig.ads[0].buyerReportingId = 'bid1';
-    ig.ads[1].buyerReportingId = 'bid2';
-  });
+  await BA.testWithMutatedServerResponse(
+      test, /*expectSuccess=*/ false,
+      msg => {
+        msg.buyerReportingId = 'bid2';
+      },
+      ig => {
+        ig.ads[0].buyerReportingId = 'bid1';
+        ig.ads[1].buyerReportingId = 'bid2';
+      });
 }, 'Basic B&A auction - buyerReportingId in wrong ad');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ false, msg => {
-    msg.buyerAndSellerReportingId = 'bsid1';
-  });
+  await BA.testWithMutatedServerResponse(
+      test, /*expectSuccess=*/ false, msg => {
+        msg.buyerAndSellerReportingId = 'bsid1';
+      });
 }, 'Basic B&A auction - buyerAndSellerReportingId not in ad');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ true, msg => {
-    msg.buyerAndSellerReportingId = 'bsid1';
-  }, ig => {
-    ig.ads[0].buyerAndSellerReportingId = 'bsid1';
-    ig.ads[1].buyerAndSellerReportingId = 'bsid2';
-  });
+  await BA.testWithMutatedServerResponse(
+      test, /*expectSuccess=*/ true,
+      msg => {
+        msg.buyerAndSellerReportingId = 'bsid1';
+      },
+      ig => {
+        ig.ads[0].buyerAndSellerReportingId = 'bsid1';
+        ig.ads[1].buyerAndSellerReportingId = 'bsid2';
+      });
 }, 'Basic B&A auction - buyerAndSellerReportingId in ad');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ false, msg => {
-    msg.buyerAndSellerReportingId = 'bsid2';
-  }, ig => {
-    ig.ads[0].buyerAndSellerReportingId = 'bsid1';
-    ig.ads[1].buyerAndSellerReportingId = 'bsid2';
-  });
+  await BA.testWithMutatedServerResponse(
+      test, /*expectSuccess=*/ false,
+      msg => {
+        msg.buyerAndSellerReportingId = 'bsid2';
+      },
+      ig => {
+        ig.ads[0].buyerAndSellerReportingId = 'bsid1';
+        ig.ads[1].buyerAndSellerReportingId = 'bsid2';
+      });
 }, 'Basic B&A auction - buyerAndSellerReportingId in wrong ad');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ false, msg => {
-    msg.components = ["https://example.org"];
-  });
+  await BA.testWithMutatedServerResponse(
+      test, /*expectSuccess=*/ false, msg => {
+        msg.components = ['https://example.org'];
+      });
 }, 'Basic B&A auction - ad component URL not in ad');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(
+  await BA.testWithMutatedServerResponse(
       test, /*expectSuccess=*/ true,
       msg => {
         msg.components = ['https://example.org'];
@@ -762,7 +726,7 @@
 subsetTest(promise_test, async test => {
   let savedUuid;
   let savedExpectUrls;
-  let result = await testWithMutatedServerResponse(
+  let result = await BA.testWithMutatedServerResponse(
       test, /*expectSuccess=*/ true,
       (msg, uuid) => {
         savedUuid = uuid;
@@ -809,7 +773,7 @@
 
 subsetTest(promise_test, async test => {
   let savedUuid;
-  let result = await testWithMutatedServerResponse(
+  let result = await BA.testWithMutatedServerResponse(
       test, /*expectWin=*/ true,
       (msg, uuid) => {
         savedUuid = uuid;
@@ -852,13 +816,14 @@
 }, 'Basic B&A auction --- beacon reporting');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ false, msg => {
-    msg.bidCurrency = 'cents';
-  });
+  await BA.testWithMutatedServerResponse(
+      test, /*expectSuccess=*/ false, msg => {
+        msg.bidCurrency = 'cents';
+      });
 }, 'Basic B&A auction - invalid ad currency');
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ true, msg => {
+  await BA.testWithMutatedServerResponse(test, /*expectSuccess=*/ true, msg => {
     msg.bidCurrency = 'USD';
   });
 }, 'Basic B&A auction - valid ad currency');
@@ -1145,7 +1110,7 @@
 /////////////////////////////////////////////////////////////////////////////
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ true, msg => {
+  await BA.testWithMutatedServerResponse(test, /*expectSuccess=*/ true, msg => {
     msg.updateGroups =
         {[window.location.origin]: [{index: 2048, updateIfOlderThanMs: 1000}]};
   });
@@ -1153,7 +1118,7 @@
 
 
 subsetTest(promise_test, async test => {
-  await testWithMutatedServerResponse(test, /*expectSuccess=*/ true, msg => {
+  await BA.testWithMutatedServerResponse(test, /*expectSuccess=*/ true, msg => {
     msg.updateGroups = {
       [window.location.origin]: [
         {index: 0, updateIfOlderThanMs: 1000},
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/tools/gentestutilsunion.py b/third_party/blink/web_tests/external/wpt/html/canvas/tools/gentestutilsunion.py
index dbd729c..c9d4963d 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/tools/gentestutilsunion.py
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/tools/gentestutilsunion.py
@@ -30,7 +30,7 @@
 # * Test the tests, add new ones to Git, remove deleted ones from Git, etc.
 
 from typing import Any, DefaultDict, FrozenSet, List, Mapping, MutableMapping
-from typing import Optional, Set, Tuple
+from typing import Set, Union
 
 import re
 import collections
@@ -38,7 +38,6 @@
 import dataclasses
 import enum
 import importlib
-import itertools
 import math
 import os
 import pathlib
@@ -156,8 +155,6 @@
 
     code = re.sub(r'@moz-UniversalBrowserRead;', '', code)
 
-    code = _remove_extra_newlines(code)
-
     code = re.sub(r'@nonfinite ([^(]+)\(([^)]+)\)(.*)', lambda m:
                   _expand_nonfinite(m.group(1), m.group(2), m.group(3)),
                   code)  # Must come before '@assert throws'.
@@ -283,11 +280,15 @@
 
 def _preprocess_code(jinja_env: jinja2.Environment, code: str,
                      params: _TestParams) -> str:
-    code = _expand_test_code(code)
+    code = _remove_extra_newlines(code)
+
     # Render the code on its own, as it could contain templates expanding
     # to multiple lines. This is needed to get proper indentation of the
     # code in the main template.
     code = _render_template(jinja_env, jinja_env.from_string(code), params)
+
+    # Expand "@..." macros.
+    code = _expand_test_code(code)
     return code
 
 
@@ -438,7 +439,7 @@
                         variant_id: int) -> None:
         """Finalize this variant by adding computed param fields."""
         self._params['id'] = variant_id
-        for param_name in ('name', 'desc', 'attributes'):
+        for param_name in ('attributes', 'desc', 'expected', 'name'):
             self._render_param(jinja_env, param_name)
         self._params['file_name'] = self._get_file_name()
         self._params['canvas_types'] = self._get_canvas_types()
@@ -874,6 +875,29 @@
     tested[name].update(canvas_types)
 
 
+def _indent_filter(s: str, width: Union[int, str] = 4,
+                   first: bool = False, blank: bool = False) -> str:
+    """Returns a copy of the string with each line indented by the `width` str.
+
+    If `width` is a number, `s` is indented by that number of whitespaces. The
+    first line and blank lines are not indented by default, unless `first` or
+    `blank` are `True`, respectively.
+
+    This is a re-implementation of the default `indent` Jinja filter, preserving
+    line ending characters (\r, \n, \f, etc.) The default `indent` Jinja filter
+    incorrectly replaces all of these characters with newlines."""
+    is_first_line = True
+    def indent_needed(line):
+        nonlocal first, blank, is_first_line
+        is_blank = not line.strip()
+        need_indent = (not is_first_line or first) and (not is_blank or blank)
+        is_first_line = False
+        return need_indent
+
+    indentation = width if isinstance(width, str) else ' ' * width
+    return textwrap.indent(s, indentation, indent_needed)
+
+
 def generate_test_files(name_to_dir_file: str) -> None:
     """Generate Canvas tests from YAML file definition."""
     output_dirs = _OutputPaths(element=pathlib.Path('..') / 'element',
@@ -886,6 +910,7 @@
         lstrip_blocks=True)
 
     jinja_env.filters['double_quote_escape'] = _double_quote_escape
+    jinja_env.filters['indent'] = _indent_filter
 
     # Run with --test argument to run unit tests.
     if len(sys.argv) > 1 and sys.argv[1] == '--test':
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml-new/the-canvas.yaml b/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml-new/the-canvas.yaml
index 4153b8e..2058adc 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml-new/the-canvas.yaml
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml-new/the-canvas.yaml
@@ -23,6 +23,142 @@
        new_canvas: |-
          new OffscreenCanvas(100, 50)
 
+- name: 2d.canvas.host.size.attributes
+  canvas_types: ['HtmlCanvas']
+  code: |
+    {{ set_attributes -}}
+    @assert canvas.width === {{ result_size[0] }};
+    @assert canvas.height === {{ result_size[1] }};
+    {% if result_size[0] != 300 %}
+      {# With "100%", Opera gets canvas.width = 100 but renders at 100% of the
+        frame width, so check the CSS display width -#}
+      @assert window.getComputedStyle(canvas, null)\-
+          .getPropertyValue("width") === "{{ result_size[0] }}px";
+    {% endif -%}
+
+    {% set attrib_size = escaped_size or input_size %}
+    @assert canvas.getAttribute('width') === '{{ attrib_size }}';
+    @assert canvas.getAttribute('height') === '{{ attrib_size }}';
+  expected: |
+    {% if result_size[0] != 0 -%}
+      size {{ result_size[0] }} {{ result_size[1] }}
+    {% endif %}
+  variants:
+  - parse:
+      desc: Parsing of non-negative integers
+      size: ['{{ input_size }}', '{{ input_size }}']
+    setAttribute:
+      desc: Parsing of non-negative integers in setAttribute
+      size: [ 50, 50 ]
+      set_attributes: |
+        canvas.setAttribute('width', '{{ escaped_size or input_size }}');
+        canvas.setAttribute('height', '{{ escaped_size or input_size }}');
+  - zero:
+      input_size: '0'
+      result_size: [0, 0]
+    empty:
+      input_size: ''
+      result_size: [300, 150]
+    onlyspace:
+      input_size: '  '
+      result_size: [300, 150]
+    space:
+      input_size: '  100'
+      result_size: [100, 100]
+    whitespace:
+      input_size: "&#xD;\n\t\x0c100"
+      escaped_size: "\\r\\n\\t\\x0c100"
+      result_size: [100, 100]
+    plus:
+      input_size: '+100'
+      result_size: [100, 100]
+    minus:
+      input_size: '-100'
+      result_size: [300, 150]
+    octal:
+      input_size: '0100'
+      result_size: [100, 100]
+    hex:
+      input_size: '0x100'
+      result_size: [0, 0]
+    exp:
+      input_size: '100e1'
+      result_size: [100, 100]
+    decimal:
+      input_size: '100.999'
+      result_size: [100, 100]
+    percent:
+      input_size: '100%'
+      result_size: [100, 100]
+    em:
+      input_size: '100em'
+      result_size: [100, 100]
+    junk:
+      input_size: '#!?'
+      result_size: [300, 150]
+    trailingjunk:
+      input_size: '100#!?'
+      result_size: [100, 100]
+
+- name: 2d.canvas.host.size.attributes.parse
+  desc: Parsing of non-negative integers
+  canvas_types: ['OffscreenCanvas', 'Worker']
+  code: |
+    {% if result_size == 'exception' %}
+    @assert throws TypeError canvas.width = '{{ input_size }}';
+    {% else %}
+    canvas.width = '{{ input_size }}';
+    canvas.height = '{{ input_size }}';
+    @assert canvas.width === {{ result_size }};
+    @assert canvas.height === {{ result_size }};
+    {% endif %}
+  variants:
+  - zero:
+      input_size: '0'
+      result_size: 0
+    empty:
+      input_size: ''
+      result_size: 0
+    onlyspace:
+      input_size: '  '
+      result_size: 0
+    space:
+      input_size: '  100'
+      result_size: 100
+    whitespace:
+      input_size: "\t\f100"
+      result_size: 100
+    plus:
+      input_size: '+100'
+      result_size: 100
+    minus:
+      input_size: '-100'
+      result_size: 'exception'
+    octal:
+      input_size: '0100'
+      result_size: 100
+    hex:
+      input_size: '0x100'
+      result_size: 0x100
+    exp:
+      input_size: '100e1'
+      result_size: 1000.0
+    decimal:
+      input_size: '100.999'
+      result_size: 100
+    percent:
+      input_size: '100%'
+      result_size: 'exception'
+    em:
+      input_size: '100em'
+      result_size: 'exception'
+    junk:
+      input_size: '#!?'
+      result_size: 'exception'
+    trailingjunk:
+      input_size: '100#!?'
+      result_size: 'exception'
+
 - name: 2d.canvas.host.type.delete
   canvas_types: ['HtmlCanvas']
   desc: window.HTMLCanvasElement interface object is [[Configurable]]
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/element/meta.yaml b/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/element/meta.yaml
index 358333d9..65ef32cb 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/element/meta.yaml
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/element/meta.yaml
@@ -1,66 +1,4 @@
 - meta: |
-    cases = [
-        ("zero", "0", 0),
-        ("empty", "", None),
-        ("onlyspace", "  ", None),
-        ("space", "  100", 100),
-        ("whitespace", "\r\n\t\f100", 100),
-        ("plus", "+100", 100),
-        ("minus", "-100", None),
-        ("octal", "0100", 100),
-        ("hex", "0x100", 0),
-        ("exp", "100e1", 100),
-        ("decimal", "100.999", 100),
-        ("percent", "100%", 100),
-        ("em", "100em", 100),
-        ("junk", "#!?", None),
-        ("trailingjunk", "100#!?", 100),
-    ]
-    def gen(name, string, exp, code):
-        if exp is None:
-            code += "@assert canvas.width === 300;\n@assert canvas.height === 150;\n"
-            expected = "size 300 150"
-        else:
-            code += "@assert canvas.width === %s;\n@assert canvas.height === %s;\n" % (exp, exp)
-            expected = "size %s %s" % (exp, exp)
-
-            # With "100%", Opera gets canvas.width = 100 but renders at 100% of the frame width,
-            # so check the CSS display width
-            code += '@assert window.getComputedStyle(canvas, null).getPropertyValue("width") === "%spx";\n' % (exp, )
-
-        code += "@assert canvas.getAttribute('width') === %r;\n" % string
-        code += "@assert canvas.getAttribute('height') === %r;\n" % string
-
-        if exp == 0:
-            expected = None # can't generate zero-sized PNGs for the expected image
-
-        return code, expected
-
-    for name, string, exp in cases:
-        code = ""
-        code, expected = gen(name, string, exp, code)
-        # We need to replace \r with &#xD; because \r\n gets converted to \n in the HTML parser.
-        htmlString = string.replace('\r', '&#xD;')
-        tests.append( {
-            "name": "2d.canvas.host.size.attributes.parse.%s" % name,
-            "desc": "Parsing of non-negative integers",
-            "size": '%s, %s' % (htmlString, htmlString),
-            "code": code,
-            "expected": expected
-        } )
-
-    for name, string, exp in cases:
-        code = "canvas.setAttribute('width', %r);\ncanvas.setAttribute('height', %r);\n" % (string, string)
-        code, expected = gen(name, string, exp, code)
-        tests.append( {
-            "name": "2d.canvas.host.size.attributes.setAttribute.%s" % name,
-            "desc": "Parsing of non-negative integers in setAttribute",
-            "size": '50, 50',
-            "code": code,
-            "expected": expected
-        } )
-
-- meta: |
     # Composite operation tests
     # <http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2007-March/010608.html>
     ops = [
diff --git a/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/offscreen/meta.yaml b/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/offscreen/meta.yaml
index 9c1cfbf..3026609 100644
--- a/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/offscreen/meta.yaml
+++ b/third_party/blink/web_tests/external/wpt/html/canvas/tools/yaml/offscreen/meta.yaml
@@ -429,50 +429,3 @@
     """ % (string,),
         }
         tests.append(test)
-
-- meta: |
-    cases = [
-        ("zero", "0", 0),
-        ("empty", "", 0),
-        ("onlyspace", "  ", 0),
-        ("space", "  100", 100),
-        ("whitespace", "\t\f100", 100),
-        ("plus", "+100", 100),
-        ("minus", "-100", "exception"),
-        ("octal", "0100", 100),
-        ("hex", "0x100", 0x100),
-        ("exp", "100e1", 100e1),
-        ("decimal", "100.999", 100),
-        ("percent", "100%", "exception"),
-        ("em", "100em", "exception"),
-        ("junk", "#!?", "exception"),
-        ("trailingjunk", "100#!?", "exception"),
-    ]
-    def gen(name, string, exp, code):
-        if exp is None:
-            code += "canvas.width = '%s';\ncanvas.height = '%s';\n" % (string, string)
-            code += "@assert canvas.width === 100;\n@assert canvas.height === 50;\n"
-            expected = None
-        elif exp == "exception":
-            code += "@assert throws TypeError canvas.width = '%s';\n" % string
-            expected = None
-        else:
-            code += "canvas.width = '%s';\ncanvas.height = '%s';\n" % (string, string)
-            code += "@assert canvas.width === %s;\n@assert canvas.height === %s;\n" % (exp, exp)
-            expected = None
-
-        code += "t.done();\n"
-
-        if exp == 0:
-            expected = None # can't generate zero-sized PNGs for the expected image
-
-        return code, expected
-
-    for name, string, exp in cases:
-        code = ""
-        code, expected = gen(name, string, exp, code)
-        tests.append( {
-            "name": "2d.canvas.host.size.attributes.parse.%s" % name,
-            "desc": "Parsing of non-negative integers",
-            "code": code,
-        } )
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-optgroup-legend-and-label.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-optgroup-legend-and-label.tentative.html
index 666dde2f..e8400ed 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-optgroup-legend-and-label.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-appearance-optgroup-legend-and-label.tentative.html
@@ -1,9 +1,9 @@
 <!DOCTYPE html>
 <html class=reftest-wait>
 <link rel=author href="mailto:jarhar@chromium.org">
-<link rel=help href="https://github.com/whatwg/html/issues/9799">
-<link rel=mismatch href="select-appearance-optgroup-legend-ref.html">
-<meta name=assert content="The label attribute should take precedence over the legend element">
+<link rel=help href="https://issues.chromium.org/issues/378601807">
+<link rel=match href="select-appearance-optgroup-legend-ref.html">
+<meta name=assert content="The legend element should take precedence over the label attribute">
 <script src="/resources/testdriver.js"></script>
 <script src="/resources/testdriver-vendor.js"></script>
 
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-innertext.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-innertext.tentative.html
new file mode 100644
index 0000000..a3e0482d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-innertext.tentative.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://issues.chromium.org/issues/387440276">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<select id=oldcontentmodel>
+  <option>one</option>
+  <optgroup label=emptygroup></optgroup>
+  <optgroup label=optgroup>
+    <option>two</option>
+  </optgroup>
+</select>
+
+<select id=newcontentmodel>
+  <option>one</option>
+  <optgroup label=emptygroup></optgroup>
+  <div>
+    <optgroup label=optgroup>
+      <div>
+        <option>
+          <span>two</span>
+        </option>
+      </div>
+    </optgroup>
+  </div>
+</select>
+
+<style>
+.base, .base::picker(select) {
+  appearance: base-select;
+}
+</style>
+
+<script>
+const expectedText = 'one\ntwo';
+const oldSelect = document.getElementById('oldcontentmodel');
+const newSelect = document.getElementById('newcontentmodel');
+
+test(() => {
+  assert_equals(oldSelect.innerText, expectedText);
+}, '<select> innerText with old content model and appearance:auto.');
+
+test(() => {
+  assert_equals(newSelect.innerText, expectedText);
+}, '<select> innerText with new content model and appearance:auto.');
+
+promise_test(async () => {
+  newSelect.classList.add('base');
+  await new Promise(requestAnimationFrame);
+  assert_equals(newSelect.innerText, expectedText);
+}, '<select> innerText with new content model and appearance:base-select.');
+
+promise_test(async () => {
+  await test_driver.bless();
+  newSelect.showPicker();
+  assert_equals(newSelect.innerText, expectedText);
+}, '<select> innerText with new content model and appearance:base-select with picker open.');
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interestelement-interface.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interestelement-interface.tentative.html
index 8b1e375..bc68dc5 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interestelement-interface.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interestelement-interface.tentative.html
@@ -84,82 +84,4 @@
       "interestTargetElement attribute value must be an instance of Element",
     );
   }, "interestTargetElement throws error on assignment of non Element");
-
-  test(function () {
-    assert_false(buttonInvoker.hasAttribute("interestaction"));
-    assert_equals(buttonInvoker.interestAction, "");
-    assert_false(aInvoker.hasAttribute("interestaction"));
-    assert_equals(aInvoker.interestAction, "");
-    assert_false(inputInvoker.hasAttribute("interestaction"));
-    assert_equals(inputInvoker.interestAction, "");
-  }, "interestAction reflects '' when attribute not present");
-
-  test(function () {
-    buttonInvoker.setAttribute("interestaction", "");
-    assert_equals(buttonInvoker.getAttribute("interestaction"), "");
-    assert_equals(buttonInvoker.interestAction, "");
-    aInvoker.setAttribute("interestaction", "");
-    assert_equals(aInvoker.getAttribute("interestaction"), "");
-    assert_equals(aInvoker.interestAction, "");
-    inputInvoker.setAttribute("interestaction", "");
-    assert_equals(inputInvoker.getAttribute("interestaction"), "");
-    assert_equals(inputInvoker.interestAction, "");
-  }, "interestAction reflects '' when attribute empty, setAttribute version");
-
-  test(function () {
-      buttonInvoker.interestAction = "";
-      assert_equals(buttonInvoker.getAttribute("interestaction"), "");
-      assert_equals(buttonInvoker.interestAction, "");
-      aInvoker.interestAction = "";
-      assert_equals(aInvoker.getAttribute("interestaction"), "");
-      assert_equals(aInvoker.interestAction, "");
-      inputInvoker.interestAction = "";
-      assert_equals(inputInvoker.getAttribute("interestaction"), "");
-      assert_equals(inputInvoker.interestAction, "");
-  }, "interestAction reflects '' when attribute empty, IDL setter version");
-
-  test(function () {
-      buttonInvoker.interestAction = "fooBarBaz";
-      assert_equals(buttonInvoker.getAttribute("interestaction"), "fooBarBaz");
-      assert_equals(buttonInvoker.interestAction, "fooBarBaz");
-      aInvoker.interestAction = "fooBarBaz";
-      assert_equals(aInvoker.getAttribute("interestaction"), "fooBarBaz");
-      assert_equals(aInvoker.interestAction, "fooBarBaz");
-      inputInvoker.interestAction = "fooBarBaz";
-      assert_equals(inputInvoker.getAttribute("interestaction"), "fooBarBaz");
-      assert_equals(inputInvoker.interestAction, "fooBarBaz");
-  }, "interestAction reflects same casing");
-
-  test(function () {
-      buttonInvoker.interestAction = [];
-      assert_equals(buttonInvoker.getAttribute("interestaction"), "");
-      assert_equals(buttonInvoker.interestAction, "");
-      aInvoker.interestAction = [];
-      assert_equals(aInvoker.getAttribute("interestaction"), "");
-      assert_equals(aInvoker.interestAction, "");
-      inputInvoker.interestAction = [];
-      assert_equals(inputInvoker.getAttribute("interestaction"), "");
-      assert_equals(inputInvoker.interestAction, "");
-  }, "interestAction reflects '' when attribute set to []");
-
-  test(function () {
-      buttonInvoker.interestAction = [1, 2, 3];
-      assert_equals(buttonInvoker.getAttribute("interestaction"), "1,2,3");
-      assert_equals(buttonInvoker.interestAction, "1,2,3");
-      aInvoker.interestAction = [1, 2, 3];
-      assert_equals(aInvoker.getAttribute("interestaction"), "1,2,3");
-      assert_equals(aInvoker.interestAction, "1,2,3");
-      inputInvoker.interestAction = [1, 2, 3];
-      assert_equals(inputInvoker.getAttribute("interestaction"), "1,2,3");
-      assert_equals(inputInvoker.interestAction, "1,2,3");
-  }, "interestAction reflects tostring value");
-
-  test(function () {
-      buttonInvoker.interestAction = {};
-      assert_equals(buttonInvoker.interestAction, "[object Object]");
-      aInvoker.interestAction = {};
-      assert_equals(aInvoker.interestAction, "[object Object]");
-      inputInvoker.interestAction = {};
-      assert_equals(inputInvoker.interestAction, "[object Object]");
-  }, "interestAction reflects tostring value 2");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interestevent-dispatch-shadow.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interestevent-dispatch-shadow.tentative.html
index d96907e..9911357e 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interestevent-dispatch-shadow.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interestevent-dispatch-shadow.tentative.html
@@ -1,8 +1,11 @@
-<!doctype html>
+<!DOCTYPE html>
 <meta charset="utf-8" />
 <meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" />
 <meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" />
-<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" />
+<link
+  rel="help"
+  href="https://open-ui.org/components/interest-invokers.explainer/"
+/>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/resources/testdriver.js"></script>
@@ -20,63 +23,35 @@
     const slot = shadow.appendChild(document.createElement("slot"));
     let childEvent = null;
     let childEventTarget = null;
-    let childEventInvoker = null;
+    let childEventSource = null;
     let hostEvent = null;
     let hostEventTarget = null;
-    let hostEventInvoker = null;
-    slot.addEventListener(
-      "interest",
-      (e) => {
+    let hostEventSource = null;
+    slot.addEventListener("interest", (e) => {
         childEvent = e;
         childEventTarget = e.target;
-        childEventInvoker = e.invoker;
-      },
-      { once: true },
-    );
-    host.addEventListener(
-      "interest",
-      (e) => {
+        childEventSource = e.source;
+      }, { once: true });
+    host.addEventListener("interest", (e) => {
         hostEvent = e;
         hostEventTarget = e.target;
-        hostEventInvoker = e.invoker;
-      },
-      { once: true },
-    );
+        hostEventSource = e.source;
+      }, { once: true });
     const event = new InterestEvent("interest", {
       bubbles: true,
-      invoker: slot,
+      source: slot,
       composed: true,
     });
     slot.dispatchEvent(event);
     assert_true(childEvent instanceof InterestEvent, "slot saw interest event");
-    assert_equals(
-      childEventTarget,
-      slot,
-      "target is child inside shadow boundary",
-    );
-    assert_equals(
-      childEventInvoker,
-      slot,
-      "invoker is child inside shadow boundary",
-    );
-    assert_equals(
-      hostEvent,
-      childEvent,
-      "event dispatch propagates across shadow boundary",
-    );
-    assert_equals(
-      hostEventTarget,
-      host,
-      "target is retargeted to shadowroot host",
-    );
-    assert_equals(
-      hostEventInvoker,
-      host,
-      "invoker is retargeted to shadowroot host",
-    );
-  }, "InterestEvent propagates across shadow boundaries retargeting invoker");
+    assert_equals(childEventTarget, slot, "target is child inside shadow boundary");
+    assert_equals(childEventSource, slot, "source is child inside shadow boundary");
+    assert_equals(hostEvent, childEvent, "event dispatch propagates across shadow boundary");
+    assert_equals(hostEventTarget, host, "target is retargeted to shadowroot host");
+    assert_equals(hostEventSource, host, "source is retargeted to shadowroot host");
+  }, "InterestEvent propagates across shadow boundaries retargeting invoker source");
 
-  test(function (t) {
+  promise_test(async (t) => {
     const host = document.createElement("div");
     document.body.append(host);
     t.add_cleanup(() => host.remove());
@@ -86,19 +61,16 @@
     button.interestTargetElement = interestee;
     let event = null;
     let eventTarget = null;
-    let eventInvoker = null;
-    interestee.addEventListener(
-      "interest",
-      (e) => {
+    let eventSource = null;
+    interestee.addEventListener("interest", (e) => {
         event = e;
         eventTarget = e.target;
-        eventInvoker = e.invoker;
-      },
-      { once: true },
-    );
-    button.focus();
+        eventSource = e.source;
+      },{ once: true });
+    await hoverOver(button);
+    assert_true(!!event,"InterestEvent gets fired");
     assert_true(event instanceof InterestEvent);
     assert_equals(eventTarget, interestee, "target is interestee");
-    assert_equals(eventInvoker, host, "interestee is host");
+    assert_equals(eventSource, host, "interestee is host");
   }, "cross shadow InterestEvent retargets interestee to host element");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interestevent-interface.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interestevent-interface.tentative.html
index ed7d82f..9ed95a6 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interestevent-interface.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interestevent-interface.tentative.html
@@ -16,152 +16,80 @@
 <script>
   test(function () {
     const event = new InterestEvent("test");
-    assert_equals(event.action, "");
-    assert_readonly(event, "action", "readonly attribute value");
-  }, "action is a readonly defaulting to ''");
+    assert_equals(event.source, null);
+    assert_readonly(event, "source", "readonly attribute value");
+  }, "source is readonly defaulting to null");
 
   test(function () {
-    const event = new InterestEvent("test");
-    assert_equals(event.invoker, null);
-    assert_readonly(event, "invoker", "readonly attribute value");
-  }, "invoker is readonly defaulting to null");
-
-  test(function () {
-    const event = new InterestEvent("test", { action: "sAmPle" });
-    assert_equals(event.action, "sAmPle");
-  }, "action reflects initialized attribute");
-
-  test(function () {
-    const event = new InterestEvent("test", { action: undefined });
-    assert_equals(event.action, "");
-  }, "action set to undefined");
-
-  test(function () {
-    const event = new InterestEvent("test", { action: null });
-    assert_equals(event.action, "null");
-  }, "action set to null");
-
-  test(function () {
-    const event = new InterestEvent("test", { action: false });
-    assert_equals(event.action, "false");
-  }, "action set to false");
-
-  test(function () {
-    const event = new InterestEvent("test", { action: "" });
-    assert_equals(event.action, "");
-  }, "action explicitly set to empty string");
-
-  test(function () {
-    const event = new InterestEvent("test", { action: true });
-    assert_equals(event.action, "true");
-  }, "action set to true");
-
-  test(function () {
-    const event = new InterestEvent("test", { action: 0.5 });
-    assert_equals(event.action, "0.5");
-  }, "action set to a number");
-
-  test(function () {
-    const event = new InterestEvent("test", { action: [] });
-    assert_equals(event.action, "");
-  }, "action set to []");
-
-  test(function () {
-    const event = new InterestEvent("test", { action: [1, 2, 3] });
-    assert_equals(event.action, "1,2,3");
-  }, "action set to [1, 2, 3]");
-
-  test(function () {
-    const event = new InterestEvent("test", { action: { sample: 0.5 } });
-    assert_equals(event.action, "[object Object]");
-  }, "action set to an object");
-
-  test(function () {
-    const event = new InterestEvent("test", {
-      action: {
-        toString() {
-          return "sample";
-        },
-      },
-    });
-    assert_equals(event.action, "sample");
-  }, "action set to an object with a toString function");
-
-  test(function () {
-    const eventInit = { action: "sample", invoker: document.body };
+    const eventInit = { source: document.body };
     const event = new InterestEvent("test", eventInit);
-    assert_equals(event.action, "sample");
-    assert_equals(event.invoker, document.body);
-  }, "InterestEventInit properties set value");
+    assert_equals(event.source, document.body);
+  }, "InterestEventInit properties set value (manual event)");
 
   test(function () {
     const eventInit = {
-      action: "open",
-      invoker: document.getElementById("div"),
+      source: document.getElementById("div"),
     };
     const event = new InterestEvent("beforetoggle", eventInit);
-    assert_equals(event.action, "open");
-    assert_equals(event.invoker, document.getElementById("div"));
-  }, "InterestEventInit properties set value 2");
+    assert_equals(event.source, document.getElementById("div"));
+  }, "InterestEventInit properties set value (beforetoggle event)");
 
   test(function () {
     const eventInit = {
-      action: "closed",
-      invoker: document.getElementById("button"),
+      source: document.getElementById("button"),
     };
     const event = new InterestEvent("toggle", eventInit);
-    assert_equals(event.action, "closed");
-    assert_equals(event.invoker, document.getElementById("button"));
-  }, "InterestEventInit properties set value 3");
+    assert_equals(event.source, document.getElementById("button"));
+  }, "InterestEventInit properties set value (toggle event)");
 
   test(function () {
-    const event = new InterestEvent("test", { invoker: undefined });
-    assert_equals(event.invoker, null);
-  }, "invoker set to undefined");
+    const event = new InterestEvent("test", { source: undefined });
+    assert_equals(event.source, null);
+  }, "source set to undefined");
 
   test(function () {
-    const event = new InterestEvent("test", { invoker: null });
-    assert_equals(event.invoker, null);
-  }, "invoker set to null");
+    const event = new InterestEvent("test", { source: null });
+    assert_equals(event.source, null);
+  }, "source set to null");
 
   test(function () {
     assert_throws_js(
       TypeError,
       function () {
-        new InterestEvent("test", { invoker: false });
+        new InterestEvent("test", { source: false });
       },
-      "invoker is not an object",
+      "source is not an object",
     );
-  }, "invoker set to false");
+  }, "source set to false");
 
   test(function () {
     assert_throws_js(
       TypeError,
       function () {
-        const event = new InterestEvent("test", { invoker: true });
+        const event = new InterestEvent("test", { source: true });
       },
-      "invoker is not an object",
+      "source is not an object",
     );
-  }, "invoker set to true");
+  }, "source set to true");
 
   test(function () {
     assert_throws_js(
       TypeError,
       function () {
-        const event = new InterestEvent("test", { invoker: {} });
+        const event = new InterestEvent("test", { source: {} });
       },
-      "invoker is not an object",
+      "source is not an object",
     );
-  }, "invoker set to {}");
+  }, "source set to {}");
 
   test(function () {
     assert_throws_js(
       TypeError,
       function () {
-        const eventInit = { action: "closed", invoker: new XMLHttpRequest() };
+        const eventInit = { source: new XMLHttpRequest() };
         const event = new InterestEvent("toggle", eventInit);
       },
-      "invoker is not an Element",
+      "source is not an Element",
     );
-  }, "invoker set to non-Element EventTarget");
+  }, "source set to non-Element EventTarget");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-anchor-event-dispatch.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-anchor-event-dispatch.tentative.html
index b5a481a..88a54c21 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-anchor-event-dispatch.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-anchor-event-dispatch.tentative.html
@@ -19,29 +19,14 @@
     t.add_cleanup(() => otherbutton.focus());
     let event = null;
     interestee.addEventListener("interest", (e) => (event = e), { once: true });
-    interestanchor.focus();
-    assert_true(event instanceof InterestEvent, "event is InterestEvent");
-    assert_equals(event.type, "interest", "type");
-    assert_equals(event.bubbles, false, "bubbles");
-    assert_equals(event.composed, true, "composed");
-    assert_equals(event.isTrusted, true, "isTrusted");
-    assert_equals(event.action, "", "action");
-    assert_equals(event.target, interestee, "target");
-    assert_equals(event.invoker, interestanchor, "invoker");
-  }, "InterestEvent dispatches on anchor focus");
-
-  promise_test(async function (t) {
-    t.add_cleanup(() => otherbutton.focus());
-    let event = null;
-    interestee.addEventListener("interest", (e) => (event = e), { once: true });
     await hoverOver(interestanchor);
+    assert_true(!!event, "InterestEvent is fired");
     assert_true(event instanceof InterestEvent, "event is InterestEvent");
     assert_equals(event.type, "interest", "type");
     assert_equals(event.bubbles, false, "bubbles");
     assert_equals(event.composed, true, "composed");
     assert_equals(event.isTrusted, true, "isTrusted");
-    assert_equals(event.action, "", "action");
     assert_equals(event.target, interestee, "target");
-    assert_equals(event.invoker, interestanchor, "invoker");
+    assert_equals(event.source, interestanchor, "source");
   }, "InterestEvent dispatches on anchor hover");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-area-event-dispatch.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-area-event-dispatch.tentative.html
index 358acbb..5573235 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-area-event-dispatch.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-area-event-dispatch.tentative.html
@@ -22,29 +22,14 @@
     t.add_cleanup(() => otherbutton.focus());
     let event = null;
     interestee.addEventListener("interest", (e) => (event = e), { once: true });
-    interestarea.focus();
-    assert_true(event instanceof InterestEvent, "event is InterestEvent");
-    assert_equals(event.type, "interest", "type");
-    assert_equals(event.bubbles, false, "bubbles");
-    assert_equals(event.composed, true, "composed");
-    assert_equals(event.isTrusted, true, "isTrusted");
-    assert_equals(event.action, "", "action");
-    assert_equals(event.target, interestee, "target");
-    assert_equals(event.invoker, interestarea, "invoker");
-  }, "InterestEvent dispatches on area focus");
-
-  promise_test(async function (t) {
-    t.add_cleanup(() => otherbutton.focus());
-    let event = null;
-    interestee.addEventListener("interest", (e) => (event = e), { once: true });
     await hoverOver(interestarea);
+    assert_true(!!event, "InterestEvent is fired");
     assert_true(event instanceof InterestEvent, "event is InterestEvent");
     assert_equals(event.type, "interest", "type");
     assert_equals(event.bubbles, false, "bubbles");
     assert_equals(event.composed, true, "composed");
     assert_equals(event.isTrusted, true, "isTrusted");
-    assert_equals(event.action, "", "action");
     assert_equals(event.target, interestee, "target");
-    assert_equals(event.invoker, interestarea, "invoker");
+    assert_equals(event.source, interestarea, "source");
   }, "InterestEvent dispatches on area hover");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-button-event-dispatch.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-button-event-dispatch.tentative.html
index 69126dbe..a9d68bb03 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-button-event-dispatch.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-button-event-dispatch.tentative.html
@@ -16,71 +16,23 @@
 
 <script>
   promise_test(async function (t) {
-    t.add_cleanup(() => otherbutton.focus());
-    let event = null;
-    interestee.addEventListener("interest", (e) => (event = e), { once: true });
-    interestbutton.focus();
-    assert_true(event instanceof InterestEvent, "event is InterestEvent");
-    assert_equals(event.type, "interest", "type");
-    assert_equals(event.bubbles, false, "bubbles");
-    assert_equals(event.composed, true, "composed");
-    assert_equals(event.isTrusted, true, "isTrusted");
-    assert_equals(event.action, "", "action");
-    assert_equals(event.target, interestee, "target");
-    assert_equals(event.invoker, interestbutton, "invoker");
-  }, "InterestEvent dispatches on button focus");
-
-  promise_test(async function (t) {
-    t.add_cleanup(() => otherbutton.focus());
     let event = null;
     interestee.addEventListener("interest", (e) => (event = e), { once: true });
     await hoverOver(interestbutton);
+    assert_true(!!event, "InterestEvent is fired");
     assert_true(event instanceof InterestEvent, "event is InterestEvent");
     assert_equals(event.type, "interest", "type");
     assert_equals(event.bubbles, false, "bubbles");
     assert_equals(event.composed, true, "composed");
     assert_equals(event.isTrusted, true, "isTrusted");
-    assert_equals(event.action, "", "action");
     assert_equals(event.target, interestee, "target");
-    assert_equals(event.invoker, interestbutton, "invoker");
+    assert_equals(event.source, interestbutton, "source");
   }, "InterestEvent dispatches on button hover");
 
   promise_test(async function (t) {
-    t.add_cleanup(() => otherbutton.focus());
-    let event = null;
-    interestee.addEventListener("interest", (e) => (event = e), { once: true });
-    interestbutton.interestAction = "fooBar";
-    interestbutton.focus();
-    assert_true(event instanceof InterestEvent, "event is InterestEvent");
-    assert_equals(event.type, "interest", "type");
-    assert_equals(event.bubbles, false, "bubbles");
-    assert_equals(event.composed, true, "composed");
-    assert_equals(event.isTrusted, true, "isTrusted");
-    assert_equals(event.action, "fooBar", "action");
-    assert_equals(event.target, interestee, "target");
-    assert_equals(event.invoker, interestbutton, "invoker");
-  }, "event action is set to interestAction");
-
-  promise_test(async function (t) {
-    t.add_cleanup(() => otherbutton.focus());
-    let event = null;
-    interestee.addEventListener("interest", (e) => (event = e), { once: true });
-    interestbutton.setAttribute("interestaction", "BaRbAz");
-    interestbutton.focus();
-    assert_true(event instanceof InterestEvent, "event is InterestEvent");
-    assert_equals(event.type, "interest", "type");
-    assert_equals(event.bubbles, false, "bubbles");
-    assert_equals(event.composed, true, "composed");
-    assert_equals(event.isTrusted, true, "isTrusted");
-    assert_equals(event.action, "BaRbAz", "action");
-    assert_equals(event.target, interestee, "target");
-    assert_equals(event.invoker, interestbutton, "invoker");
-  }, "event action is set to interestaction attribute");
-
-  promise_test(async function (t) {
-    t.add_cleanup(() => {
+    t.add_cleanup(async () => {
       interestbutton.removeAttribute('disabled');
-      otherbutton.focus();
+      await hoverOver(otherbutton);
     });
     let called = false;
     interestee.addEventListener(
@@ -91,34 +43,27 @@
       { once: true },
     );
     interestbutton.setAttribute("disabled", "");
-    interestbutton.focus();
+    await hoverOver(interestbutton);
     assert_false(called, "event was not called");
   }, "event does not dispatch if invoker is disabled");
 
   promise_test(async function (t) {
     svgInterestee = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
-    t.add_cleanup(() => {
+    t.add_cleanup(async () => {
       interestbutton.interestTargetElement = interestee;
       svgInterestee.remove();
-      otherbutton.focus();
+      await hoverOver(otherbutton);
     });
     document.body.append(svgInterestee);
     let called = false;
     assert_false(svgInterestee instanceof HTMLElement);
     assert_true(svgInterestee instanceof Element);
     let event = null;
-    svgInterestee.addEventListener(
-      "interest",
-      (e) => {
-        event = e;
-        called = true;
-      },
-      { once: true },
-    );
+    svgInterestee.addEventListener("interest", (e) => (event = e), { once: true });
     interestbutton.interestTargetElement = svgInterestee;
-    interestbutton.focus();
-    assert_true(called, "event was called");
-    assert_equals(event.invoker, interestbutton, "event.invoker is set to right element");
+    await hoverOver(interestbutton);
+    assert_true(!!event, "InterestEvent is fired");
+    assert_equals(event.source, interestbutton, "event.source is set to right element");
     assert_equals(event.target, svgInterestee, "event.target is set to right element");
   }, "event dispatches if interestee is non-HTML Element");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-on-popover-behavior.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-on-popover-behavior.tentative.html
index fd0a77b9..cbb5e40 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-on-popover-behavior.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-on-popover-behavior.tentative.html
@@ -17,44 +17,17 @@
 <button id="otherbutton">Other button</button>
 
 <script>
-  function reset() {
+  async function reset() {
       hoverOver(otherbutton);
-      otherbutton.focus();
+      await hoverOver(otherbutton);
       interestee.hidePopover();
-      interestbutton.removeAttribute("interestaction");
   }
-
-  // auto
-
   promise_test(async function (t) {
     t.add_cleanup(reset);
     assert_false(interestee.matches(":popover-open"));
     await hoverOver(interestbutton);
     assert_true(interestee.matches(":popover-open"));
-  }, "hover interest invoking (as auto) closed popover opens");
-
-  promise_test(async function (t) {
-    t.add_cleanup(reset);
-    interestee.showPopover();
-    assert_true(interestee.matches(":popover-open"));
-    await hoverOver(interestbutton);
-    assert_false(interestee.matches(":popover-open"));
-  }, "hover interest invoking (as auto) open popover closes");
-
-  promise_test(async function (t) {
-    t.add_cleanup(reset);
-    assert_false(interestee.matches(":popover-open"));
-    interestbutton.focus();
-    assert_true(interestee.matches(":popover-open"));
-  }, "focus interest invoking (as auto) closed popover opens");
-
-  promise_test(async function (t) {
-    t.add_cleanup(reset);
-    interestee.showPopover();
-    assert_true(interestee.matches(":popover-open"));
-    interestbutton.focus();
-    assert_false(interestee.matches(":popover-open"));
-  }, "focus interest invoking (as auto) open popover closes");
+  }, "hover interest invoking closed popover opens");
 
   promise_test(async function (t) {
     t.add_cleanup(reset);
@@ -64,50 +37,5 @@
     });
     await hoverOver(interestbutton);
     assert_false(interestee.matches(":popover-open"));
-  }, "interest invoking (as auto) closed popover with preventDefault does not open");
-
-  // togglepopover
-
-  promise_test(async function (t) {
-    t.add_cleanup(reset);
-    assert_false(interestee.matches(":popover-open"));
-    interestbutton.setAttribute("interestaction", "toggle-popover");
-    await hoverOver(interestbutton);
-    assert_true(interestee.matches(":popover-open"));
-  }, "hover interest invoking (as togglepopover) closed popover opens");
-
-  promise_test(async function (t) {
-    t.add_cleanup(reset);
-    interestee.showPopover();
-    assert_true(interestee.matches(":popover-open"));
-    interestbutton.setAttribute("interestaction", "toggle-popover");
-    await hoverOver(interestbutton);
-    assert_false(interestee.matches(":popover-open"));
-  }, "hover interest invoking (as togglepopover) open popover closes");
-
-  promise_test(async function (t) {
-    t.add_cleanup(reset);
-    assert_false(interestee.matches(":popover-open"));
-    interestbutton.setAttribute("interestaction", "toggle-popover");
-    interestbutton.focus();
-    assert_true(interestee.matches(":popover-open"));
-  }, "focus interest invoking (as togglepopover) closed popover opens");
-
-  promise_test(async function (t) {
-    t.add_cleanup(reset);
-    interestee.showPopover();
-    assert_true(interestee.matches(":popover-open"));
-    interestbutton.setAttribute("interestaction", "toggle-popover");
-    interestbutton.focus();
-    assert_false(interestee.matches(":popover-open"));
-  }, "focus interest invoking (as togglepopover) open popover closes");
-
-  promise_test(async function (t) {
-    t.add_cleanup(reset);
-    assert_false(interestee.matches(":popover-open"));
-    interestbutton.setAttribute("interestaction", "tOgGlE-pOpOvEr");
-    interestbutton.focus();
-    assert_true(interestee.matches(":popover-open"));
-  }, "interest invoking (as togglepopover - case insensitive) closed popover opens");
-
+  }, "interest invoking closed popover with preventDefault does not open");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-svg-a-event-dispatch.tentative.html b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-svg-a-event-dispatch.tentative.html
index 7fb4b1c19..8208c8d 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-svg-a-event-dispatch.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/the-button-element/interest-target/interesttarget-svg-a-event-dispatch.tentative.html
@@ -20,32 +20,19 @@
 
 <script>
   promise_test(async function (t) {
-    t.add_cleanup(() => otherbutton.focus());
-    let event = null;
-    interestee.addEventListener("interest", (e) => (event = e), { once: true });
-    interestsvga.focus();
-    assert_true(event instanceof InterestEvent, "event is InterestEvent");
-    assert_equals(event.type, "interest", "type");
-    assert_equals(event.bubbles, false, "bubbles");
-    assert_equals(event.composed, true, "composed");
-    assert_equals(event.isTrusted, true, "isTrusted");
-    assert_equals(event.action, "", "action");
-    assert_equals(event.target, interestee, "target");
-    assert_equals(event.invoker, interestsvga, "invoker");
-  }, "InterestEvent dispatches on svg a focus");
-
-  promise_test(async function (t) {
-    t.add_cleanup(() => otherbutton.focus());
+    t.add_cleanup(async () => {
+      await hoverOver(otherbutton);
+    });
     let event = null;
     interestee.addEventListener("interest", (e) => (event = e), { once: true });
     await hoverOver(interestsvga);
+    assert_true(!!event, "InterestEvent is fired");
     assert_true(event instanceof InterestEvent, "event is InterestEvent");
     assert_equals(event.type, "interest", "type");
     assert_equals(event.bubbles, false, "bubbles");
     assert_equals(event.composed, true, "composed");
     assert_equals(event.isTrusted, true, "isTrusted");
-    assert_equals(event.action, "", "action");
     assert_equals(event.target, interestee, "target");
-    assert_equals(event.invoker, interestsvga, "invoker");
+    assert_equals(event.source, interestsvga, "source");
   }, "InterestEvent dispatches on svg a hover");
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/css-fonts-5.idl b/third_party/blink/web_tests/external/wpt/interfaces/css-fonts-5.idl
new file mode 100644
index 0000000..06461b0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/interfaces/css-fonts-5.idl
@@ -0,0 +1,54 @@
+// GENERATED CONTENT - DO NOT EDIT
+// Content was automatically extracted by Reffy into webref
+// (https://github.com/w3c/webref)
+// Source: CSS Fonts Module Level 5 (https://drafts.csswg.org/css-fonts-5/)
+
+[Exposed=Window]
+interface CSSFontFaceDescriptors : CSSStyleDeclaration {
+  attribute [LegacyNullToEmptyString] CSSOMString src;
+  attribute [LegacyNullToEmptyString] CSSOMString fontFamily;
+  attribute [LegacyNullToEmptyString] CSSOMString font-family;
+  attribute [LegacyNullToEmptyString] CSSOMString fontStyle;
+  attribute [LegacyNullToEmptyString] CSSOMString font-style;
+  attribute [LegacyNullToEmptyString] CSSOMString fontWeight;
+  attribute [LegacyNullToEmptyString] CSSOMString font-weight;
+  attribute [LegacyNullToEmptyString] CSSOMString fontStretch;
+  attribute [LegacyNullToEmptyString] CSSOMString font-stretch;
+  attribute [LegacyNullToEmptyString] CSSOMString fontWidth;
+  attribute [LegacyNullToEmptyString] CSSOMString font-width;
+  attribute [LegacyNullToEmptyString] CSSOMString fontSize;
+  attribute [LegacyNullToEmptyString] CSSOMString font-size;
+  attribute [LegacyNullToEmptyString] CSSOMString sizeAdjust;
+  attribute [LegacyNullToEmptyString] CSSOMString size-adjust;
+  attribute [LegacyNullToEmptyString] CSSOMString unicodeRange;
+  attribute [LegacyNullToEmptyString] CSSOMString unicode-range;
+  attribute [LegacyNullToEmptyString] CSSOMString fontFeatureSettings;
+  attribute [LegacyNullToEmptyString] CSSOMString font-feature-settings;
+  attribute [LegacyNullToEmptyString] CSSOMString fontVariationSettings;
+  attribute [LegacyNullToEmptyString] CSSOMString font-variation-settings;
+  attribute [LegacyNullToEmptyString] CSSOMString fontNamedInstance;
+  attribute [LegacyNullToEmptyString] CSSOMString font-named-instance;
+  attribute [LegacyNullToEmptyString] CSSOMString fontDisplay;
+  attribute [LegacyNullToEmptyString] CSSOMString font-display;
+  attribute [LegacyNullToEmptyString] CSSOMString fontLanguageOverride;
+  attribute [LegacyNullToEmptyString] CSSOMString font-language-override;
+  attribute [LegacyNullToEmptyString] CSSOMString ascentOverride;
+  attribute [LegacyNullToEmptyString] CSSOMString ascent-override;
+  attribute [LegacyNullToEmptyString] CSSOMString descentOverride;
+  attribute [LegacyNullToEmptyString] CSSOMString descent-override;
+  attribute [LegacyNullToEmptyString] CSSOMString lineGapOverride;
+  attribute [LegacyNullToEmptyString] CSSOMString line-gap-override;
+  attribute [LegacyNullToEmptyString] CSSOMString superscriptPositionOverride;
+  attribute [LegacyNullToEmptyString] CSSOMString superscript-position-override;
+  attribute [LegacyNullToEmptyString] CSSOMString subscriptPositionOverride;
+  attribute [LegacyNullToEmptyString] CSSOMString subscript-position-override;
+  attribute [LegacyNullToEmptyString] CSSOMString superscriptSizeOverride;
+  attribute [LegacyNullToEmptyString] CSSOMString superscript-size-override;
+  attribute [LegacyNullToEmptyString] CSSOMString subscriptSizeOverride;
+  attribute [LegacyNullToEmptyString] CSSOMString subscript-size-override;
+};
+
+[Exposed=Window]
+interface CSSFontFaceRule : CSSRule {
+  [SameObject, PutForwards=cssText] readonly attribute CSSFontFaceDescriptors style;
+};
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/css-fonts.idl b/third_party/blink/web_tests/external/wpt/interfaces/css-fonts.idl
index d5c9dc8..9b8034bc 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/css-fonts.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/css-fonts.idl
@@ -3,44 +3,6 @@
 // (https://github.com/w3c/webref)
 // Source: CSS Fonts Module Level 4 (https://drafts.csswg.org/css-fonts-4/)
 
-[Exposed=Window]
-interface CSSFontFaceDescriptors : CSSStyleDeclaration {
-  attribute [LegacyNullToEmptyString] CSSOMString src;
-  attribute [LegacyNullToEmptyString] CSSOMString fontFamily;
-  attribute [LegacyNullToEmptyString] CSSOMString font-family;
-  attribute [LegacyNullToEmptyString] CSSOMString fontStyle;
-  attribute [LegacyNullToEmptyString] CSSOMString font-style;
-  attribute [LegacyNullToEmptyString] CSSOMString fontWeight;
-  attribute [LegacyNullToEmptyString] CSSOMString font-weight;
-  attribute [LegacyNullToEmptyString] CSSOMString fontStretch;
-  attribute [LegacyNullToEmptyString] CSSOMString font-stretch;
-  attribute [LegacyNullToEmptyString] CSSOMString fontWidth;
-  attribute [LegacyNullToEmptyString] CSSOMString font-width;
-  attribute [LegacyNullToEmptyString] CSSOMString unicodeRange;
-  attribute [LegacyNullToEmptyString] CSSOMString unicode-range;
-  attribute [LegacyNullToEmptyString] CSSOMString fontFeatureSettings;
-  attribute [LegacyNullToEmptyString] CSSOMString font-feature-settings;
-  attribute [LegacyNullToEmptyString] CSSOMString fontVariationSettings;
-  attribute [LegacyNullToEmptyString] CSSOMString font-variation-settings;
-  attribute [LegacyNullToEmptyString] CSSOMString fontNamedInstance;
-  attribute [LegacyNullToEmptyString] CSSOMString font-named-instance;
-  attribute [LegacyNullToEmptyString] CSSOMString fontDisplay;
-  attribute [LegacyNullToEmptyString] CSSOMString font-display;
-  attribute [LegacyNullToEmptyString] CSSOMString fontLanguageOverride;
-  attribute [LegacyNullToEmptyString] CSSOMString font-language-override;
-  attribute [LegacyNullToEmptyString] CSSOMString ascentOverride;
-  attribute [LegacyNullToEmptyString] CSSOMString ascent-override;
-  attribute [LegacyNullToEmptyString] CSSOMString descentOverride;
-  attribute [LegacyNullToEmptyString] CSSOMString descent-override;
-  attribute [LegacyNullToEmptyString] CSSOMString lineGapOverride;
-  attribute [LegacyNullToEmptyString] CSSOMString line-gap-override;
-};
-
-[Exposed=Window]
-interface CSSFontFaceRule : CSSRule {
-  [SameObject, PutForwards=cssText] readonly attribute CSSFontFaceDescriptors style;
-};
-
 partial interface CSSRule {  const unsigned short FONT_FEATURE_VALUES_RULE = 14;
 };
 [Exposed=Window]
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/css-mixins.idl b/third_party/blink/web_tests/external/wpt/interfaces/css-mixins.idl
index 49806ab..6629b38 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/css-mixins.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/css-mixins.idl
@@ -5,3 +5,13 @@
 
 [Exposed=Window]
 interface CSSFunctionRule : CSSGroupingRule { };
+
+[Exposed=Window]
+interface CSSFunctionDescriptors : CSSStyleDeclaration {
+  attribute [LegacyNullToEmptyString] CSSOMString result;
+};
+
+[Exposed=Window]
+interface CSSFunctionDeclarations : CSSRule {
+  [SameObject, PutForwards=cssText] readonly attribute CSSFunctionDescriptors style;
+};
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/digital-credentials.idl b/third_party/blink/web_tests/external/wpt/interfaces/digital-credentials.idl
index e20079e..e4ebb3b 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/digital-credentials.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/digital-credentials.idl
@@ -19,5 +19,5 @@
 [Exposed=Window, SecureContext]
 interface DigitalCredential : Credential {
   readonly attribute DOMString protocol;
-  readonly attribute object data;
+  [SameObject] readonly attribute object data;
 };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/fedcm.idl b/third_party/blink/web_tests/external/wpt/interfaces/fedcm.idl
index 16b5b2f..07f7955f 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/fedcm.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/fedcm.idl
@@ -49,6 +49,7 @@
   USVString nonce;
   DOMString loginHint;
   DOMString domainHint;
+  sequence<USVString> fields;
   any params;
 };
 
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/html.idl b/third_party/blink/web_tests/external/wpt/interfaces/html.idl
index d4f8b9a7..3832a6f9 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/html.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/html.idl
@@ -1222,9 +1222,11 @@
 
   [CEReactions] attribute boolean open;
   attribute DOMString returnValue;
+  [CEReactions] attribute DOMString closedBy;
   [CEReactions] undefined show();
   [CEReactions] undefined showModal();
   [CEReactions] undefined close(optional DOMString returnValue);
+  [CEReactions] undefined requestClose(optional DOMString returnValue);
 };
 
 [Exposed=Window]
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/mediacapture-surface-control.idl b/third_party/blink/web_tests/external/wpt/interfaces/mediacapture-surface-control.idl
index b0bbd22..964f662d 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/mediacapture-surface-control.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/mediacapture-surface-control.idl
@@ -12,5 +12,5 @@
 
 partial interface CaptureController {
   constructor();
-  Promise<undefined> forwardWheel(HTMLElement element);
+  Promise<undefined> forwardWheel(HTMLElement? element);
 };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/permissions-policy.idl b/third_party/blink/web_tests/external/wpt/interfaces/permissions-policy.idl
index 5878d8d1..806d2eb 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/permissions-policy.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/permissions-policy.idl
@@ -27,4 +27,5 @@
   readonly attribute long? lineNumber;
   readonly attribute long? columnNumber;
   readonly attribute DOMString disposition;
+  readonly attribute DOMString? allowAttribute;
 };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/shared-storage.idl b/third_party/blink/web_tests/external/wpt/interfaces/shared-storage.idl
index b6863eff..6f38d67 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/shared-storage.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/shared-storage.idl
@@ -39,14 +39,48 @@
 };
 
 [Exposed=(Window,SharedStorageWorklet)]
+interface SharedStorageModifierMethod {};
+
+[Exposed=(Window, SharedStorageWorklet)]
+interface SharedStorageSetMethod : SharedStorageModifierMethod {
+  constructor(DOMString key, DOMString value, optional SharedStorageSetMethodOptions options = {});
+};
+
+[Exposed=(Window, SharedStorageWorklet)]
+interface SharedStorageAppendMethod : SharedStorageModifierMethod {
+  constructor(DOMString key, DOMString value, optional SharedStorageModifierMethodOptions options = {});
+};
+
+[Exposed=(Window, SharedStorageWorklet)]
+interface SharedStorageDeleteMethod : SharedStorageModifierMethod {
+  constructor(DOMString key, optional SharedStorageModifierMethodOptions options = {});
+};
+
+[Exposed=(Window, SharedStorageWorklet)]
+interface SharedStorageClearMethod : SharedStorageModifierMethod {
+  constructor(optional SharedStorageModifierMethodOptions options = {});
+};
+
+dictionary SharedStorageModifierMethodOptions {
+  DOMString withLock;
+};
+
+dictionary SharedStorageSetMethodOptions : SharedStorageModifierMethodOptions {
+  boolean ignoreIfPresent;
+};
+
+[Exposed=(Window,SharedStorageWorklet)]
 interface SharedStorage {
   Promise<any> set(DOMString key,
                    DOMString value,
                    optional SharedStorageSetMethodOptions options = {});
   Promise<any> append(DOMString key,
-                      DOMString value);
-  Promise<any> delete(DOMString key);
-  Promise<any> clear();
+                      DOMString value,
+                      optional SharedStorageModifierMethodOptions options = {});
+  Promise<any> delete(DOMString key, optional SharedStorageModifierMethodOptions options = {});
+  Promise<any> clear(optional SharedStorageModifierMethodOptions options = {});
+  Promise<any> batchUpdate(sequence<SharedStorageModifierMethod> methods,
+                           optional SharedStorageModifierMethodOptions options = {});
 
   [Exposed=Window]
   Promise<SharedStorageResponse> selectURL(DOMString name,
@@ -76,10 +110,6 @@
   async iterable<DOMString, DOMString>;
 };
 
-dictionary SharedStorageSetMethodOptions {
-  boolean ignoreIfPresent = false;
-};
-
 dictionary SharedStoragePrivateAggregationConfig {
   USVString aggregationCoordinatorOrigin;
   USVString contextId;
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webnn.idl b/third_party/blink/web_tests/external/wpt/interfaces/webnn.idl
index 132280b..3e1d9a9f4 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webnn.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webnn.idl
@@ -34,6 +34,10 @@
 
 typedef record<USVString, MLTensor> MLNamedTensors;
 
+dictionary MLContextLostInfo {
+  DOMString message;
+};
+
 [SecureContext, Exposed=(Window, DedicatedWorker)]
 interface MLContext {
   undefined dispatch(MLGraph graph, MLNamedTensors inputs, MLNamedTensors outputs);
@@ -46,6 +50,10 @@
   undefined writeTensor(MLTensor tensor, AllowSharedBufferSource inputData);
 
   MLOpSupportLimits opSupportLimits();
+
+  undefined destroy();
+
+  readonly attribute Promise<MLContextLostInfo> lost;
 };
 
 dictionary MLOpSupportLimits {
@@ -71,7 +79,9 @@
 };
 
 [SecureContext, Exposed=(Window, DedicatedWorker)]
-interface MLGraph {};
+interface MLGraph {
+  undefined destroy();
+};
 
 enum MLInputOperandLayout {
   "nchw",
diff --git a/third_party/blink/web_tests/external/wpt/permissions-policy/idlharness.window-expected.txt b/third_party/blink/web_tests/external/wpt/permissions-policy/idlharness.window-expected.txt
index f901356..a49f0943 100644
--- a/third_party/blink/web_tests/external/wpt/permissions-policy/idlharness.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/permissions-policy/idlharness.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 34 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 35 FAIL, 0 TIMEOUT, 0 NOTRUN.
 [FAIL] PermissionsPolicy interface: existence and properties of interface object
   assert_own_property: self does not have own property "PermissionsPolicy" expected property "PermissionsPolicy" missing
 [FAIL] PermissionsPolicy interface object length
@@ -60,6 +60,8 @@
   assert_own_property: self does not have own property "PermissionsPolicyViolationReportBody" expected property "PermissionsPolicyViolationReportBody" missing
 [FAIL] PermissionsPolicyViolationReportBody interface: attribute disposition
   assert_own_property: self does not have own property "PermissionsPolicyViolationReportBody" expected property "PermissionsPolicyViolationReportBody" missing
+[FAIL] PermissionsPolicyViolationReportBody interface: attribute allowAttribute
+  assert_own_property: self does not have own property "PermissionsPolicyViolationReportBody" expected property "PermissionsPolicyViolationReportBody" missing
 [FAIL] HTMLIFrameElement interface: attribute permissionsPolicy
   assert_true: The prototype object must have a property "permissionsPolicy" expected true got false
 [FAIL] HTMLIFrameElement interface: document.createElement("iframe") must inherit property "permissionsPolicy" with the proper type
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/about-blank-replacement.https-expected.txt b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/about-blank-replacement.https-expected.txt
index 329f457..dff7120 100644
--- a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/about-blank-replacement.https-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/about-blank-replacement.https-expected.txt
@@ -7,8 +7,6 @@
   assert_false: result: failure: could not find about:blank client expected false got true
 [FAIL] Simple about:blank is controlled and is exposed to clients.matchAll().
   promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'scriptURL')"
-[FAIL] Nested about:srcdoc is controlled and is exposed to clients.matchAll().
-  promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'scriptURL')"
 [FAIL] Dynamic about:blank is controlled and is exposed to clients.matchAll().
   promise_test: Unhandled rejection with value: object "TypeError: Cannot read properties of null (reading 'scriptURL')"
 Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/srcdoc-iframe-worker.js b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/srcdoc-iframe-worker.js
new file mode 100644
index 0000000..548116c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/srcdoc-iframe-worker.js
@@ -0,0 +1,11 @@
+self.addEventListener('message', event => {
+  event.source.postMessage('passed');
+});
+
+self.addEventListener('fetch', event => {
+  let url = new URL(event.request.url);
+  if (!url.searchParams.get('test_resource')) {
+    return;
+  }
+  event.respondWith(new Response('passed'));
+});
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/srcdoc-iframe.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/srcdoc-iframe.html
new file mode 100644
index 0000000..6407845
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/resources/srcdoc-iframe.html
@@ -0,0 +1,92 @@
+<!doctype html>
+<html>
+<body>
+<iframe id="srcdocFrame" srcdoc="
+<script>
+  function reportTestResult(result) {
+    top.postMessage({ type: 'TEST_RESULT', result: result }, '*');
+  }
+
+  async function postMessageToController() {
+    let controller = navigator.serviceWorker.controller;
+    if (!controller) {
+      reportTestResult('no navigator.serviceWorker.controller');
+    }
+    try {
+      controller.postMessage('test');
+    } catch (e) {
+      reportTestResult('Unexpected Error ' + e.name + ' : ' + e.message);
+    }
+  }
+
+  async function getServiceWorkerRegistration(scope) {
+    try {
+      let reg = await navigator.serviceWorker.getRegistration(scope);
+      if (!reg) {
+        reportTestResult('no regsitration for ' + scope);
+      }
+      if (reg.scope !==  scope) {
+        reportTestResult('regsitration scope does not match');
+      }
+
+      if (!reg.active) {
+        reportTestResult('regsitration.active is not valid' + reg.active);
+      }
+      reportTestResult('passed');
+    } catch (e) {
+      reportTestResult('Unexpected Error ' + e.name + ' : ' + e.message);
+    }
+  }
+
+  function addSrcdocIframeWithSandbox(sandbox) {
+    let frame = document.createElement('iframe');
+    frame.sandbox = sandbox;
+    frame.srcdoc = `
+    <script>
+      function reportTestResult(result) {
+        top.postMessage({ type: 'TEST_RESULT', result: result }, '*');
+      }
+
+      window.onload = function onLoad() {
+        try {
+          let controller = navigator.serviceWorker.controller;
+          reportTestResult(controller ? 'HasController' : 'NoController');
+        } catch (e) {
+          reportTestResult((e.name == 'SecurityError') ?
+              'NoController' : 'UnexpectedError:' + e.message);
+        }
+      }
+    <\/script>`;
+    document.body.appendChild(frame);
+  }
+
+  function addSandboxedSrcdocFrame() {
+    addSrcdocIframeWithSandbox('allow-scripts');
+  }
+
+  function addSameOriginSandboxedSrcdocFrame() {
+    addSrcdocIframeWithSandbox('allow-scripts allow-same-origin');
+  }
+
+  async function fetchResource() {
+    let response = await fetch('?test_resource=1');
+    reportTestResult(await response.text());
+  }
+
+  window.navigator.serviceWorker.addEventListener(
+      'message', function onMsg(evt) {
+    // Forward the message as test result.
+    reportTestResult(evt.data);
+  });
+</script>
+"></iframe>
+
+<script>
+// Helper routine to make it slightly easier for our parent to find
+// the srcdoc frame.
+function srcdocFrame() {
+  return document.getElementById('srcdocFrame').contentWindow;
+}
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/service-workers/service-worker/srcdoc-iframe.https.html b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/srcdoc-iframe.https.html
new file mode 100644
index 0000000..9c1c88e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/service-workers/service-worker/srcdoc-iframe.https.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<title>Service Worker: srcdoc frame handling</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<body>
+<script>
+
+// This test attempts to verify various service worker behaviors in
+// srcdoc iframe.
+
+function runTestInSrcDocFrame(testFunc, testFuncParam) {
+  return new Promise((resolve, reject) => {
+    window.addEventListener('message', function onMsg(evt) {
+      if (evt.data.type !== 'TEST_RESULT') {
+        return;
+      }
+      window.removeEventListener('message', onMsg);
+      resolve(evt.data.result);
+    });
+    testFunc(testFuncParam);
+  });
+}
+
+async function doAsyncTest(t, testFuncName, expectedResult) {
+  const worker = 'resources/srcdoc-iframe-worker.js';
+  const scope = 'resources/srcdoc-iframe.html';
+
+  let reg = await service_worker_unregister_and_register(t, worker, scope);
+
+  t.add_cleanup(() => service_worker_unregister(t, scope));
+
+  await wait_for_state(t, reg.installing, 'activated');
+
+  let frame = await with_iframe(scope);
+  let scopeFullUlr = frame.contentWindow.location.href;
+
+  let testFunc = frame.contentWindow.srcdocFrame()[testFuncName];
+  let testResult = await runTestInSrcDocFrame(testFunc, scopeFullUlr);
+  assert_equals(testResult, expectedResult);
+
+  frame.remove();
+}
+
+promise_test(async function(t) {
+  await doAsyncTest(t, 'addSandboxedSrcdocFrame', 'NoController');
+}, 'nested sandboxed srcdoc frame should not inherit controller');
+
+promise_test(async function(t) {
+  await doAsyncTest(t, 'addSameOriginSandboxedSrcdocFrame', 'HasController');
+}, 'nested same origin sandboxed srcdoc frame should inherit controller');
+
+promise_test(async function(t) {
+  await doAsyncTest(t, 'fetchResource', 'passed');
+}, 'should be able to serve resource from controller for srcdoc frame');
+
+promise_test(async function(t) {
+  await doAsyncTest(t, 'getServiceWorkerRegistration', 'passed');
+}, 'getRegistration should work in srcdoc iframe');
+
+promise_test(async function(t) {
+  await doAsyncTest(t, 'postMessageToController', 'passed');
+}, 'controller.postMessage should work in srcdoc iframe');
+
+</script>
+</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/urlpattern/resources/urlpatterntestdata.json b/third_party/blink/web_tests/external/wpt/urlpattern/resources/urlpatterntestdata.json
index 058079b..1d2ba25 100644
--- a/third_party/blink/web_tests/external/wpt/urlpattern/resources/urlpatterntestdata.json
+++ b/third_party/blink/web_tests/external/wpt/urlpattern/resources/urlpatterntestdata.json
@@ -1122,6 +1122,63 @@
     }
   },
   {
+    "pattern": ["http://\uD83D\uDEB2.com/"],
+    "inputs": ["http://\uD83D\uDEB2.com/"],
+    "exactly_empty_components": [ "port" ],
+    "expected_obj": {
+      "protocol": "http",
+      "hostname": "xn--h78h.com",
+      "pathname": "/"
+    },
+    "expected_match": {
+      "protocol": { "input": "http", "groups": {}},
+      "hostname": { "input": "xn--h78h.com", "groups": {}},
+      "pathname": { "input": "/", "groups": {}}
+    }
+  },
+  {
+    "pattern": ["http://\uD83D \uDEB2"],
+    "expected_obj": "error"
+  },
+  {
+    "pattern": [{"hostname":"\uD83D \uDEB2"}],
+    "expected_obj": "error"
+  },
+  {
+    "pattern": [{"pathname":"\uD83D \uDEB2"}],
+    "inputs": [],
+    "expected_obj": {
+      "pathname": "%EF%BF%BD%20%EF%BF%BD"
+    },
+    "expected_match": null
+  },
+  {
+    "pattern": [{"pathname":":\uD83D \uDEB2"}],
+    "expected_obj": "error"
+  },
+  {
+    "pattern": [{"pathname":":a\uDB40\uDD00b"}],
+    "inputs": [],
+    "expected_obj": {
+      "pathname": ":a\uDB40\uDD00b"
+    },
+    "expected_match": null
+  },
+  {
+    "pattern": [{"pathname":"test/:a\uD801\uDC50b"}],
+    "inputs": [{"pathname":"test/foo"}],
+    "expected_obj": {
+      "pathname": "test/:a\uD801\uDC50b"
+    },
+    "expected_match": {
+      "pathname": { "input": "test/foo", "groups": { "a\uD801\uDC50b": "foo" }}
+    }
+  },
+  {
+    "pattern": [{"pathname":":\uD83D\uDEB2"}],
+    "expected_obj": "error"
+  },
+  {
     "pattern": [{ "port": "" }],
     "inputs": [{ "protocol": "http", "port": "80" }],
     "exactly_empty_components": [ "port" ],
diff --git a/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/RTCRtpScriptTransform-encoded-transform.https.html b/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/RTCRtpScriptTransform-encoded-transform.https.html
index c935644..9eb7020a 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/RTCRtpScriptTransform-encoded-transform.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/RTCRtpScriptTransform-encoded-transform.https.html
@@ -46,8 +46,6 @@
     assert_equals(await updatePromise, "got expected");
 
     await playingPromise;
-    assert_equals(video.videoTracks.length, 1);
-    assert_equals(video.audioTracks.length, 0);
 
 }, "Receiver and sender read, modifiy and write video frames.");
 
@@ -83,8 +81,6 @@
     assert_equals(await updatePromise, "got expected");
 
     await playingPromise;
-    assert_equals(video.videoTracks.length, 0);
-    assert_equals(video.audioTracks.length, 1);
 
 }, "Receiver and sender read, modifiy and write audio frames.");
 
diff --git a/third_party/blink/web_tests/fast/forms/select/customizable-select/allowed-select-descendants-console-message-expected.txt b/third_party/blink/web_tests/fast/forms/select/customizable-select/allowed-select-descendants-console-message-expected.txt
index 6b6aa1fd..bd3d4734 100644
--- a/third_party/blink/web_tests/fast/forms/select/customizable-select/allowed-select-descendants-console-message-expected.txt
+++ b/third_party/blink/web_tests/fast/forms/select/customizable-select/allowed-select-descendants-console-message-expected.txt
@@ -11,7 +11,19 @@
  
 ..
 ..
-           
+  
+..
+..
+ 
+..
+..
+  
+..
+..
+ 
+..
+..
+     
 ..
  
 ..
@@ -19,9 +31,17 @@
 1
 2
  
+..
+..
  
+..
+..
  
+..
+..
  
+..
+..
  
  
  
@@ -31,7 +51,9 @@
 One
 Two
  
-  
+ 
+some text
+ 
 ..
  
 ..
diff --git a/third_party/blink/web_tests/fast/forms/select/customizable-select/disallowed-select-descendants-console-message-expected.txt b/third_party/blink/web_tests/fast/forms/select/customizable-select/disallowed-select-descendants-console-message-expected.txt
index befdf3c..736fa94 100644
--- a/third_party/blink/web_tests/fast/forms/select/customizable-select/disallowed-select-descendants-console-message-expected.txt
+++ b/third_party/blink/web_tests/fast/forms/select/customizable-select/disallowed-select-descendants-console-message-expected.txt
@@ -24,5 +24,12 @@
  
  
 ..
-   
-  
+ 
+.. ..
+ 
+.. ..
+ 
+ 
+.. ..
+ 
+.. ..
diff --git a/third_party/blink/web_tests/http/tests/devtools/profiler/heap-snapshot-summary-show-ranges-expected.txt b/third_party/blink/web_tests/http/tests/devtools/profiler/heap-snapshot-summary-show-ranges-expected.txt
index 90b80b7..0c2aa15e 100644
--- a/third_party/blink/web_tests/http/tests/devtools/profiler/heap-snapshot-summary-show-ranges-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/profiler/heap-snapshot-summary-show-ranges-expected.txt
@@ -8,235 +8,235 @@
 
 Step 3
 Retrieved ranges: [{"from":0,"to":10}]
-[0] A 773492  492  
-[1] A 213482  482  
-[2] A 613482  482  
-[3] A 853472  472  
-[4] A 1813462  462  
-[5] A 413452  452  
-[6] A 1413452  452  
-[7] A 1853442  442  
-[8] A 573432  432  
-[9] A 533412  412  
+[0] A 77349  2  49  2  
+[1] A 21348  2  48  2  
+[2] A 61348  2  48  2  
+[3] A 85347  2  47  2  
+[4] A 181346  2  46  2  
+[5] A 41345  2  45  2  
+[6] A 141345  2  45  2  
+[7] A 185344  2  44  2  
+[8] A 57343  2  43  2  
+[9] A 53341  2  41  2  
 [10] Show 10 beforeShow all 40Show 10 after
 
 Step 4
 Retrieved ranges: [{"from":0,"to":10},{"from":30,"to":40}]
-[0] A 773492  492  
-[1] A 213482  482  
-[2] A 613482  482  
-[3] A 853472  472  
-[4] A 1813462  462  
-[5] A 413452  452  
-[6] A 1413452  452  
-[7] A 1853442  442  
-[8] A 573432  432  
-[9] A 533412  412  
+[0] A 77349  2  49  2  
+[1] A 21348  2  48  2  
+[2] A 61348  2  48  2  
+[3] A 85347  2  47  2  
+[4] A 181346  2  46  2  
+[5] A 41345  2  45  2  
+[6] A 141345  2  45  2  
+[7] A 185344  2  44  2  
+[8] A 57343  2  43  2  
+[9] A 53341  2  41  2  
 [10] Show 10 beforeShow all 20Show 10 after
-[11] A 1333241  241  
-[12] A 173231  231  
-[13] A 1453231  231  
-[14] A 1773211  211  
-[15] A 1973211  211  
-[16] A 1893181  181  
-[17] A 253161  161  
-[18] A 1093151  151  
-[19] A 2013151  151  
-[20] A 1933131  131  
+[11] A 133324  1  24  1  
+[12] A 17323  1  23  1  
+[13] A 145323  1  23  1  
+[14] A 177321  1  21  1  
+[15] A 197321  1  21  1  
+[16] A 189318  1  18  1  
+[17] A 25316  1  16  1  
+[18] A 109315  1  15  1  
+[19] A 201315  1  15  1  
+[20] A 193313  1  13  1  
 [21] Show 10 beforeShow all 10Show 10 after
 
 Step 5
 Retrieved ranges: [{"from":0,"to":10},{"from":20,"to":25},{"from":30,"to":40}]
-[0] A 773492  492  
-[1] A 213482  482  
-[2] A 613482  482  
-[3] A 853472  472  
-[4] A 1813462  462  
-[5] A 413452  452  
-[6] A 1413452  452  
-[7] A 1853442  442  
-[8] A 573432  432  
-[9] A 533412  412  
+[0] A 77349  2  49  2  
+[1] A 21348  2  48  2  
+[2] A 61348  2  48  2  
+[3] A 85347  2  47  2  
+[4] A 181346  2  46  2  
+[5] A 41345  2  45  2  
+[6] A 141345  2  45  2  
+[7] A 185344  2  44  2  
+[8] A 57343  2  43  2  
+[9] A 53341  2  41  2  
 [10] Show 10 beforeShow all 10Show 10 after
-[11] A 1293342  342  
-[12] A 813312  312  
-[13] A 1573302  302  
-[14] A 1373291  291  
-[15] A 1693291  291  
+[11] A 129334  2  34  2  
+[12] A 81331  2  31  2  
+[13] A 157330  2  30  2  
+[14] A 137329  1  29  1  
+[15] A 169329  1  29  1  
 [16] Show 10 beforeShow all 5Show 10 after
-[17] A 1333241  241  
-[18] A 173231  231  
-[19] A 1453231  231  
-[20] A 1773211  211  
-[21] A 1973211  211  
-[22] A 1893181  181  
-[23] A 253161  161  
-[24] A 1093151  151  
-[25] A 2013151  151  
-[26] A 1933131  131  
+[17] A 133324  1  24  1  
+[18] A 17323  1  23  1  
+[19] A 145323  1  23  1  
+[20] A 177321  1  21  1  
+[21] A 197321  1  21  1  
+[22] A 189318  1  18  1  
+[23] A 25316  1  16  1  
+[24] A 109315  1  15  1  
+[25] A 201315  1  15  1  
+[26] A 193313  1  13  1  
 [27] Show 10 beforeShow all 10Show 10 after
 
 Step 6
 Retrieved ranges: [{"from":0,"to":10},{"from":20,"to":25},{"from":28,"to":40}]
-[0] A 773492  492  
-[1] A 213482  482  
-[2] A 613482  482  
-[3] A 853472  472  
-[4] A 1813462  462  
-[5] A 413452  452  
-[6] A 1413452  452  
-[7] A 1853442  442  
-[8] A 573432  432  
-[9] A 533412  412  
+[0] A 77349  2  49  2  
+[1] A 21348  2  48  2  
+[2] A 61348  2  48  2  
+[3] A 85347  2  47  2  
+[4] A 181346  2  46  2  
+[5] A 41345  2  45  2  
+[6] A 141345  2  45  2  
+[7] A 185344  2  44  2  
+[8] A 57343  2  43  2  
+[9] A 53341  2  41  2  
 [10] Show 10 beforeShow all 10Show 10 after
-[11] A 1293342  342  
-[12] A 813312  312  
-[13] A 1573302  302  
-[14] A 1373291  291  
-[15] A 1693291  291  
+[11] A 129334  2  34  2  
+[12] A 81331  2  31  2  
+[13] A 157330  2  30  2  
+[14] A 137329  1  29  1  
+[15] A 169329  1  29  1  
 [16] Show 10 beforeShow all 3Show 10 after
-[17] A 453251  251  
-[18] A 333241  241  
-[19] A 1333241  241  
-[20] A 173231  231  
-[21] A 1453231  231  
-[22] A 1773211  211  
-[23] A 1973211  211  
-[24] A 1893181  181  
-[25] A 253161  161  
-[26] A 1093151  151  
-[27] A 2013151  151  
-[28] A 1933131  131  
+[17] A 45325  1  25  1  
+[18] A 33324  1  24  1  
+[19] A 133324  1  24  1  
+[20] A 17323  1  23  1  
+[21] A 145323  1  23  1  
+[22] A 177321  1  21  1  
+[23] A 197321  1  21  1  
+[24] A 189318  1  18  1  
+[25] A 25316  1  16  1  
+[26] A 109315  1  15  1  
+[27] A 201315  1  15  1  
+[28] A 193313  1  13  1  
 [29] Show 10 beforeShow all 10Show 10 after
 
 Step 7
 Retrieved ranges: [{"from":0,"to":10},{"from":18,"to":26},{"from":28,"to":40}]
-[0] A 773492  492  
-[1] A 213482  482  
-[2] A 613482  482  
-[3] A 853472  472  
-[4] A 1813462  462  
-[5] A 413452  452  
-[6] A 1413452  452  
-[7] A 1853442  442  
-[8] A 573432  432  
-[9] A 533412  412  
+[0] A 77349  2  49  2  
+[1] A 21348  2  48  2  
+[2] A 61348  2  48  2  
+[3] A 85347  2  47  2  
+[4] A 181346  2  46  2  
+[5] A 41345  2  45  2  
+[6] A 141345  2  45  2  
+[7] A 185344  2  44  2  
+[8] A 57343  2  43  2  
+[9] A 53341  2  41  2  
 [10] Show 10 beforeShow all 8Show 10 after
-[11] A 1253352  352  
-[12] A 1173342  342  
-[13] A 1293342  342  
-[14] A 813312  312  
-[15] A 1573302  302  
-[16] A 1373291  291  
-[17] A 1693291  291  
-[18] A 2053281  281  
+[11] A 125335  2  35  2  
+[12] A 117334  2  34  2  
+[13] A 129334  2  34  2  
+[14] A 81331  2  31  2  
+[15] A 157330  2  30  2  
+[16] A 137329  1  29  1  
+[17] A 169329  1  29  1  
+[18] A 205328  1  28  1  
 [19] Show 10 beforeShow all 2Show 10 after
-[20] A 453251  251  
-[21] A 333241  241  
-[22] A 1333241  241  
-[23] A 173231  231  
-[24] A 1453231  231  
-[25] A 1773211  211  
-[26] A 1973211  211  
-[27] A 1893181  181  
-[28] A 253161  161  
-[29] A 1093151  151  
-[30] A 2013151  151  
-[31] A 1933131  131  
+[20] A 45325  1  25  1  
+[21] A 33324  1  24  1  
+[22] A 133324  1  24  1  
+[23] A 17323  1  23  1  
+[24] A 145323  1  23  1  
+[25] A 177321  1  21  1  
+[26] A 197321  1  21  1  
+[27] A 189318  1  18  1  
+[28] A 25316  1  16  1  
+[29] A 109315  1  15  1  
+[30] A 201315  1  15  1  
+[31] A 193313  1  13  1  
 [32] Show 10 beforeShow all 10Show 10 after
 
 Step 8
 Retrieved ranges: [{"from":0,"to":10},{"from":15,"to":45}]
-[0] A 773492  492  
-[1] A 213482  482  
-[2] A 613482  482  
-[3] A 853472  472  
-[4] A 1813462  462  
-[5] A 413452  452  
-[6] A 1413452  452  
-[7] A 1853442  442  
-[8] A 573432  432  
-[9] A 533412  412  
+[0] A 77349  2  49  2  
+[1] A 21348  2  48  2  
+[2] A 61348  2  48  2  
+[3] A 85347  2  47  2  
+[4] A 181346  2  46  2  
+[5] A 41345  2  45  2  
+[6] A 141345  2  45  2  
+[7] A 185344  2  44  2  
+[8] A 57343  2  43  2  
+[9] A 53341  2  41  2  
 [10] Show 10 beforeShow all 5Show 10 after
-[11] A 653382  382  
-[12] A 1133382  382  
-[13] A 1533362  362  
-[14] A 1253352  352  
-[15] A 1173342  342  
-[16] A 1293342  342  
-[17] A 813312  312  
-[18] A 1573302  302  
-[19] A 1373291  291  
-[20] A 1693291  291  
-[21] A 2053281  281  
-[22] A 373271  271  
-[23] A 493261  261  
-[24] A 453251  251  
-[25] A 333241  241  
-[26] A 1333241  241  
-[27] A 173231  231  
-[28] A 1453231  231  
-[29] A 1773211  211  
-[30] A 1973211  211  
-[31] A 1893181  181  
-[32] A 253161  161  
-[33] A 1093151  151  
-[34] A 2013151  151  
-[35] A 1933131  131  
-[36] A 1493121  121  
-[37] A 693111  111  
-[38] A 29390  90  
-[39] A 121380  80  
-[40] A 165380  80  
+[11] A 65338  2  38  2  
+[12] A 113338  2  38  2  
+[13] A 153336  2  36  2  
+[14] A 125335  2  35  2  
+[15] A 117334  2  34  2  
+[16] A 129334  2  34  2  
+[17] A 81331  2  31  2  
+[18] A 157330  2  30  2  
+[19] A 137329  1  29  1  
+[20] A 169329  1  29  1  
+[21] A 205328  1  28  1  
+[22] A 37327  1  27  1  
+[23] A 49326  1  26  1  
+[24] A 45325  1  25  1  
+[25] A 33324  1  24  1  
+[26] A 133324  1  24  1  
+[27] A 17323  1  23  1  
+[28] A 145323  1  23  1  
+[29] A 177321  1  21  1  
+[30] A 197321  1  21  1  
+[31] A 189318  1  18  1  
+[32] A 25316  1  16  1  
+[33] A 109315  1  15  1  
+[34] A 201315  1  15  1  
+[35] A 193313  1  13  1  
+[36] A 149312  1  12  1  
+[37] A 69311  1  11  1  
+[38] A 2939  0  9  0  
+[39] A 12138  0  8  0  
+[40] A 16538  0  8  0  
 [41] Show 10 beforeShow all 5Show 10 after
 
 Step 9
 Retrieved ranges: [{"from":0,"to":45}]
-[0] A 773492  492  
-[1] A 213482  482  
-[2] A 613482  482  
-[3] A 853472  472  
-[4] A 1813462  462  
-[5] A 413452  452  
-[6] A 1413452  452  
-[7] A 1853442  442  
-[8] A 573432  432  
-[9] A 533412  412  
-[10] A 893402  402  
-[11] A 973402  402  
-[12] A 733392  392  
-[13] A 1013392  392  
-[14] A 133382  382  
-[15] A 653382  382  
-[16] A 1133382  382  
-[17] A 1533362  362  
-[18] A 1253352  352  
-[19] A 1173342  342  
-[20] A 1293342  342  
-[21] A 813312  312  
-[22] A 1573302  302  
-[23] A 1373291  291  
-[24] A 1693291  291  
-[25] A 2053281  281  
-[26] A 373271  271  
-[27] A 493261  261  
-[28] A 453251  251  
-[29] A 333241  241  
-[30] A 1333241  241  
-[31] A 173231  231  
-[32] A 1453231  231  
-[33] A 1773211  211  
-[34] A 1973211  211  
-[35] A 1893181  181  
-[36] A 253161  161  
-[37] A 1093151  151  
-[38] A 2013151  151  
-[39] A 1933131  131  
-[40] A 1493121  121  
-[41] A 693111  111  
-[42] A 29390  90  
-[43] A 121380  80  
-[44] A 165380  80  
+[0] A 77349  2  49  2  
+[1] A 21348  2  48  2  
+[2] A 61348  2  48  2  
+[3] A 85347  2  47  2  
+[4] A 181346  2  46  2  
+[5] A 41345  2  45  2  
+[6] A 141345  2  45  2  
+[7] A 185344  2  44  2  
+[8] A 57343  2  43  2  
+[9] A 53341  2  41  2  
+[10] A 89340  2  40  2  
+[11] A 97340  2  40  2  
+[12] A 73339  2  39  2  
+[13] A 101339  2  39  2  
+[14] A 13338  2  38  2  
+[15] A 65338  2  38  2  
+[16] A 113338  2  38  2  
+[17] A 153336  2  36  2  
+[18] A 125335  2  35  2  
+[19] A 117334  2  34  2  
+[20] A 129334  2  34  2  
+[21] A 81331  2  31  2  
+[22] A 157330  2  30  2  
+[23] A 137329  1  29  1  
+[24] A 169329  1  29  1  
+[25] A 205328  1  28  1  
+[26] A 37327  1  27  1  
+[27] A 49326  1  26  1  
+[28] A 45325  1  25  1  
+[29] A 33324  1  24  1  
+[30] A 133324  1  24  1  
+[31] A 17323  1  23  1  
+[32] A 145323  1  23  1  
+[33] A 177321  1  21  1  
+[34] A 197321  1  21  1  
+[35] A 189318  1  18  1  
+[36] A 25316  1  16  1  
+[37] A 109315  1  15  1  
+[38] A 201315  1  15  1  
+[39] A 193313  1  13  1  
+[40] A 149312  1  12  1  
+[41] A 69311  1  11  1  
+[42] A 2939  0  9  0  
+[43] A 12138  0  8  0  
+[44] A 16538  0  8  0  
 [45] Show 10 beforeShow all 5Show 10 after
 
 Profiler was disabled.
diff --git a/third_party/blink/web_tests/http/tests/devtools/profiler/heap-snapshot-summary-show-ranges.js b/third_party/blink/web_tests/http/tests/devtools/profiler/heap-snapshot-summary-show-ranges.js
index 73cf1cd..dc323cf 100644
--- a/third_party/blink/web_tests/http/tests/devtools/profiler/heap-snapshot-summary-show-ranges.js
+++ b/third_party/blink/web_tests/http/tests/devtools/profiler/heap-snapshot-summary-show-ranges.js
@@ -23,7 +23,8 @@
       TestRunner.addResult(step);
       TestRunner.addResult('Retrieved ranges: ' + JSON.stringify(row.retrievedChildrenRanges));
       for (var i = 0; i < row.children.length; ++i)
-        TestRunner.addResult('[' + i + '] ' + row.children[i].element().textContent.replace(/[^\w\d]/mg, ' '));
+        TestRunner.addResult('[' + i + '] ' + row.children[i].element().textContent
+          .replace(/[@%B]|\s+/mg, ' '));
       return row.populateChildren(from, to);
     }
 
diff --git a/third_party/blink/web_tests/platform/webview/external/wpt/bluetooth/idl/idlharness.tentative.https.window-expected.txt b/third_party/blink/web_tests/platform/webview/external/wpt/bluetooth/idl/idlharness.tentative.https.window-expected.txt
deleted file mode 100644
index 26bf149b..0000000
--- a/third_party/blink/web_tests/platform/webview/external/wpt/bluetooth/idl/idlharness.tentative.https.window-expected.txt
+++ /dev/null
@@ -1,323 +0,0 @@
-This is a testharness.js-based test.
-Found 160 FAIL, 0 TIMEOUT, 0 NOTRUN.
-[FAIL] Bluetooth interface: existence and properties of interface object
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface object length
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface object name
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface: operation getAvailability()
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface: attribute onavailabilitychanged
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface: attribute referringDevice
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface: operation getDevices()
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface: operation requestDevice(optional RequestDeviceOptions)
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface: attribute onadvertisementreceived
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface: attribute ongattserverdisconnected
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface: attribute oncharacteristicvaluechanged
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface: attribute onserviceadded
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface: attribute onservicechanged
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth interface: attribute onserviceremoved
-  assert_own_property: self does not have own property "Bluetooth" expected property "Bluetooth" missing
-[FAIL] Bluetooth must be primary interface of navigator.bluetooth
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Stringification of navigator.bluetooth
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Bluetooth interface: navigator.bluetooth must inherit property "getAvailability()" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Bluetooth interface: navigator.bluetooth must inherit property "onavailabilitychanged" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Bluetooth interface: navigator.bluetooth must inherit property "referringDevice" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Bluetooth interface: navigator.bluetooth must inherit property "getDevices()" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Bluetooth interface: navigator.bluetooth must inherit property "requestDevice(optional RequestDeviceOptions)" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Bluetooth interface: calling requestDevice(optional RequestDeviceOptions) on navigator.bluetooth with too few arguments must throw TypeError
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Bluetooth interface: navigator.bluetooth must inherit property "onadvertisementreceived" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Bluetooth interface: navigator.bluetooth must inherit property "ongattserverdisconnected" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Bluetooth interface: navigator.bluetooth must inherit property "oncharacteristicvaluechanged" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Bluetooth interface: navigator.bluetooth must inherit property "onserviceadded" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Bluetooth interface: navigator.bluetooth must inherit property "onservicechanged" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Bluetooth interface: navigator.bluetooth must inherit property "onserviceremoved" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] BluetoothPermissionResult interface: existence and properties of interface object
-  assert_own_property: self does not have own property "BluetoothPermissionResult" expected property "BluetoothPermissionResult" missing
-[FAIL] BluetoothPermissionResult interface object length
-  assert_own_property: self does not have own property "BluetoothPermissionResult" expected property "BluetoothPermissionResult" missing
-[FAIL] BluetoothPermissionResult interface object name
-  assert_own_property: self does not have own property "BluetoothPermissionResult" expected property "BluetoothPermissionResult" missing
-[FAIL] BluetoothPermissionResult interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "BluetoothPermissionResult" expected property "BluetoothPermissionResult" missing
-[FAIL] BluetoothPermissionResult interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "BluetoothPermissionResult" expected property "BluetoothPermissionResult" missing
-[FAIL] BluetoothPermissionResult interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "BluetoothPermissionResult" expected property "BluetoothPermissionResult" missing
-[FAIL] BluetoothPermissionResult interface: attribute devices
-  assert_own_property: self does not have own property "BluetoothPermissionResult" expected property "BluetoothPermissionResult" missing
-[FAIL] ValueEvent interface: existence and properties of interface object
-  assert_own_property: self does not have own property "ValueEvent" expected property "ValueEvent" missing
-[FAIL] ValueEvent interface object length
-  assert_own_property: self does not have own property "ValueEvent" expected property "ValueEvent" missing
-[FAIL] ValueEvent interface object name
-  assert_own_property: self does not have own property "ValueEvent" expected property "ValueEvent" missing
-[FAIL] ValueEvent interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "ValueEvent" expected property "ValueEvent" missing
-[FAIL] ValueEvent interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "ValueEvent" expected property "ValueEvent" missing
-[FAIL] ValueEvent interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "ValueEvent" expected property "ValueEvent" missing
-[FAIL] ValueEvent interface: attribute value
-  assert_own_property: self does not have own property "ValueEvent" expected property "ValueEvent" missing
-[FAIL] BluetoothDevice interface: existence and properties of interface object
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface object length
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface object name
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: attribute id
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: attribute name
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: attribute gatt
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: operation forget()
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: operation watchAdvertisements(optional WatchAdvertisementsOptions)
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: attribute watchingAdvertisements
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: attribute onadvertisementreceived
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: attribute ongattserverdisconnected
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: attribute oncharacteristicvaluechanged
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: attribute onserviceadded
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: attribute onservicechanged
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothDevice interface: attribute onserviceremoved
-  assert_own_property: self does not have own property "BluetoothDevice" expected property "BluetoothDevice" missing
-[FAIL] BluetoothAdvertisingEvent interface object length
-  assert_equals: wrong value for BluetoothAdvertisingEvent.length expected 2 but got 0
-[FAIL] BluetoothAdvertisingEvent must be primary interface of event
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] Stringification of event
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] BluetoothAdvertisingEvent interface: event must inherit property "device" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] BluetoothAdvertisingEvent interface: event must inherit property "uuids" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] BluetoothAdvertisingEvent interface: event must inherit property "name" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] BluetoothAdvertisingEvent interface: event must inherit property "appearance" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] BluetoothAdvertisingEvent interface: event must inherit property "txPower" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] BluetoothAdvertisingEvent interface: event must inherit property "rssi" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] BluetoothAdvertisingEvent interface: event must inherit property "manufacturerData" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] BluetoothAdvertisingEvent interface: event must inherit property "serviceData" with the proper type
-  assert_equals: wrong typeof object expected "object" but got "undefined"
-[FAIL] BluetoothRemoteGATTServer interface: existence and properties of interface object
-  assert_own_property: self does not have own property "BluetoothRemoteGATTServer" expected property "BluetoothRemoteGATTServer" missing
-[FAIL] BluetoothRemoteGATTServer interface object length
-  assert_own_property: self does not have own property "BluetoothRemoteGATTServer" expected property "BluetoothRemoteGATTServer" missing
-[FAIL] BluetoothRemoteGATTServer interface object name
-  assert_own_property: self does not have own property "BluetoothRemoteGATTServer" expected property "BluetoothRemoteGATTServer" missing
-[FAIL] BluetoothRemoteGATTServer interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "BluetoothRemoteGATTServer" expected property "BluetoothRemoteGATTServer" missing
-[FAIL] BluetoothRemoteGATTServer interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "BluetoothRemoteGATTServer" expected property "BluetoothRemoteGATTServer" missing
-[FAIL] BluetoothRemoteGATTServer interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "BluetoothRemoteGATTServer" expected property "BluetoothRemoteGATTServer" missing
-[FAIL] BluetoothRemoteGATTServer interface: attribute device
-  assert_own_property: self does not have own property "BluetoothRemoteGATTServer" expected property "BluetoothRemoteGATTServer" missing
-[FAIL] BluetoothRemoteGATTServer interface: attribute connected
-  assert_own_property: self does not have own property "BluetoothRemoteGATTServer" expected property "BluetoothRemoteGATTServer" missing
-[FAIL] BluetoothRemoteGATTServer interface: operation connect()
-  assert_own_property: self does not have own property "BluetoothRemoteGATTServer" expected property "BluetoothRemoteGATTServer" missing
-[FAIL] BluetoothRemoteGATTServer interface: operation disconnect()
-  assert_own_property: self does not have own property "BluetoothRemoteGATTServer" expected property "BluetoothRemoteGATTServer" missing
-[FAIL] BluetoothRemoteGATTServer interface: operation getPrimaryService(BluetoothServiceUUID)
-  assert_own_property: self does not have own property "BluetoothRemoteGATTServer" expected property "BluetoothRemoteGATTServer" missing
-[FAIL] BluetoothRemoteGATTServer interface: operation getPrimaryServices(optional BluetoothServiceUUID)
-  assert_own_property: self does not have own property "BluetoothRemoteGATTServer" expected property "BluetoothRemoteGATTServer" missing
-[FAIL] BluetoothRemoteGATTService interface: existence and properties of interface object
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface object length
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface object name
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface: attribute device
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface: attribute uuid
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface: attribute isPrimary
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface: operation getCharacteristic(BluetoothCharacteristicUUID)
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface: operation getCharacteristics(optional BluetoothCharacteristicUUID)
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface: operation getIncludedService(BluetoothServiceUUID)
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface: operation getIncludedServices(optional BluetoothServiceUUID)
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface: attribute oncharacteristicvaluechanged
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface: attribute onserviceadded
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface: attribute onservicechanged
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTService interface: attribute onserviceremoved
-  assert_own_property: self does not have own property "BluetoothRemoteGATTService" expected property "BluetoothRemoteGATTService" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: existence and properties of interface object
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface object length
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface object name
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: attribute service
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: attribute uuid
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: attribute properties
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: attribute value
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: operation getDescriptor(BluetoothDescriptorUUID)
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: operation getDescriptors(optional BluetoothDescriptorUUID)
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: operation readValue()
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: operation writeValue(BufferSource)
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: operation writeValueWithResponse(BufferSource)
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: operation writeValueWithoutResponse(BufferSource)
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: operation startNotifications()
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: operation stopNotifications()
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothRemoteGATTCharacteristic interface: attribute oncharacteristicvaluechanged
-  assert_own_property: self does not have own property "BluetoothRemoteGATTCharacteristic" expected property "BluetoothRemoteGATTCharacteristic" missing
-[FAIL] BluetoothCharacteristicProperties interface: existence and properties of interface object
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothCharacteristicProperties interface object length
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothCharacteristicProperties interface object name
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothCharacteristicProperties interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothCharacteristicProperties interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothCharacteristicProperties interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothCharacteristicProperties interface: attribute broadcast
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothCharacteristicProperties interface: attribute read
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothCharacteristicProperties interface: attribute writeWithoutResponse
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothCharacteristicProperties interface: attribute write
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothCharacteristicProperties interface: attribute notify
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothCharacteristicProperties interface: attribute indicate
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothCharacteristicProperties interface: attribute authenticatedSignedWrites
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothCharacteristicProperties interface: attribute reliableWrite
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothCharacteristicProperties interface: attribute writableAuxiliaries
-  assert_own_property: self does not have own property "BluetoothCharacteristicProperties" expected property "BluetoothCharacteristicProperties" missing
-[FAIL] BluetoothRemoteGATTDescriptor interface: existence and properties of interface object
-  assert_own_property: self does not have own property "BluetoothRemoteGATTDescriptor" expected property "BluetoothRemoteGATTDescriptor" missing
-[FAIL] BluetoothRemoteGATTDescriptor interface object length
-  assert_own_property: self does not have own property "BluetoothRemoteGATTDescriptor" expected property "BluetoothRemoteGATTDescriptor" missing
-[FAIL] BluetoothRemoteGATTDescriptor interface object name
-  assert_own_property: self does not have own property "BluetoothRemoteGATTDescriptor" expected property "BluetoothRemoteGATTDescriptor" missing
-[FAIL] BluetoothRemoteGATTDescriptor interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "BluetoothRemoteGATTDescriptor" expected property "BluetoothRemoteGATTDescriptor" missing
-[FAIL] BluetoothRemoteGATTDescriptor interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "BluetoothRemoteGATTDescriptor" expected property "BluetoothRemoteGATTDescriptor" missing
-[FAIL] BluetoothRemoteGATTDescriptor interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "BluetoothRemoteGATTDescriptor" expected property "BluetoothRemoteGATTDescriptor" missing
-[FAIL] BluetoothRemoteGATTDescriptor interface: attribute characteristic
-  assert_own_property: self does not have own property "BluetoothRemoteGATTDescriptor" expected property "BluetoothRemoteGATTDescriptor" missing
-[FAIL] BluetoothRemoteGATTDescriptor interface: attribute uuid
-  assert_own_property: self does not have own property "BluetoothRemoteGATTDescriptor" expected property "BluetoothRemoteGATTDescriptor" missing
-[FAIL] BluetoothRemoteGATTDescriptor interface: attribute value
-  assert_own_property: self does not have own property "BluetoothRemoteGATTDescriptor" expected property "BluetoothRemoteGATTDescriptor" missing
-[FAIL] BluetoothRemoteGATTDescriptor interface: operation readValue()
-  assert_own_property: self does not have own property "BluetoothRemoteGATTDescriptor" expected property "BluetoothRemoteGATTDescriptor" missing
-[FAIL] BluetoothRemoteGATTDescriptor interface: operation writeValue(BufferSource)
-  assert_own_property: self does not have own property "BluetoothRemoteGATTDescriptor" expected property "BluetoothRemoteGATTDescriptor" missing
-[FAIL] BluetoothUUID interface: existence and properties of interface object
-  assert_own_property: self does not have own property "BluetoothUUID" expected property "BluetoothUUID" missing
-[FAIL] BluetoothUUID interface object length
-  assert_own_property: self does not have own property "BluetoothUUID" expected property "BluetoothUUID" missing
-[FAIL] BluetoothUUID interface object name
-  assert_own_property: self does not have own property "BluetoothUUID" expected property "BluetoothUUID" missing
-[FAIL] BluetoothUUID interface: existence and properties of interface prototype object
-  assert_own_property: self does not have own property "BluetoothUUID" expected property "BluetoothUUID" missing
-[FAIL] BluetoothUUID interface: existence and properties of interface prototype object's "constructor" property
-  assert_own_property: self does not have own property "BluetoothUUID" expected property "BluetoothUUID" missing
-[FAIL] BluetoothUUID interface: existence and properties of interface prototype object's @@unscopables property
-  assert_own_property: self does not have own property "BluetoothUUID" expected property "BluetoothUUID" missing
-[FAIL] BluetoothUUID interface: operation getService((DOMString or unsigned long))
-  assert_own_property: self does not have own property "BluetoothUUID" expected property "BluetoothUUID" missing
-[FAIL] BluetoothUUID interface: operation getCharacteristic((DOMString or unsigned long))
-  assert_own_property: self does not have own property "BluetoothUUID" expected property "BluetoothUUID" missing
-[FAIL] BluetoothUUID interface: operation getDescriptor((DOMString or unsigned long))
-  assert_own_property: self does not have own property "BluetoothUUID" expected property "BluetoothUUID" missing
-[FAIL] BluetoothUUID interface: operation canonicalUUID(unsigned long)
-  assert_own_property: self does not have own property "BluetoothUUID" expected property "BluetoothUUID" missing
-[FAIL] Navigator interface: attribute bluetooth
-  assert_true: The prototype object must have a property "bluetooth" expected true got false
-[FAIL] Navigator interface: navigator must inherit property "bluetooth" with the proper type
-  assert_inherits: property "bluetooth" not found in prototype chain
-Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/html/syntax/charset/without-inheritance-expected.png b/third_party/blink/web_tests/platform/win/external/wpt/html/syntax/charset/without-inheritance-expected.png
index fb04abe..9650133 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/html/syntax/charset/without-inheritance-expected.png
+++ b/third_party/blink/web_tests/platform/win/external/wpt/html/syntax/charset/without-inheritance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/external/wpt/html/syntax/charset/without-inheritance-expected.png b/third_party/blink/web_tests/platform/win11-arm64/external/wpt/html/syntax/charset/without-inheritance-expected.png
index fb04abe..9650133 100644
--- a/third_party/blink/web_tests/platform/win11-arm64/external/wpt/html/syntax/charset/without-inheritance-expected.png
+++ b/third_party/blink/web_tests/platform/win11-arm64/external/wpt/html/syntax/charset/without-inheritance-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/select-parser-relaxation/fast/forms/select/customizable-select/allowed-select-descendants-console-message-expected.txt b/third_party/blink/web_tests/virtual/select-parser-relaxation/fast/forms/select/customizable-select/allowed-select-descendants-console-message-expected.txt
new file mode 100644
index 0000000..bd3d4734
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/select-parser-relaxation/fast/forms/select/customizable-select/allowed-select-descendants-console-message-expected.txt
@@ -0,0 +1,64 @@
+CONSOLE MESSAGE: <script> element
+CONSOLE MESSAGE: <script> element
+CONSOLE MESSAGE: <script> element
+ 
+..
+ 
+..
+..
+ 
+..
+ 
+..
+..
+  
+..
+..
+ 
+..
+..
+  
+..
+..
+ 
+..
+..
+     
+..
+ 
+..
+..
+1
+2
+ 
+..
+..
+ 
+..
+..
+ 
+..
+..
+ 
+..
+..
+ 
+ 
+ 
+ 
+One
+ 
+One
+Two
+ 
+ 
+some text
+ 
+..
+ 
+..
+..
+ 
+..
+ 
+ 
diff --git a/third_party/blink/web_tests/virtual/select-parser-relaxation/fast/forms/select/customizable-select/disallowed-select-descendants-console-message-expected.txt b/third_party/blink/web_tests/virtual/select-parser-relaxation/fast/forms/select/customizable-select/disallowed-select-descendants-console-message-expected.txt
index c45eb99..f572451 100644
--- a/third_party/blink/web_tests/virtual/select-parser-relaxation/fast/forms/select/customizable-select/disallowed-select-descendants-console-message-expected.txt
+++ b/third_party/blink/web_tests/virtual/select-parser-relaxation/fast/forms/select/customizable-select/disallowed-select-descendants-console-message-expected.txt
@@ -5,5 +5,12 @@
  
  
 ..
-   
-  
+ 
+.. ..
+ 
+.. ..
+ 
+ 
+.. ..
+ 
+.. ..
diff --git a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
index 7bd5528..f58ad5e4 100644
--- a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
@@ -364,7 +364,6 @@
     property href
     property hrefTranslate
     property hreflang
-    property interestAction
     property interestTargetElement
     property name
     property origin
@@ -395,7 +394,6 @@
     property host
     property hostname
     property href
-    property interestAction
     property interestTargetElement
     property noHref
     property origin
@@ -517,7 +515,6 @@
     property formMethod
     property formNoValidate
     property formTarget
-    property interestAction
     property interestTargetElement
     property labels
     property name
@@ -775,7 +772,6 @@
     property height
     property incremental
     property indeterminate
-    property interestAction
     property interestTargetElement
     property labels
     property list
@@ -1611,7 +1607,6 @@
     property getCTM
     property getScreenCTM
     property href
-    property interestAction
     property interestTargetElement
     property nearestViewportElement
     property requiredExtensions
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 97cf6af..f36ecf2b 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -3564,7 +3564,6 @@
     getter href
     getter hrefTranslate
     getter hreflang
-    getter interestAction
     getter interestTargetElement
     getter name
     getter origin
@@ -3595,7 +3594,6 @@
     setter href
     setter hrefTranslate
     setter hreflang
-    setter interestAction
     setter interestTargetElement
     setter name
     setter password
@@ -3623,7 +3621,6 @@
     getter host
     getter hostname
     getter href
-    getter interestAction
     getter interestTargetElement
     getter noHref
     getter origin
@@ -3649,7 +3646,6 @@
     setter host
     setter hostname
     setter href
-    setter interestAction
     setter interestTargetElement
     setter noHref
     setter password
@@ -3753,7 +3749,6 @@
     getter formMethod
     getter formNoValidate
     getter formTarget
-    getter interestAction
     getter interestTargetElement
     getter labels
     getter name
@@ -3776,7 +3771,6 @@
     setter formMethod
     setter formNoValidate
     setter formTarget
-    setter interestAction
     setter interestTargetElement
     setter name
     setter popoverTargetAction
@@ -4453,7 +4447,6 @@
     getter height
     getter incremental
     getter indeterminate
-    getter interestAction
     getter interestTargetElement
     getter labels
     getter list
@@ -4514,7 +4507,6 @@
     setter height
     setter incremental
     setter indeterminate
-    setter interestAction
     setter interestTargetElement
     setter max
     setter maxLength
@@ -5665,8 +5657,7 @@
     method getTargetRanges
 interface InterestEvent : Event
     attribute @@toStringTag
-    getter action
-    getter invoker
+    getter source
     method constructor
 interface IntersectionObserver
     attribute @@toStringTag
@@ -8175,11 +8166,9 @@
 interface SVGAElement : SVGGraphicsElement
     attribute @@toStringTag
     getter href
-    getter interestAction
     getter interestTargetElement
     getter target
     method constructor
-    setter interestAction
     setter interestTargetElement
 interface SVGAngle
     attribute @@toStringTag
diff --git a/third_party/boringssl/src b/third_party/boringssl/src
index bca2d72c..d4b6eb5 160000
--- a/third_party/boringssl/src
+++ b/third_party/boringssl/src
@@ -1 +1 @@
-Subproject commit bca2d72c5e3174f0e1b541fb0a3d6bdf31c23825
+Subproject commit d4b6eb542d4fd109baacd550935efd00c521e674
diff --git a/third_party/chromium-variations b/third_party/chromium-variations
index ecd1b2b..187d83f 160000
--- a/third_party/chromium-variations
+++ b/third_party/chromium-variations
@@ -1 +1 @@
-Subproject commit ecd1b2bcb2bca351b58c2d19cf3f149c2ffea50f
+Subproject commit 187d83f8ee5fbe166ce2b668b65b67fa9b940c7f
diff --git a/third_party/dawn b/third_party/dawn
index 42451fa..4548f0c 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit 42451fad80b27c0fa7332b9d0566ff037d708b04
+Subproject commit 4548f0cdeb8bef00bbce127760a0cda8bdaa9fb7
diff --git a/third_party/depot_tools b/third_party/depot_tools
index ea75de4c..80d1969 160000
--- a/third_party/depot_tools
+++ b/third_party/depot_tools
@@ -1 +1 @@
-Subproject commit ea75de4c3baba538f4758d507dd304136cfeab52
+Subproject commit 80d1969422e75e8e9eecafa46074074b289e2568
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index d023fd4..c6bb3b2c 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit d023fd41cb61a756301922cb329b5524c7e7110d
+Subproject commit c6bb3b2ce900d42eefac9c72600ea69e74d40943
diff --git a/third_party/llvm-libc/src b/third_party/llvm-libc/src
index 3fec72b..12809bf 160000
--- a/third_party/llvm-libc/src
+++ b/third_party/llvm-libc/src
@@ -1 +1 @@
-Subproject commit 3fec72bd5608491ec977142db828cc42b88f0ef3
+Subproject commit 12809bfa855813dcef51871e2ee3155e53ed35ea
diff --git a/third_party/perfetto b/third_party/perfetto
index c28325f..943a0d8 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit c28325f6986bd765e5610569211234e403a47a0f
+Subproject commit 943a0d89925e7f9a898c2e49c41feb9bf5dab71e
diff --git a/third_party/search_engines_data/resources b/third_party/search_engines_data/resources
index 797c94d..34ba850 160000
--- a/third_party/search_engines_data/resources
+++ b/third_party/search_engines_data/resources
@@ -1 +1 @@
-Subproject commit 797c94d72b29932c8095bd7885ae26b7ed267db9
+Subproject commit 34ba850d314fced586dfaaf5b099c4906ce17b2d
diff --git a/third_party/skia b/third_party/skia
index c2b103c..a9e8d00 160000
--- a/third_party/skia
+++ b/third_party/skia
@@ -1 +1 @@
-Subproject commit c2b103c89e370ea00d0cc918c92d81fa712ff058
+Subproject commit a9e8d004f0ea4088f8a333cc36e7c019542fcf7e
diff --git a/third_party/swiftshader b/third_party/swiftshader
index f6ad529..64af418 160000
--- a/third_party/swiftshader
+++ b/third_party/swiftshader
@@ -1 +1 @@
-Subproject commit f6ad529e86e441bad3210b0b714dca7faa99b0f8
+Subproject commit 64af4180372914d6d3f37749a5fab2bc3f43a7f9
diff --git a/third_party/webrtc b/third_party/webrtc
index 88833e6..b0038dd1 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 88833e6d22580fa7b358eb5ee551e1b4989fed71
+Subproject commit b0038dd14a1cc637ffbc48e36e55bd96452eef5d
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index b9fcb214..6458507 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -6476,17 +6476,17 @@
   <int value="1027" label="ExecCommandOnInputOrTextarea"/>
   <int value="1028" label="V8History_ScrollRestoration_AttributeGetter"/>
   <int value="1029" label="V8History_ScrollRestoration_AttributeSetter"/>
-  <int value="1030" label="SVG1DOMFilter"/>
+  <int value="1030" label="ObsoleteSVG1DOMFilter"/>
   <int value="1031" label="OfflineAudioContextStartRendering"/>
   <int value="1032" label="OfflineAudioContextSuspend"/>
   <int value="1033" label="OfflineAudioContextResume"/>
   <int value="1034" label="AttrCloneNode"/>
-  <int value="1035" label="SVG1DOMPaintServer"/>
+  <int value="1035" label="ObsoleteSVG1DOMPaintServer"/>
   <int value="1036" label="SVGSVGElementFragmentSVGView"/>
   <int value="1037" label="SVGSVGElementFragmentSVGViewElement"/>
   <int value="1038" label="PresentationConnectionClose"/>
   <int value="1039" label="SVG1DOMShape"/>
-  <int value="1040" label="SVG1DOMText"/>
+  <int value="1040" label="ObsoleteSVG1DOMText"/>
   <int value="1041" label="RTCPeerConnectionConstructorConstraints"/>
   <int value="1042" label="RTCPeerConnectionConstructorCompliant"/>
   <int value="1043"
diff --git a/tools/metrics/histograms/metadata/apps/histograms.xml b/tools/metrics/histograms/metadata/apps/histograms.xml
index 4cbcd102..5cc739d2 100644
--- a/tools/metrics/histograms/metadata/apps/histograms.xml
+++ b/tools/metrics/histograms/metadata/apps/histograms.xml
@@ -2194,7 +2194,7 @@
 </histogram>
 
 <histogram name="Apps.AppShimSignatureValidationResult"
-    enum="WebAppShimSignatureValidationResult" expires_after="2024-11-17">
+    enum="WebAppShimSignatureValidationResult" expires_after="2025-07-15">
   <owner>markrowe@chromium.org</owner>
   <owner>chrome-platform-security-core@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/cookie/histograms.xml b/tools/metrics/histograms/metadata/cookie/histograms.xml
index 9e40fcf..8f2fcf3 100644
--- a/tools/metrics/histograms/metadata/cookie/histograms.xml
+++ b/tools/metrics/histograms/metadata/cookie/histograms.xml
@@ -845,6 +845,8 @@
     other origins.
 
     This metric is recorded once per included cookie on every read attempt.
+
+    Subsampled to 1/1000.
   </summary>
   <token key="HostType">
     <variant name="Localhost"/>
@@ -875,6 +877,8 @@
     domain to set the Domain attribute).
 
     This metric is recorded once per included cookie on every read attempt.
+
+    Subsampled to 1/1000.
   </summary>
   <token key="HostType">
     <variant name="DomainSet"/>
diff --git a/tools/metrics/histograms/metadata/enterprise/enums.xml b/tools/metrics/histograms/metadata/enterprise/enums.xml
index 9cc3cfbc..9dbcd07 100644
--- a/tools/metrics/histograms/metadata/enterprise/enums.xml
+++ b/tools/metrics/histograms/metadata/enterprise/enums.xml
@@ -2188,7 +2188,7 @@
   <int value="1324" label="WebRtcIPHandlingUrl"/>
   <int value="1325" label="GenAIPhotoEditingSettings"/>
   <int value="1326" label="PartitionedBlobUrlUsage"/>
-  <int value="1327" label="GlicEnabled"/>
+  <int value="1327" label="GlicSettings"/>
   <int value="1328" label="DefaultControlledFrameSetting"/>
   <int value="1329" label="ControlledFrameAllowedForUrls"/>
   <int value="1330" label="ControlledFrameBlockedForUrls"/>
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index 7533f44..ccac8073 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -2004,17 +2004,6 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Controls.OverlayCastButtonIsCovered" enum="Boolean"
-    expires_after="2025-01-26">
-  <owner>muyaoxu@google.com</owner>
-  <owner>openscreen-eng@google.com</owner>
-  <summary>
-    Records how often the overlay cast button is hidden because it is covered by
-    another html element. Emitted when the overlay cast button is tried to be
-    shown.
-  </summary>
-</histogram>
-
 <histogram name="Media.Controls.PlaybackSpeed"
     enum="MediaControlsPlaybackSpeed" expires_after="2023-11-12">
   <owner>steimel@chromium.org</owner>
@@ -5035,7 +5024,7 @@
 </histogram>
 
 <histogram name="Media.Remoting.RemotePlaybackEnabledByPage"
-    enum="BooleanEnabled" expires_after="2025-02-01">
+    enum="BooleanEnabled" expires_after="2025-08-05">
   <owner>jophba@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -5046,35 +5035,35 @@
 </histogram>
 
 <histogram name="Media.Remoting.SessionDuration" units="ms"
-    expires_after="2025-02-01">
+    expires_after="2025-08-05">
   <owner>jophba@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>Measures the duration of each remoting session.</summary>
 </histogram>
 
 <histogram name="Media.Remoting.SessionStartFailedReason"
-    enum="RemotingStartFailReason" expires_after="2025-02-01">
+    enum="RemotingStartFailReason" expires_after="2025-08-05">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>Tracks the reason that a session failed to start.</summary>
 </histogram>
 
 <histogram name="Media.Remoting.SessionStartTrigger"
-    enum="RemotingStartTrigger" expires_after="2025-02-01">
+    enum="RemotingStartTrigger" expires_after="2025-08-05">
   <owner>jophba@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>Tracks the trigger for starting a remoting session.</summary>
 </histogram>
 
 <histogram name="Media.Remoting.SessionStopTrigger" enum="RemotingStopTrigger"
-    expires_after="2025-02-01">
+    expires_after="2025-08-05">
   <owner>jophba@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>Tracks the trigger for stopping a remoting session.</summary>
 </histogram>
 
 <histogram name="Media.Remoting.ShortSessionDuration" units="ms"
-    expires_after="2025-02-01">
+    expires_after="2025-08-05">
   <owner>jophba@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -7310,17 +7299,6 @@
   </summary>
 </histogram>
 
-<histogram name="MediaRouter.CastStreaming.Audio.PlaybackOnReceiver"
-    enum="Boolean" expires_after="2024-08-04">
-  <owner>muyaoxu@google.com</owner>
-  <owner>openscreen-eng@google.com</owner>
-  <summary>
-    Records whether audio is played on the receiver device. If not, then on the
-    sender side. Recorded when a new site-initiated mirroring session is
-    started.
-  </summary>
-</histogram>
-
 <histogram name="MediaRouter.CastStreaming.Session.Launch" units="ms"
     expires_after="2025-06-08">
   <owner>muyaoxu@google.com</owner>
@@ -7349,7 +7327,7 @@
 </histogram>
 
 <histogram name="MediaRouter.CastStreaming.Session.Length.OffscreenTab"
-    units="ms" expires_after="2025-02-05">
+    units="ms" expires_after="2025-08-05">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -7358,14 +7336,14 @@
 </histogram>
 
 <histogram name="MediaRouter.CastStreaming.Session.Length.Screen" units="ms"
-    expires_after="2025-06-08">
+    expires_after="2025-08-05">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>Total length of a Cast Streaming Screen mirror session.</summary>
 </histogram>
 
 <histogram name="MediaRouter.CastStreaming.Session.Length.Tab" units="ms"
-    expires_after="2025-06-08">
+    expires_after="2025-08-05">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -7441,7 +7419,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Dial.CreateRoute"
-    enum="MediaRouterDialCreateRouteResult" expires_after="2025-02-05">
+    enum="MediaRouterDialCreateRouteResult" expires_after="2025-08-05">
   <owner>muyaoxu@google.com</owner>
   <owner>mfoltz@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
@@ -7452,7 +7430,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Dial.DeviceDescriptionParsingResult"
-    enum="MediaRouterDeviceDescriptionParsingResult" expires_after="2025-02-10">
+    enum="MediaRouterDeviceDescriptionParsingResult" expires_after="2025-08-05">
   <owner>muyaoxu@google.com</owner>
   <owner>mfoltz@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
@@ -7475,7 +7453,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Dial.KnownDevicesCount" units="devices"
-    expires_after="2025-02-01">
+    expires_after="2025-08-05">
   <owner>muyaoxu@google.com</owner>
   <owner>mfoltz@chromium.org</owner>
   <owner>openscreen-eng@google.com</owner>
@@ -7625,7 +7603,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Source.CastingSource"
-    enum="MediaRouterSourceTypes" expires_after="2025-02-10">
+    enum="MediaRouterSourceTypes" expires_after="2025-08-05">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -7635,7 +7613,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Ui.Android.DialogAction"
-    enum="MediaRouterAndroidDialogAction" expires_after="2025-02-05">
+    enum="MediaRouterAndroidDialogAction" expires_after="2025-08-05">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -7647,7 +7625,7 @@
 </histogram>
 
 <histogram name="MediaRouter.Ui.Android.DialogType"
-    enum="MediaRouterAndroidDialogType" expires_after="2025-02-05">
+    enum="MediaRouterAndroidDialogType" expires_after="2025-08-05">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
@@ -7671,7 +7649,7 @@
 
 <histogram name="MediaRouter.Ui.Dialog.ActivationLocationAndCastMode"
     enum="MediaRouterDialogActivationLocationAndCastMode"
-    expires_after="2025-02-05">
+    expires_after="2025-08-05">
   <owner>muyaoxu@google.com</owner>
   <owner>openscreen-eng@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/mobile/histograms.xml b/tools/metrics/histograms/metadata/mobile/histograms.xml
index aff5631f..c32511f 100644
--- a/tools/metrics/histograms/metadata/mobile/histograms.xml
+++ b/tools/metrics/histograms/metadata/mobile/histograms.xml
@@ -325,7 +325,7 @@
 </histogram>
 
 <histogram name="Mobile.Messages.Banner.OnScreenTime" units="ms"
-    expires_after="2025-02-26">
+    expires_after="2026-02-26">
   <owner>thegreenfrog@chromium.org</owner>
   <owner>bling-team@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml
index 55846d3..5e8e9c5e6 100644
--- a/tools/metrics/histograms/metadata/network/histograms.xml
+++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -167,6 +167,15 @@
 </histogram>
 
 <histogram name="Network.Ash.Cellular.Apn.CreateCustomApn.Result"
+    enum="BooleanSuccess" expires_after="2026-01-14">
+  <owner>nikhilcn@google.com</owner>
+  <owner>cros-device-enablement@google.com</owner>
+  <summary>
+    Deprecated. Records the result of an attempt to create a custom APN.
+  </summary>
+</histogram>
+
+<histogram name="Network.Ash.Cellular.Apn.CreateCustomApn.Result2"
     enum="CreateCustomApnResult" expires_after="2026-01-14">
   <owner>nikhilcn@google.com</owner>
   <owner>cros-device-enablement@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 29d0e00..d43f3a1 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -7713,6 +7713,28 @@
   </summary>
 </histogram>
 
+<histogram name="Process.Sandbox.IPC.ThreadCreateRemoteThreadErrorCode"
+    enum="WinGetLastError" expires_after="2025-06-01">
+  <owner>ajgo@chromium.org</owner>
+  <owner>wfh@chromium.org</owner>
+  <summary>
+    Error returned when CreateThreadAction fails to CreateRemoteThread(). Not
+    logged on success. For decoding error code please refer to
+    http://goo.gl/fJJiAv.
+  </summary>
+</histogram>
+
+<histogram name="Process.Sandbox.IPC.ThreadDuplicateHandleErrorCode"
+    enum="WinGetLastError" expires_after="2025-06-01">
+  <owner>ajgo@chromium.org</owner>
+  <owner>wfh@chromium.org</owner>
+  <summary>
+    Error returned when CreateThreadAction fails to DuplicateHandle(). Not
+    logged on success. For decoding error code please refer to
+    http://goo.gl/fJJiAv.
+  </summary>
+</histogram>
+
 <histogram name="Process.Sandbox.Launch.Error" enum="WinGetLastError"
     expires_after="never">
 <!-- expires-never: metric needed for diagnosing sandbox issues. -->
diff --git a/tools/metrics/histograms/metadata/stability/histograms.xml b/tools/metrics/histograms/metadata/stability/histograms.xml
index 3b58bb8c..0b7e332 100644
--- a/tools/metrics/histograms/metadata/stability/histograms.xml
+++ b/tools/metrics/histograms/metadata/stability/histograms.xml
@@ -312,7 +312,7 @@
 </histogram>
 
 <histogram name="Stability.ChildFrameCrash.TabMarkedForReload"
-    enum="BooleanMarkedForReload" expires_after="2025-02-16">
+    enum="BooleanMarkedForReload" expires_after="2026-02-16">
   <owner>alexmos@chromium.org</owner>
   <owner>boliu@chromium.org</owner>
   <summary>
@@ -322,7 +322,7 @@
 </histogram>
 
 <histogram name="Stability.ChildFrameCrash.TabMarkedForReload.Visibility"
-    enum="FrameVisibility" expires_after="2025-02-16">
+    enum="FrameVisibility" expires_after="2026-02-16">
   <owner>alexmos@chromium.org</owner>
   <owner>boliu@chromium.org</owner>
   <summary>
diff --git a/tools/perf/core/perf_benchmark_unittest.py b/tools/perf/core/perf_benchmark_unittest.py
index eaa9476..3a399a0 100644
--- a/tools/perf/core/perf_benchmark_unittest.py
+++ b/tools/perf/core/perf_benchmark_unittest.py
@@ -60,7 +60,7 @@
     self.assertEqual(num_expected_matches, len(ruleset_data_to_copy))
 
   # Flaky: https://crbug.com/1342706
-  @decorators.Disabled('android-nougat', 'android-oreo', 'android-pie')
+  @decorators.Disabled('android')
   def testVariationArgs(self):
     benchmark = perf_benchmark.PerfBenchmark()
     options = options_for_unittests.GetCopy()
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index b770a90..6724392 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v49.0/linux-arm64/trace_processor_shell"
         },
         "win": {
-            "hash": "dd2db82e436b522f699a72ce3ecfeca786345eee",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/5cac0a369729cd1e8aa92a9ca4cbeee7cbec8ad2/trace_processor_shell.exe"
+            "hash": "1c92a6e5ce76875a764aafc645124b645187cfd3",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/943a0d89925e7f9a898c2e49c41feb9bf5dab71e/trace_processor_shell.exe"
         },
         "linux_arm": {
             "hash": "a15d8362d80cfd7cd8d785cf6afc22586de688cd",
@@ -21,8 +21,8 @@
             "full_remote_path": "perfetto-luci-artifacts/v49.0/mac-arm64/trace_processor_shell"
         },
         "linux": {
-            "hash": "293465353d9208742e4d269cf28c64bf16c283ed",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/5cac0a369729cd1e8aa92a9ca4cbeee7cbec8ad2/trace_processor_shell"
+            "hash": "c993ea68c410707bc9f008686392a2c995791669",
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/943a0d89925e7f9a898c2e49c41feb9bf5dab71e/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/tools/whats_new/whats_new_util.py b/tools/whats_new/whats_new_util.py
index 81399d3..81a2058 100644
--- a/tools/whats_new/whats_new_util.py
+++ b/tools/whats_new/whats_new_util.py
@@ -159,7 +159,8 @@
       feature_dict: Data for the new What's New feature.
   """
     feature_name = feature_dict['Feature name']
-    whats_new_util_file = os.path.join(BASE_DIR, '..', CHROME_ICON_FILENAME)
+    whats_new_util_file = os.path.join(
+        BASE_DIR, '..', 'ios/chrome/browser/ui/whats_new/whats_new_util.mm')
     with open(whats_new_util_file, 'r+', encoding='utf-8', newline='') as file:
         read_data = file.read()
         whats_new_type_error_regex = r'case WhatsNewType::kError:'
@@ -446,8 +447,7 @@
 
     icon_name_regex = re.compile(r"@\"(.*)\";")
 
-    valid_icons_file = os.path.join(
-        BASE_DIR, '../ios/chrome/browser/shared/ui/symbols/symbol_names.mm')
+    valid_icons_file = os.path.join(BASE_DIR, '..', CHROME_ICON_FILENAME)
     found_default_icons = False
     with open(valid_icons_file, 'r', encoding='utf-8', newline='') as file:
         # The icons file starts with custom icons until reaching the comment
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index d7e6690..f30df0d 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -780,6 +780,7 @@
   sources = [
     "java/src/org/chromium/ui/listmenu/ListMenuButtonTest.java",
     "java/src/org/chromium/ui/widget/TextViewWithTightWrapTest.java",
+    "javatests/src/org/chromium/ui/animation/RenderTestAnimationUtils.java",
     "javatests/src/org/chromium/ui/base/ClipboardAndroidTest.java",
     "javatests/src/org/chromium/ui/util/WindowInsetsUtilsJavaUnitTest.java",
     "junit/src/org/chromium/ui/widget/TextViewWithLeadingTest.java",
diff --git a/ui/android/javatests/src/org/chromium/ui/animation/RenderTestAnimationUtils.java b/ui/android/javatests/src/org/chromium/ui/animation/RenderTestAnimationUtils.java
new file mode 100644
index 0000000..6051f49
--- /dev/null
+++ b/ui/android/javatests/src/org/chromium/ui/animation/RenderTestAnimationUtils.java
@@ -0,0 +1,50 @@
+// Copyright 2025 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.ui.animation;
+
+import android.animation.ValueAnimator;
+import android.view.View;
+
+import org.chromium.base.ThreadUtils;
+import org.chromium.ui.test.util.RenderTestRule;
+
+import java.io.IOException;
+import java.util.Locale;
+
+/** Utilities related to render testing for animations. */
+public class RenderTestAnimationUtils {
+    /**
+     * Steps through an animation and generates rendered images for each step.
+     *
+     * @param testcaseName The base name for the render test results.
+     * @param renderTestRule The {@link RenderTestRule} for the render test.
+     * @param rootView The root {@link View} where the render test takes place.
+     * @param animator The {@link ValueAnimator} for the render test.
+     * @param steps The number of steps the animation should take (2 or more).
+     * @throws IOException if the rendered image cannot be saved to the device.
+     */
+    public static void stepThroughAnimation(
+            String testcaseName,
+            RenderTestRule renderTestRule,
+            View rootView,
+            ValueAnimator animator,
+            int steps)
+            throws IOException {
+        assert steps >= 2;
+        float fractionPerStep = 1.0f / (steps - 1);
+
+        // Manually drive the animation instead of using a ValueAnimator for exact control over
+        // step size and timing.
+        for (int step = 0; step < steps; step++) {
+            final float animationFraction = fractionPerStep * step;
+            ThreadUtils.runOnUiThreadBlocking(() -> animator.setCurrentFraction(animationFraction));
+
+            renderTestRule.render(
+                    rootView,
+                    testcaseName
+                            + String.format(Locale.ENGLISH, "_step_%d_of_%d", step + 1, steps));
+        }
+    }
+}
diff --git a/ui/aura/demo/demo_main.cc b/ui/aura/demo/demo_main.cc
index 60467f6..8f29297 100644
--- a/ui/aura/demo/demo_main.cc
+++ b/ui/aura/demo/demo_main.cc
@@ -18,7 +18,6 @@
 #include "base/task/thread_pool/thread_pool_instance.h"
 #include "build/build_config.h"
 #include "components/viz/host/host_frame_sink_manager.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "mojo/core/embedder/embedder.h"
 #include "third_party/skia/include/core/SkBlendMode.h"
@@ -185,9 +184,8 @@
 
   // The ContextFactory must exist before any Compositors are created.
   viz::HostFrameSinkManager host_frame_sink_manager;
-  viz::ServerSharedBitmapManager server_shared_bitmap_manager;
   viz::FrameSinkManagerImpl frame_sink_manager{
-      viz::FrameSinkManagerImpl::InitParams(&server_shared_bitmap_manager)};
+      viz::FrameSinkManagerImpl::InitParams()};
   host_frame_sink_manager.SetLocalManager(&frame_sink_manager);
   frame_sink_manager.SetLocalClient(&host_frame_sink_manager);
   auto context_factory = std::make_unique<ui::InProcessContextFactory>(
diff --git a/ui/aura/window.cc b/ui/aura/window.cc
index 10d798d..09f237bc 100644
--- a/ui/aura/window.cc
+++ b/ui/aura/window.cc
@@ -62,6 +62,7 @@
 #include "ui/compositor/compositor.h"
 #include "ui/compositor/layer.h"
 #include "ui/compositor/layer_animator.h"
+#include "ui/compositor/layer_type.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/events/event_target_iterator.h"
@@ -333,7 +334,9 @@
     return;
   transparent_ = transparent;
 
-  layer()->SetFillsBoundsOpaquely(!transparent_);
+  if (layer()->type() != ui::LAYER_SOLID_COLOR) {
+    layer()->SetFillsBoundsOpaquely(!transparent_);
+  }
   TriggerChangedCallback(&transparent_);
 }
 
diff --git a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.cc b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.cc
index 09e9ba0..f413108 100644
--- a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.cc
+++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux.cc
@@ -35,10 +35,7 @@
     scoped_refptr<dbus::Bus> bus)
     : bus_(std::move(bus)) {
   if (!bus_) {
-    dbus::Bus::Options options;
-    options.bus_type = dbus::Bus::SESSION;
-    options.dbus_task_runner = dbus_thread_linux::GetTaskRunner();
-    bus_ = base::MakeRefCounted<dbus::Bus>(options);
+    bus_ = dbus_thread_linux::GetSharedSessionBus();
   }
 
   global_shortcuts_proxy_ = bus_->GetObjectProxy(
@@ -69,9 +66,6 @@
         base::DoNothing());
   }
   session_map_.clear();
-
-  dbus_thread_linux::GetTaskRunner()->PostTask(
-      FROM_HERE, base::BindOnce(&dbus::Bus::ShutdownAndBlock, bus_));
 }
 
 void GlobalAcceleratorListenerLinux::OnSystemdUnitStarted(
diff --git a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux_unittest.cc b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux_unittest.cc
index 1907d808..9826e6af 100644
--- a/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux_unittest.cc
+++ b/ui/base/accelerators/global_accelerator_listener/global_accelerator_listener_linux_unittest.cc
@@ -369,7 +369,6 @@
           MatchMethod(GlobalAcceleratorListenerLinux::kSessionInterface,
                       GlobalAcceleratorListenerLinux::kMethodCloseSession),
           _, _));
-  EXPECT_CALL(*mock_bus, ShutdownAndBlock());
   global_shortcut_listener.reset();
 }
 
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index 9f8aab7..c2edc927 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -205,6 +205,12 @@
 #endif
 );
 
+// TODO(crbug.com/389771428): Switch the ui::Compositor to use
+// cc::PropertyTrees and layer lists rather than layer trees.
+BASE_FEATURE(kUiCompositorUsesLayerLists,
+             "UiCompositorUsesLayerLists",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // Enables the use of a touch fling curve that is based on the behavior of
 // native apps on Windows.
 BASE_FEATURE(kExperimentalFlingAnimation,
diff --git a/ui/base/ui_base_features.h b/ui/base/ui_base_features.h
index 27162c4..a66151f1 100644
--- a/ui/base/ui_base_features.h
+++ b/ui/base/ui_base_features.h
@@ -25,6 +25,8 @@
 COMPONENT_EXPORT(UI_BASE_FEATURES) BASE_DECLARE_FEATURE(kSystemKeyboardLock);
 COMPONENT_EXPORT(UI_BASE_FEATURES)
 BASE_DECLARE_FEATURE(kUiCompositorScrollWithLayers);
+COMPONENT_EXPORT(UI_BASE_FEATURES)
+BASE_DECLARE_FEATURE(kUiCompositorUsesLayerLists);
 
 COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsUiGpuRasterizationEnabled();
 
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 75ffc37..4cc0de6 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -307,12 +307,14 @@
   clone->SetVisible(GetTargetVisibility());
   clone->SetClipRect(GetTargetClipRect());
   clone->SetAcceptEvents(accept_events());
-  clone->SetFillsBoundsOpaquely(fills_bounds_opaquely_);
   clone->SetFillsBoundsCompletely(fills_bounds_completely_);
   clone->SetRoundedCornerRadius(GetTargetRoundedCornerRadius());
   clone->SetGradientMask(gradient_mask());
   clone->SetIsFastRoundedCorner(is_fast_rounded_corner());
   clone->SetName(name_);
+  if (type() != LAYER_SOLID_COLOR) {
+    clone->SetFillsBoundsOpaquely(fills_bounds_opaquely_);
+  }
 
   // the |damaged_region_| will be sent to cc later in SendDamagedRects().
   clone->damaged_region_ = damaged_region_;
@@ -896,6 +898,7 @@
 }
 
 void Layer::SetFillsBoundsOpaquely(bool fills_bounds_opaquely) {
+  CHECK_NE(type_, LayerType::LAYER_SOLID_COLOR);
   SetFillsBoundsOpaquelyWithReason(fills_bounds_opaquely,
                                    PropertyChangeReason::NOT_FROM_ANIMATION);
 }
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index 729a8f2..b0d78219 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -414,6 +414,8 @@
   // Note: Setting a layer non-opaque has significant performance impact,
   // especially on low-end Chrome OS devices. Please ensure you are not
   // adding unnecessary overdraw. When in doubt, talk to the graphics team.
+  // NOTE: Opacity of SOLID_COLOR layer is determined by the color's alpha
+  // channel. Calling this on SOLID_COLOR results in check failure.
   void SetFillsBoundsOpaquely(bool fills_bounds_opaquely);
   bool fills_bounds_opaquely() const { return fills_bounds_opaquely_; }
 
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc
index 6bf2754..ecc6b84 100644
--- a/ui/compositor/layer_unittest.cc
+++ b/ui/compositor/layer_unittest.cc
@@ -865,7 +865,7 @@
 
   constexpr SkColor kTransparent = SK_ColorTRANSPARENT;
   layer->SetColor(kTransparent);
-  layer->SetFillsBoundsOpaquely(false);
+
   // Color and opaqueness targets should be preserved during cloning, even after
   // switching away from solid color content.
   ASSERT_TRUE(layer->SwitchCCLayerForTest());
@@ -882,23 +882,6 @@
   EXPECT_FALSE(clone->LayerHasCustomColorMatrix());
   EXPECT_FALSE(clone->fills_bounds_opaquely());
 
-  // A solid color layer with transparent color can be marked as opaque. The
-  // clone should retain this state.
-  layer = CreateLayer(LAYER_SOLID_COLOR);
-  layer->SetColor(kTransparent);
-  layer->SetFillsBoundsOpaquely(true);
-
-  clone = layer->Clone();
-  EXPECT_TRUE(clone->GetTargetTransform().IsIdentity());
-  EXPECT_EQ(kTransparent, clone->background_color());
-  EXPECT_EQ(kTransparent, clone->GetTargetColor());
-  EXPECT_FALSE(clone->layer_inverted());
-  // Sepia and hue rotation should be off by default.
-  EXPECT_FLOAT_EQ(0, layer->layer_sepia());
-  EXPECT_FLOAT_EQ(0, clone->layer_hue_rotation());
-  EXPECT_FALSE(clone->LayerHasCustomColorMatrix());
-  EXPECT_TRUE(clone->fills_bounds_opaquely());
-
   layer = CreateLayer(LAYER_SOLID_COLOR);
   layer->SetVisible(true);
   layer->SetOpacity(1.0f);
@@ -1143,7 +1126,6 @@
 
 TEST_F(LayerWithNullDelegateTest, SwitchLayerPreservesCCLayerState) {
   std::unique_ptr<Layer> l1 = CreateLayer(LAYER_SOLID_COLOR);
-  l1->SetFillsBoundsOpaquely(true);
   l1->SetVisible(false);
   l1->SetBounds(gfx::Rect(4, 5));
 
@@ -2690,7 +2672,6 @@
   SkColor transparent = SK_ColorTRANSPARENT;
   std::unique_ptr<Layer> root = CreateLayer(LAYER_SOLID_COLOR);
   GetCompositor()->SetRootLayer(root.get());
-  root->SetFillsBoundsOpaquely(false);
   root->SetColor(transparent);
 
   EXPECT_FALSE(root->fills_bounds_opaquely());
@@ -2727,7 +2708,6 @@
   {
     ui::ScopedLayerAnimationSettings animation(root->GetAnimator());
     animation.SetTransitionDuration(base::Milliseconds(1000));
-    root->SetFillsBoundsOpaquely(false);
     root->SetColor(transparent);
   }
 
diff --git a/ui/compositor/test/test_context_factories.cc b/ui/compositor/test/test_context_factories.cc
index 030afd4..1729d20d 100644
--- a/ui/compositor/test/test_context_factories.cc
+++ b/ui/compositor/test/test_context_factories.cc
@@ -7,7 +7,6 @@
 #include "base/command_line.h"
 #include "components/viz/common/features.h"
 #include "components/viz/host/host_frame_sink_manager.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "ui/compositor/compositor_switches.h"
 #include "ui/gl/gl_implementation.h"
@@ -21,9 +20,8 @@
     enable_pixel_output = true;
   if (enable_pixel_output)
     disable_null_draw_ = std::make_unique<gl::DisableNullDrawGLBindings>();
-  shared_bitmap_manager_ = std::make_unique<viz::ServerSharedBitmapManager>();
   frame_sink_manager_ = std::make_unique<viz::FrameSinkManagerImpl>(
-      viz::FrameSinkManagerImpl::InitParams(shared_bitmap_manager_.get()));
+      viz::FrameSinkManagerImpl::InitParams());
   host_frame_sink_manager_ = std::make_unique<viz::HostFrameSinkManager>();
   implicit_factory_ = std::make_unique<InProcessContextFactory>(
       host_frame_sink_manager_.get(), frame_sink_manager_.get(),
diff --git a/ui/compositor/test/test_context_factories.h b/ui/compositor/test/test_context_factories.h
index bccb8fc..299532b 100644
--- a/ui/compositor/test/test_context_factories.h
+++ b/ui/compositor/test/test_context_factories.h
@@ -15,7 +15,6 @@
 
 namespace viz {
 class HostFrameSinkManager;
-class ServerSharedBitmapManager;
 class FrameSinkManagerImpl;
 }  // namespace viz
 
@@ -40,7 +39,6 @@
 
  private:
   std::unique_ptr<gl::DisableNullDrawGLBindings> disable_null_draw_;
-  std::unique_ptr<viz::ServerSharedBitmapManager> shared_bitmap_manager_;
   std::unique_ptr<viz::FrameSinkManagerImpl> frame_sink_manager_;
   std::unique_ptr<viz::HostFrameSinkManager> host_frame_sink_manager_;
   std::unique_ptr<ui::InProcessContextFactory> implicit_factory_;
diff --git a/ui/gfx/frame_data.h b/ui/gfx/frame_data.h
index cc1a291..38d90bc 100644
--- a/ui/gfx/frame_data.h
+++ b/ui/gfx/frame_data.h
@@ -38,6 +38,10 @@
   // fall out of delegated mode.
   gfx::CALayerResult ca_layer_error_code = gfx::kCALayerSuccess;
 #endif
+
+#if BUILDFLAG(IS_MAC)
+  bool is_handling_interaction_or_animation = false;
+#endif
 };
 
 }  // namespace gfx
diff --git a/ui/shell_dialogs/shell_dialog_linux.cc b/ui/shell_dialogs/shell_dialog_linux.cc
index 2276160..21ce7fae 100644
--- a/ui/shell_dialogs/shell_dialog_linux.cc
+++ b/ui/shell_dialogs/shell_dialog_linux.cc
@@ -25,12 +25,6 @@
 #endif
 }
 
-void Finalize() {
-#if BUILDFLAG(USE_DBUS)
-  ui::SelectFileDialogLinuxPortal::DestroyPortalConnection();
-#endif
-}
-
 }  // namespace shell_dialog_linux
 
 namespace ui {
@@ -58,8 +52,6 @@
   // Check to see if the portal is available.
   if (SelectFileDialogLinuxPortal::IsPortalAvailable())
     return kPortal;
-  // Make sure to kill the portal connection.
-  SelectFileDialogLinuxPortal::DestroyPortalConnection();
 #endif
 
   // Check to see if KDE is the desktop environment.
diff --git a/ui/shell_dialogs/shell_dialog_linux.h b/ui/shell_dialogs/shell_dialog_linux.h
index 9e27f2f..2888ae3 100644
--- a/ui/shell_dialogs/shell_dialog_linux.h
+++ b/ui/shell_dialogs/shell_dialog_linux.h
@@ -9,13 +9,11 @@
 
 namespace shell_dialog_linux {
 
-// TODO(thomasanderson): Remove Initialize() and Finalize().
+// TODO(thomasanderson): Remove Initialize().
 
 // Should be called before the first call to CreateSelectFileDialog.
 SHELL_DIALOGS_EXPORT void Initialize();
 
-SHELL_DIALOGS_EXPORT void Finalize();
-
 }  // namespace shell_dialog_linux
 
 #endif  // UI_SHELL_DIALOGS_SHELL_DIALOG_LINUX_H_
diff --git a/ui/touch_selection/touch_selection_magnifier_aura.cc b/ui/touch_selection/touch_selection_magnifier_aura.cc
index ff95688..dba189ca 100644
--- a/ui/touch_selection/touch_selection_magnifier_aura.cc
+++ b/ui/touch_selection/touch_selection_magnifier_aura.cc
@@ -300,7 +300,6 @@
   // Create the zoom layer, which will show the zoomed contents.
   zoom_layer_ = std::make_unique<Layer>(LAYER_SOLID_COLOR);
   zoom_layer_->SetBackgroundZoom(kMagnifierScale, 0);
-  zoom_layer_->SetFillsBoundsOpaquely(false);
   // BackdropFilterBounds applies after the backdrop filter (the zoom effect)
   // but before anything else, meaning its clipping effect is transformed by
   // the layer_offset() filter operation. SetRoundedCornerRadius() applies too
diff --git a/ui/views/examples/examples_main_proc.cc b/ui/views/examples/examples_main_proc.cc
index c3858f9..48fdc364 100644
--- a/ui/views/examples/examples_main_proc.cc
+++ b/ui/views/examples/examples_main_proc.cc
@@ -26,7 +26,6 @@
 #include "base/test/test_timeouts.h"
 #include "build/build_config.h"
 #include "components/viz/host/host_frame_sink_manager.h"
-#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h"
 #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
 #include "mojo/core/embedder/embedder.h"
 #include "ui/accessibility/platform/ax_platform_for_test.h"
diff --git a/ui/views/view.cc b/ui/views/view.cc
index 4c3aabce..7ca7279 100644
--- a/ui/views/view.cc
+++ b/ui/views/view.cc
@@ -61,6 +61,7 @@
 #include "ui/compositor/clip_recorder.h"
 #include "ui/compositor/compositor.h"
 #include "ui/compositor/layer.h"
+#include "ui/compositor/layer_type.h"
 #include "ui/compositor/paint_context.h"
 #include "ui/compositor/paint_recorder.h"
 #include "ui/compositor/transform_recorder.h"
@@ -2561,7 +2562,9 @@
 
   CreateOrDestroyLayer();
 
-  layer()->SetFillsBoundsOpaquely(false);
+  if (layer()->type() != ui::LAYER_SOLID_COLOR) {
+    layer()->SetFillsBoundsOpaquely(false);
+  }
 }
 
 void View::SetLayerParent(ui::Layer* parent_layer) {
diff --git a/ui/views/widget/native_widget_mac.mm b/ui/views/widget/native_widget_mac.mm
index 242a2c8..89247a9e 100644
--- a/ui/views/widget/native_widget_mac.mm
+++ b/ui/views/widget/native_widget_mac.mm
@@ -62,8 +62,12 @@
   // If the Widget is modal, it will be displayed as a sheet. This works best if
   // it has NSWindowStyleMaskTitled. For example, with
   // NSWindowStyleMaskBorderless, the parent window still accepts input.
+  // A sheet will not have a titlebar despite being NSWindowStyleMaskTitled.
   // NSWindowStyleMaskFullSizeContentView ensures that calculating the modal's
   // content rect doesn't account for a nonexistent title bar.
+  // TODO(crbug.com/390441085): a window-modal should always have a parent.
+  // Otherwise, it will not be displayed as a sheet. Then it will show a native
+  // titlebar, which will cover the content.
   if (params.delegate &&
       params.delegate->GetModalType() == ui::mojom::ModalType::kWindow) {
     return NSWindowStyleMaskTitled | NSWindowStyleMaskFullSizeContentView;
diff --git a/ui/views/window/dialog_delegate.cc b/ui/views/window/dialog_delegate.cc
index de21236..7d1c482 100644
--- a/ui/views/window/dialog_delegate.cc
+++ b/ui/views/window/dialog_delegate.cc
@@ -105,8 +105,7 @@
 
 // static
 bool DialogDelegate::CanSupportCustomFrame(gfx::NativeView parent) {
-#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && \
-    BUILDFLAG(ENABLE_DESKTOP_AURA)
+#if BUILDFLAG(IS_LINUX) && BUILDFLAG(ENABLE_DESKTOP_AURA)
   // The new style doesn't support unparented dialogs on Linux desktop.
   return parent != nullptr;
 #else
diff --git a/ui/webui/resources/cr_components/commerce/product_specifications_browser_proxy.ts b/ui/webui/resources/cr_components/commerce/product_specifications_browser_proxy.ts
index c6eebd7d..8edfbbc 100644
--- a/ui/webui/resources/cr_components/commerce/product_specifications_browser_proxy.ts
+++ b/ui/webui/resources/cr_components/commerce/product_specifications_browser_proxy.ts
@@ -6,13 +6,15 @@
 import type {Url} from '//resources/mojo/url/mojom/url.mojom-webui.js';
 
 import {PageCallbackRouter, ProductSpecificationsHandlerFactory, ProductSpecificationsHandlerRemote} from './product_specifications.mojom-webui.js';
-import type {DisclosureVersion} from './product_specifications.mojom-webui.ts';
+import type {DisclosureVersion, ShowSetDisposition} from './product_specifications.mojom-webui.ts';
 
 let instance: ProductSpecificationsBrowserProxy|null = null;
 
 export interface ProductSpecificationsBrowserProxy {
   getCallbackRouter(): PageCallbackRouter;
   showProductSpecificationsSetForUuid(uuid: Uuid, inNewTab: boolean): void;
+  showProductSpecificationsSetsForUuids(
+      uuids: Uuid[], disposition: ShowSetDisposition): void;
   showComparePage(inNewTab: boolean): void;
   setAcceptedDisclosureVersion(version: DisclosureVersion): void;
   maybeShowDisclosure(urls: Url[], name: string, setId: string):
@@ -51,6 +53,11 @@
     this.handler.showProductSpecificationsSetForUuid(uuid, inNewTab);
   }
 
+  showProductSpecificationsSetsForUuids(
+      uuids: Uuid[], disposition: ShowSetDisposition): void {
+    this.handler.showProductSpecificationsSetsForUuids(uuids, disposition);
+  }
+
   showComparePage(inNewTab: boolean) {
     this.handler.showComparePage(inNewTab);
   }
diff --git a/url/scheme_host_port.cc b/url/scheme_host_port.cc
index d66fdb10..fa0c15b4 100644
--- a/url/scheme_host_port.cc
+++ b/url/scheme_host_port.cc
@@ -269,15 +269,10 @@
          base::trace_event::EstimateMemoryUsage(host_);
 }
 
-bool SchemeHostPort::operator==(const SchemeHostPort& other) const {
-  return (*this <=> other) == 0;
-}
+bool SchemeHostPort::operator==(const SchemeHostPort& other) const = default;
 
 std::strong_ordering SchemeHostPort::operator<=>(
-    const SchemeHostPort& other) const {
-  return std::tie(port_, scheme_, host_) <=>
-         std::tie(other.port_, other.scheme_, other.host_);
-}
+    const SchemeHostPort& other) const = default;
 
 std::string SchemeHostPort::SerializeInternal(url::Parsed* parsed) const {
   std::string result;
diff --git a/url/scheme_host_port.h b/url/scheme_host_port.h
index ae8385a..6b02f58b 100644
--- a/url/scheme_host_port.h
+++ b/url/scheme_host_port.h
@@ -161,9 +161,10 @@
   std::string SerializeInternal(url::Parsed* parsed) const;
 
  private:
+  // Note: `port_` is declared first to control the sort order.
+  uint16_t port_ = 0;
   std::string scheme_;
   std::string host_;
-  uint16_t port_ = 0;
 };
 
 COMPONENT_EXPORT(URL)
diff --git a/v8 b/v8
index 15c092a..4bd0d47 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 15c092afb8cad2be5d5a62d970ee38b16a14645b
+Subproject commit 4bd0d4731ea90ff9460eeb6d5cfc6cc1127363d3