diff --git a/DEPS b/DEPS
index 6852a58..131458fd 100644
--- a/DEPS
+++ b/DEPS
@@ -295,7 +295,7 @@
   # 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': '20b9f62811d76f8f5de0eba491b161f8bb948c6e',
+  'src_internal_revision': 'ce971f9a7bc61262bdef47fe1bee00420f0bbfc0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
@@ -303,23 +303,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.
-  'v8_revision': '84b3ee97fdd8f2df1e477b0cc79b893707da21b1',
+  'v8_revision': '7f56d608e37d27607c638cdb4d9995bd7ad4d68c',
   # 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': '2d61c576f0dd8a1a5ef5adeefe19101c5ea64eb7',
+  'angle_revision': '02bc00c406237810c435277a13f742d14cd16782',
   # 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': '093b4d82a49affdcc5e6d68cc17aa0c33c82e9a2',
+  'swiftshader_revision': '930d46d31b5d637f313fd5ef55da2bbf053c26c1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'e265bde6fee233a96f87ca46464dd662531e0f90',
+  'pdfium_revision': 'b8e8a35d09143db3342b94ddef076c03aa46159f',
   # 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': '0f1d0df6183d6ddf0b4d7a10bf80122c7ec260e6',
+  'boringssl_revision': '864a235afcf4d2575b1eab8de96fbf0d84f6cda9',
   # 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.
@@ -371,11 +371,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '52ad7cf544050f01f47c070716b9dae8eb9fab2b',
+  'catapult_revision': '64c31ffa4d735add4a7e7520a52e0e3160216132',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling CrossBench
   # and whatever else without interference from each other.
-  'crossbench_revision': '35e8177a7d7594203c543fe3875fd587b949fca2',
+  'crossbench_revision': '73e0cfe22e2699bf14bedb489c0b39ba1bc03702',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -391,7 +391,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': 'cd650159ff9210d83ed1e1aaf02b1c071d5123b3',
+  'devtools_frontend_revision': 'e5e1d2ed56cb0ad0c82a0b36295cae4578d8a226',
   # 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.
@@ -415,7 +415,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': '5b595fdbcfc1d0d79354ab74f675c564273874c7',
+  'dawn_revision': 'dfe3855e5d0b5367a598e60674766ffa1c894c71',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -519,7 +519,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':    'd7bdad4ef86b827a96469b1dfdfcfa1218930e59',
+  'llvm_libc_revision':    'e3e030ec6ee1674bf2195d0cfd0a4bf5fee16537',
   # 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.
@@ -1486,12 +1486,12 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '4326f348545fb5ef5cbb59389e7e3a903d2320eb',
+    '72050536a13f233b345a2f22647ff5c1a289b42f',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
   'src/docs/website': {
-    'url': Var('chromium_git') + '/website.git' + '@' + 'b2558d301fc88f8e4671024c47960ff1ee82cf34',
+    'url': Var('chromium_git') + '/website.git' + '@' + '406e2b2832f3d734a2ba4f8fa93fb78deecb16d2',
   },
 
   'src/ios/third_party/earl_grey2/src': {
@@ -1515,7 +1515,7 @@
   },
 
   'src/ios/third_party/material_components_ios/src': {
-      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'f25aad87ce54240ed58abe7ae84b9316e80a8492',
+      'url': Var('chromium_git') + '/external/github.com/material-components/material-components-ios.git' + '@' + 'b29adefbd8be6d39542d679b8f9189fc466283b7',
       'condition': 'checkout_ios',
   },
 
@@ -1645,7 +1645,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'maUv18JmcvP3nwKYXghGhozFkEce3pFDH6JtbSR0SqQC',
+          'version': 'a1d-sHWq2y1nFvYwrBfZHKtnwQXeYeyJXaK6H_dKEtQC',
       },
     ],
     'condition': 'checkout_android and non_git_source',
@@ -1948,7 +1948,7 @@
   # Tools used when building Chrome for Chrome OS. This affects both the Simple
   # Chrome workflow, as well as the chromeos-chrome ebuild.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '73e7b77d6878e58c3d26d5fe0d5877cf9ac44597',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '96096c3c83cbe537782e84dfc12981f146723f6a',
       'condition': 'checkout_chromeos',
   },
 
@@ -1990,7 +1990,7 @@
     Var('chromium_git') + '/external/github.com/jk-jeon/dragonbox.git' + '@' + '6c7c925b571d54486b9ffae8d9d18a822801cbda',
 
   'src/third_party/eigen3/src':
-    Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + '464c1d097891a1462ab28bf8bb763c1683883892',
+    Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + '729443409942a1816ddf74b95224003b83f4925c',
 
   'src/third_party/emoji-metadata/src': {
     'url': Var('chromium_git') + '/external/github.com/googlefonts/emoji-metadata' + '@' + '045f146fca682a836e01cd265171312bfb300e06',
@@ -2827,7 +2827,7 @@
     Var('chromium_git') + '/external/github.com/GoogleChromeLabs/text-fragments-polyfill.git' + '@' + 'c036420683f672d685e27415de0a5f5e85bdc23f',
 
   'src/third_party/tflite/src':
-    Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + '829a168408c4422b4290eba063287542a608382e',
+    Var('chromium_git') + '/external/github.com/tensorflow/tensorflow.git' + '@' + 'c4aa8ff91256b43fe0cecc246c77a00e2145b3bd',
 
   'src/third_party/turbine/cipd': {
       'packages': [
@@ -2840,16 +2840,16 @@
       'dep_type': 'cipd',
   },
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@96793fb0ff6fb5d4328cc6f71d84f5cb2d835daf',
-  'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@fc9889c889561c5882e83819dcaffef5ed45529b',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@0b7863549d960381696d3cd7485ac6a284122e15',
+  'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@963588074b26326ff0426c8953c1235213309bdb',
   'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3',
-  'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@bab63ff679c41eb75fc67dac76e1dc44426101e1',
-  'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@8e9165a3d162967a424dcf2ff645a98b50381cce',
-  'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@e2e53a724677f6eba8ff0ce1ccb64ee321785cbd',
-  'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@fb78607414e154c7a5c01b23177ba719c8a44909',
-  'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@0b8196724e4ad28cc7459b82a9b75f252c08cb3e',
-  'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@4e246c56ec5afb5ad66b9b04374d39ac04675c8e',
-  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@cea6ec1cdd37494c1f0fc5619c6c356ac33372fb',
+  'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@6d0784e9f1ab92c17eeea94821b2465c14a52be9',
+  'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@e8864edbebe9fb9872c6c95b2363b490c6105a15',
+  'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@9c77de5c3dd216f28e407eec65ed9c0a296c1f74',
+  'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@fefd7ed96ef9994f0080dbd078822b07d8637918',
+  'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@ba13d38d06830f714a93c5bb159e6e4bacacf0bc',
+  'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@be40e67892c83d4752ccfbee7ce690ea88087d2b',
+  'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@17cf8188d8b49bafb6c2f8ecede238f8d2dec5bf',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '56300b29fbfcc693ee6609ddad3fdd5b7a449a21',
@@ -2888,13 +2888,13 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'c01b768bce4a143e152c1870b6ba99ea6267d2b0',
 
   'src/third_party/webgpu-cts/src':
-    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'e5e17d8bb639438e1c99cbf367928a242c3bdef6',
+    Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '8b48ab0e88b1c6716598ffa1218783acb0691771',
 
   'src/third_party/webpagereplay':
     Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'd69d0808c37b4ea338e59a58837ef180023316a6',
+    Var('webrtc_git') + '/src.git' + '@' + '8ae8263ecc04b6fd78886e9906194e71a574f88c',
 
   # 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.
@@ -2918,7 +2918,7 @@
   },
 
   'src/third_party/xnnpack/src':
-    Var('chromium_git') + '/external/github.com/google/XNNPACK.git' + '@' + '05eec89fee573a08d841fbaf11db0357586fc6ae',
+    Var('chromium_git') + '/external/github.com/google/XNNPACK.git' + '@' + '1b2beba83092bed68775b6e2433596627988c74b',
 
   'src/third_party/libei/cipd': {
 
@@ -3016,7 +3016,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/boca_app/app',
-        'version': 'vZXDAVTh-nB3mXxHO9THfBFo8DrgCW6mY-5f1Emi_ukC',
+        'version': 'N1iPTYt5ijzkWmHZWIguGMVPVXU1jBWJLdMHT6fvAj0C',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3038,7 +3038,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/media_app/app',
-        'version': 'f-zzVreu26KWGyDpux6WAgj31DoLpwAdHVhSl2LBEDQC',
+        'version': 'taLuHtO78kWltYP_ZiTnuCBWONLuhnxQmxNbAQWuQEQC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -4591,7 +4591,7 @@
 
   'src/components/autofill/core/browser/form_parsing/internal_resources': {
       'url': Var('chrome_git') + '/chrome/components/autofill_regex_patterns.git' + '@' +
-        'ee4b7f29de55d7867a004208580a994e5da35ed0',
+        '3fb8783d75611c5ada2ee094ad2be8331e9bd233',
       'condition': 'checkout_src_internal',
   },
 
@@ -4614,7 +4614,7 @@
 
   'src/components/optimization_guide/internal': {
       'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' +
-        'd9be8fda5a8b047287677a35f2b3a6111f5883be',
+        '448e864469af5010a1dbb83b15d0cbfa1dcb5f55',
       'condition': 'checkout_src_internal',
   },
 
@@ -4680,7 +4680,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '6753a19d2e211ea27988a2603073395d0469a202',
+        '8fbb4c8734fa75050d85b613666e0d87d27e061e',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc
index cab4c11f..b749005 100644
--- a/android_webview/browser/aw_content_browser_client.cc
+++ b/android_webview/browser/aw_content_browser_client.cc
@@ -674,8 +674,10 @@
     // NavigationThrottles that don't delay or cancel navigations (e.g.
     // throttles that are only observing callbacks without affecting navigation
     // behavior) should be added before MetricsNavigationThrottle.
-    registry.AddThrottle(page_load_metrics::MetricsNavigationThrottle::Create(
-        &navigation_handle));
+    // TODO(https://crbug.com/412524375): This assumption is fragile. This
+    // should be cared by adding an attribute flag to
+    // NavigationThrottleRegistry::AddThrottle().
+    page_load_metrics::MetricsNavigationThrottle::CreateAndAdd(registry);
   }
   // Use Synchronous mode for the navigation interceptor, since this class
   // doesn't actually call into an arbitrary client, it just posts a task to
diff --git a/android_webview/browser/aw_settings.cc b/android_webview/browser/aw_settings.cc
index 94d9a5f..2b68416 100644
--- a/android_webview/browser/aw_settings.cc
+++ b/android_webview/browser/aw_settings.cc
@@ -664,9 +664,6 @@
   web_prefs->local_storage_enabled =
       Java_AwSettings_getDomStorageEnabledLocked(env, obj);
 
-  web_prefs->databases_enabled =
-      Java_AwSettings_getDatabaseEnabledLocked(env, obj);
-
   web_prefs->wide_viewport_quirk = true;
   web_prefs->use_wide_viewport =
       Java_AwSettings_getUseWideViewportLocked(env, obj);
diff --git a/android_webview/common/aw_switches.cc b/android_webview/common/aw_switches.cc
index 8260e5f..9f75931 100644
--- a/android_webview/common/aw_switches.cc
+++ b/android_webview/common/aw_switches.cc
@@ -86,6 +86,11 @@
 // updater downloading service in nonembedded WebView.
 const char kWebViewFpsComponent[] = "webview-fps-component";
 
+// Enables downloading MaskedDomainListComponentInstallerPolicy by the component
+// updater downloading service in nonembedded WebView.
+const char kWebViewMaskedDomainListComponent[] =
+    "webview-masked-domain-list-component";
+
 // Force disables 3rd party cookie for all apps.
 const char kWebViewForceDisable3pcs[] = "webview-force-disable-3pcs";
 
diff --git a/android_webview/common/aw_switches.h b/android_webview/common/aw_switches.h
index fbab4d82..24539fa 100644
--- a/android_webview/common/aw_switches.h
+++ b/android_webview/common/aw_switches.h
@@ -25,6 +25,7 @@
 extern const char kWebViewEnableTrustTokensComponent[];
 extern const char kWebViewTpcdMetadaComponent[];
 extern const char kWebViewFpsComponent[];
+extern const char kWebViewMaskedDomainListComponent[];
 extern const char kWebViewForceDisable3pcs[];
 extern const char kWebViewForceCrashJava[];
 extern const char kWebViewForceCrashNative[];
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java
index 54bf4bf0..724b1a62 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/ContentSettingsAdapter.java
@@ -712,11 +712,7 @@
 
     @Override
     public synchronized void setDatabaseEnabled(boolean flag) {
-        try (TraceEvent event =
-                TraceEvent.scoped("WebView.APICall.Framework.WEB_SETTINGS_SET_DATABASE_ENABLED")) {
-            WebViewChromium.recordWebViewApiCall(ApiCall.WEB_SETTINGS_SET_DATABASE_ENABLED);
-            mAwSettings.setDatabaseEnabled(flag);
-        }
+        // Intentional no-op.
     }
 
     @Override
@@ -747,11 +743,8 @@
 
     @Override
     public synchronized boolean getDatabaseEnabled() {
-        try (TraceEvent event =
-                TraceEvent.scoped("WebView.APICall.Framework.WEB_SETTINGS_GET_DATABASE_ENABLED")) {
-            WebViewChromium.recordWebViewApiCall(ApiCall.WEB_SETTINGS_GET_DATABASE_ENABLED);
-            return mAwSettings.getDatabaseEnabled();
-        }
+        // Intentional no-op.
+        return false;
     }
 
     @Override
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 06605b7..88de490 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
@@ -426,11 +426,12 @@
                 () -> {
                     AwBrowserProcess.initializeMetricsLogUploader();
 
-                    RecordHistogram.recordSparseHistogram(
-                            "Android.WebView.TargetSdkVersion",
+                    int targetSdkVersion =
                             ContextUtils.getApplicationContext()
                                     .getApplicationInfo()
-                                    .targetSdkVersion);
+                                    .targetSdkVersion;
+                    RecordHistogram.recordSparseHistogram(
+                            "Android.WebView.TargetSdkVersion", targetSdkVersion);
 
                     try (ScopedSysTraceEvent e =
                             ScopedSysTraceEvent.scoped(
@@ -445,7 +446,7 @@
 
                     if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
                             ? CompatChanges.isChangeEnabled(WebSettings.ENABLE_SIMPLIFIED_DARK_MODE)
-                            : BuildInfo.targetsAtLeastT()) {
+                            : targetSdkVersion >= Build.VERSION_CODES.TIRAMISU) {
                         AwDarkMode.enableSimplifiedDarkMode();
                     }
 
diff --git a/android_webview/java/src/org/chromium/android_webview/AwSettings.java b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
index 197004b..3d2ab0f5 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwSettings.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwSettings.java
@@ -161,7 +161,6 @@
     private boolean mJavaScriptCanOpenWindowsAutomatically;
     private boolean mSupportMultipleWindows;
     private boolean mDomStorageEnabled;
-    private boolean mDatabaseEnabled;
     private boolean mUseWideViewport;
     private boolean mZeroLayoutHeightDisablesViewportQuirk;
     private boolean mForceZeroLayoutHeight;
@@ -1591,30 +1590,6 @@
         return mDomStorageEnabled;
     }
 
-    /** See {@link android.webkit.WebSettings#setDatabaseEnabled}. */
-    public void setDatabaseEnabled(boolean flag) {
-        if (TRACE) Log.i(TAG, "setDatabaseEnabled=" + flag);
-        synchronized (mAwSettingsLock) {
-            if (mDatabaseEnabled != flag) {
-                mDatabaseEnabled = flag;
-                mEventHandler.updateWebkitPreferencesLocked();
-            }
-        }
-    }
-
-    /** See {@link android.webkit.WebSettings#getDatabaseEnabled}. */
-    public boolean getDatabaseEnabled() {
-        synchronized (mAwSettingsLock) {
-            return mDatabaseEnabled;
-        }
-    }
-
-    @CalledByNative
-    private boolean getDatabaseEnabledLocked() {
-        assert Thread.holdsLock(mAwSettingsLock);
-        return mDatabaseEnabled;
-    }
-
     /** See {@link android.webkit.WebSettings#setDefaultTextEncodingName}. */
     public void setDefaultTextEncodingName(String encoding) {
         if (TRACE) Log.i(TAG, "setDefaultTextEncodingName=" + encoding);
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 a6506ca..81dc59d 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
@@ -942,9 +942,6 @@
         Flag.baseFeature(
                 "AllowSensorsToEnterBfcache",
                 "Allow pages with sensors to enter back/forward cache."),
-        Flag.baseFeature(
-                BlinkFeatures.FONTATIONS_FONT_BACKEND,
-                "Enables the Fontations font backend for web fonts."),
         Flag.baseFeature("OverrideAPIKey"),
         Flag.baseFeature(
                 "RustyPng", "When enabled, uses Rust `png` crate to decode and encode PNG images."),
@@ -1056,6 +1053,7 @@
                 "Enable the <link blocking=\"full-frame-rate\"/> API to lower the frame rate during"
                         + " loading"),
         Flag.baseFeature("ProgressiveAccessibility"),
+        Flag.baseFeature("PreloadingNoSamePageFragmentAnchorTracking"),
         // 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/AwBackForwardCacheTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java
index 2a7e62e..2626f64 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwBackForwardCacheTest.java
@@ -516,8 +516,6 @@
                 () -> settings.setForceZeroLayoutHeight(!settings.getForceZeroLayoutHeight()));
         verifyPageEvictedWithSettingsChange(
                 () -> settings.setDomStorageEnabled(!settings.getDomStorageEnabled()));
-        verifyPageEvictedWithSettingsChange(
-                () -> settings.setDatabaseEnabled(!settings.getDatabaseEnabled()));
         verifyPageEvictedWithSettingsChange(() -> settings.setDefaultTextEncodingName("Latin-1"));
         verifyPageEvictedWithSettingsChange(
                 () -> {
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwParameterizedTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwParameterizedTest.java
index c786b837..e75b7a9 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/AwParameterizedTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwParameterizedTest.java
@@ -30,7 +30,6 @@
                         settings.setAllowFileAccessFromFileUrls(true);
                         settings.setAllowUniversalAccessFromFileUrls(true);
                         settings.setBuiltInZoomControls(true);
-                        settings.setDatabaseEnabled(true);
                         settings.setDisplayZoomControls(false);
                         settings.setDomStorageEnabled(true);
                         settings.setImagesEnabled(false);
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/WebExposedTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/WebExposedTest.java
index 36db7e82..01e1f45 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/WebExposedTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/WebExposedTest.java
@@ -141,8 +141,6 @@
                     settings.setAllowFileAccessFromFileUrls(true);
                     settings.setJavaScriptEnabled(true);
 
-                    // Exposes window.openDatabase
-                    settings.setDatabaseEnabled(true);
                     // Exposes Payment APIs
                     settings.setPaymentRequestEnabled(true);
 
diff --git a/android_webview/nonembedded/component_updater/registration.cc b/android_webview/nonembedded/component_updater/registration.cc
index 25bd290..0f09d91 100644
--- a/android_webview/nonembedded/component_updater/registration.cc
+++ b/android_webview/nonembedded/component_updater/registration.cc
@@ -43,18 +43,27 @@
   component_installer_list.push_back(
       std::make_unique<
           component_updater::OriginTrialsComponentInstallerPolicy>());
-  component_installer_list.push_back(
-      std::make_unique<
-          component_updater::MaskedDomainListComponentInstallerPolicy>(
-          /*on_list_ready=*/base::BindRepeating(
-              [](base::Version version,
-                 std::optional<mojo_base::ProtoWrapper> masked_domain_list) {
-                if (masked_domain_list.has_value()) {
-                  VLOG(1) << "Received Masked Domain List version " << version;
-                } else {
-                  LOG(ERROR) << "Could not read Masked Domain List file";
-                }
-              })));
+
+  // Note: We're using a command-line switch because finch features
+  // isn't supported in nonembedded WebView.
+  // After setting this flag, it may be necessary to force restart the
+  // non-embedded process.
+  if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kWebViewMaskedDomainListComponent)) {
+    component_installer_list.push_back(
+        std::make_unique<
+            component_updater::MaskedDomainListComponentInstallerPolicy>(
+            /*on_list_ready=*/base::BindRepeating(
+                [](base::Version version,
+                   std::optional<mojo_base::ProtoWrapper> masked_domain_list) {
+                  if (masked_domain_list.has_value()) {
+                    VLOG(1)
+                        << "Received Masked Domain List version " << version;
+                  } else {
+                    LOG(ERROR) << "Could not read Masked Domain List file";
+                  }
+                })));
+  }
 
   // Note: We're using a command-line switch because finch features
   // isn't supported in nonembedded WebView.
diff --git a/android_webview/tools/run_cts.pydeps b/android_webview/tools/run_cts.pydeps
index a80607f7..9f26c3bf 100644
--- a/android_webview/tools/run_cts.pydeps
+++ b/android_webview/tools/run_cts.pydeps
@@ -74,7 +74,6 @@
 //third_party/catapult/devil/devil/devil_env.py
 //third_party/catapult/devil/devil/utils/__init__.py
 //third_party/catapult/devil/devil/utils/cmd_helper.py
-//third_party/catapult/devil/devil/utils/host_utils.py
 //third_party/catapult/devil/devil/utils/lazy/__init__.py
 //third_party/catapult/devil/devil/utils/lazy/weak_constant.py
 //third_party/catapult/devil/devil/utils/logging_common.py
diff --git a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
index 01fcc52..f0ed2ee 100644
--- a/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
+++ b/android_webview/tools/system_webview_shell/apk/src/org/chromium/webview_shell/WebViewBrowserActivity.java
@@ -34,7 +34,6 @@
 import androidx.webkit.WebViewCompat;
 import androidx.webkit.WebViewFeature;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.PackageManagerUtils;
@@ -114,8 +113,7 @@
         if (!WebViewFeature.isFeatureSupported(WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE)) {
             menu.findItem(R.id.menu_enable_tracing).setEnabled(false);
         }
-        if (!WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)
-                || BuildInfo.targetsAtLeastT()) {
+        if (!WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
             menu.findItem(R.id.menu_force_dark_off).setEnabled(false);
             menu.findItem(R.id.menu_force_dark_auto).setEnabled(false);
             menu.findItem(R.id.menu_force_dark_on).setEnabled(false);
@@ -126,8 +124,7 @@
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
             menu.findItem(R.id.menu_night_mode_on).setEnabled(false);
         }
-        if (!BuildInfo.targetsAtLeastT()
-                || !WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
+        if (!WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
             menu.findItem(R.id.menu_algorithmic_darkening_on).setEnabled(false);
         }
         return super.onCreateOptionsMenu(menu);
@@ -142,8 +139,7 @@
         } else {
             menu.findItem(R.id.menu_enable_tracing).setEnabled(false);
         }
-        if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)
-                && !BuildInfo.targetsAtLeastT()) {
+        if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
             int forceDarkState = WebSettingsCompat.getForceDark(mWebView.getSettings());
             switch (forceDarkState) {
                 case WebSettingsCompat.FORCE_DARK_OFF:
@@ -171,8 +167,7 @@
             }
             menu.findItem(R.id.menu_night_mode_on).setChecked(checked);
         }
-        if (BuildInfo.targetsAtLeastT()
-                && WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
+        if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) {
             menu.findItem(R.id.menu_algorithmic_darkening_on)
                     .setChecked(
                             WebSettingsCompat.isAlgorithmicDarkeningAllowed(
@@ -232,10 +227,10 @@
                     try (StrictModeContext ignored = StrictModeContext.allowDiskWrites()) {
                         String outFileName = getFilesDir() + "/webview_tracing.json";
                         try {
+                            mIsStoppingTracing = true;
                             tracingController.stop(
                                     new TracingLogger(outFileName, this),
                                     Executors.newSingleThreadExecutor());
-                            mIsStoppingTracing = true;
                         } catch (FileNotFoundException e) {
                             throw new RuntimeException(e);
                         }
@@ -422,19 +417,16 @@
         @Override
         public void close() throws IOException {
             super.close();
-            showDialog(mByteCount);
-            mIsStoppingTracing = false;
-        }
 
-        private void showDialog(long nbBytes) {
             StringBuilder info = new StringBuilder();
             info.append("Tracing data written to file\n");
-            info.append("number of bytes: " + nbBytes);
+            info.append("number of bytes: " + mByteCount);
 
             mActivity.runOnUiThread(
                     new Runnable() {
                         @Override
                         public void run() {
+                            mIsStoppingTracing = false;
                             AlertDialog dialog =
                                     new AlertDialog.Builder(mActivity)
                                             .setTitle("Tracing API")
diff --git a/ash/controls/contextual_nudge.cc b/ash/controls/contextual_nudge.cc
index 6e9c08c6..08d072f6f 100644
--- a/ash/controls/contextual_nudge.cc
+++ b/ash/controls/contextual_nudge.cc
@@ -50,7 +50,7 @@
   // Bubbles that use transparent colors should not paint their ClientViews to a
   // layer as doing so could result in visual artifacts.
   SetPaintClientToLayer(false);
-  set_background_color(SK_ColorTRANSPARENT);
+  SetBackgroundColor(SK_ColorTRANSPARENT);
   set_close_on_deactivate(false);
   set_margins(gfx::Insets());
   set_accept_events(!tap_callback.is_null());
diff --git a/ash/game_dashboard/game_dashboard_main_menu_view.cc b/ash/game_dashboard/game_dashboard_main_menu_view.cc
index 76252d1b..2fde27e1 100644
--- a/ash/game_dashboard/game_dashboard_main_menu_view.cc
+++ b/ash/game_dashboard/game_dashboard_main_menu_view.cc
@@ -814,7 +814,7 @@
     : context_(context) {
   DCHECK(context_);
   DCHECK(context_->game_dashboard_button_widget());
-  set_background_color(cros_tokens::kCrosSysSystemBaseElevatedOpaque);
+  SetBackgroundColor(cros_tokens::kCrosSysSystemBaseElevatedOpaque);
   SetBorder(views::CreateRoundedRectBorder(
       /*thickness=*/1, kBubbleCornerRadius,
       cros_tokens::kCrosSysSystemHighlight1));
diff --git a/ash/quick_insert/views/quick_insert_preview_bubble.cc b/ash/quick_insert/views/quick_insert_preview_bubble.cc
index 1f6a4ac..2ae58bc 100644
--- a/ash/quick_insert/views/quick_insert_preview_bubble.cc
+++ b/ash/quick_insert/views/quick_insert_preview_bubble.cc
@@ -32,8 +32,6 @@
 namespace ash {
 namespace {
 
-constexpr ui::ColorId kBackgroundColor =
-    cros_tokens::kCrosSysSystemBaseElevatedOpaque;
 constexpr int kBubbleOverlapOverPicker = 4;
 constexpr int kQuickInsertBubbleCornerRadius = 12;
 constexpr gfx::Insets kMargins(8);
@@ -76,7 +74,7 @@
                                views::BubbleBorder::LEFT_CENTER,
                                views::BubbleBorder::STANDARD_SHADOW,
                                /*autosize=*/true) {
-  set_background_color(kBackgroundColor);
+  SetBackgroundColor(cros_tokens::kCrosSysSystemBaseElevatedOpaque);
 
   // Configuration for this view.
   SetLayoutManager(
diff --git a/ash/shelf/shelf_bubble.cc b/ash/shelf/shelf_bubble.cc
index 5ee893a4..021b69f 100644
--- a/ash/shelf/shelf_bubble.cc
+++ b/ash/shelf/shelf_bubble.cc
@@ -67,7 +67,7 @@
           anchor,
           arrow_position.value_or(GetArrow(alignment))),
       for_tooltip_(for_tooltip) {
-  set_background_color(SK_ColorTRANSPARENT);
+  SetBackgroundColor(SK_ColorTRANSPARENT);
 
   // Bubbles that use transparent colors should not paint their ClientViews to a
   // layer as doing so could result in visual artifacts.
diff --git a/ash/shelf/shelf_shutdown_confirmation_bubble.cc b/ash/shelf/shelf_shutdown_confirmation_bubble.cc
index bea7d87..e39df7e8 100644
--- a/ash/shelf/shelf_shutdown_confirmation_bubble.cc
+++ b/ash/shelf/shelf_shutdown_confirmation_bubble.cc
@@ -191,7 +191,7 @@
       AshColorProvider::ContentLayerType::kButtonLabelColor);
   cancel_->SetEnabledTextColors(button_color);
   confirm_->SetEnabledTextColors(button_color);
-  set_background_color(ShelfConfig::Get()->GetDefaultShelfColor(GetWidget()));
+  SetBackgroundColor(ShelfConfig::Get()->GetDefaultShelfColor(GetWidget()));
 }
 
 std::u16string ShelfShutdownConfirmationBubble::GetAccessibleWindowTitle()
diff --git a/ash/system/accessibility/facegaze_bubble_view.cc b/ash/system/accessibility/facegaze_bubble_view.cc
index aecc216..1a70348 100644
--- a/ash/system/accessibility/facegaze_bubble_view.cc
+++ b/ash/system/accessibility/facegaze_bubble_view.cc
@@ -80,7 +80,7 @@
     const base::RepeatingCallback<void()>& on_mouse_entered,
     const base::RepeatingCallback<void(const ui::Event& event)>&
         on_close_button_clicked) {
-  set_background_color(kBackgroundColorId);
+  SetBackgroundColor(kBackgroundColorId);
   set_parent_window(
       Shell::GetContainer(Shell::GetPrimaryRootWindow(),
                           kShellWindowId_AccessibilityBubbleContainer));
@@ -120,19 +120,7 @@
 }
 
 void FaceGazeBubbleView::UpdateColor(bool is_warning) {
-  ui::ColorId background_color_id =
-      is_warning ? kWarningBackgroundColor : kBackgroundColorId;
-  SkColor background_color = GetColorProvider()->GetColor(background_color_id);
-
-  set_background_color(background_color_id);
-  View* const contents_view = GetContentsView();
-  DCHECK(contents_view);
-  contents_view->SetBackground(
-      (views::CreateSolidBackground(background_color)));
-  views::BubbleFrameView* frame_view = GetBubbleFrameView();
-  if (frame_view) {
-    frame_view->SetBackgroundColor(background_color);
-  }
+  SetBackgroundColor(is_warning ? kWarningBackgroundColor : kBackgroundColorId);
 }
 
 std::u16string_view FaceGazeBubbleView::GetTextForTesting() const {
@@ -178,7 +166,7 @@
       is_warning ? kWarningForegroundColor : kColorAshTextColorPrimary;
   image_->SetImage(ui::ImageModel::FromVectorIcon(
       kFacegazeIcon, foreground_color, kIconSizeDip));
-  label_->SetEnabledColor(GetColorProvider()->GetColor(foreground_color));
+  label_->SetEnabledColor(foreground_color);
 }
 
 BEGIN_METADATA(FaceGazeBubbleMainContentView)
diff --git a/ash/system/toast/anchored_nudge.cc b/ash/system/toast/anchored_nudge.cc
index a958d8db..9e35355 100644
--- a/ash/system/toast/anchored_nudge.cc
+++ b/ash/system/toast/anchored_nudge.cc
@@ -119,7 +119,7 @@
       click_callback_(std::move(nudge_data.click_callback)),
       dismiss_callback_(std::move(nudge_data.dismiss_callback)) {
   SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone));
-  set_background_color(SK_ColorTRANSPARENT);
+  SetBackgroundColor(SK_ColorTRANSPARENT);
   set_margins(gfx::Insets());
   set_close_on_deactivate(false);
   set_highlight_button_when_shown(nudge_data.highlight_anchor_button);
diff --git a/ash/system/tray/tray_bubble_view.cc b/ash/system/tray/tray_bubble_view.cc
index 94dc07a..3802dd6 100644
--- a/ash/system/tray/tray_bubble_view.cc
+++ b/ash/system/tray/tray_bubble_view.cc
@@ -356,7 +356,7 @@
         gfx::RoundedCornersF{static_cast<float>(params_.corner_radius)});
     layer()->SetIsFastRoundedCorner(true);
 
-    set_background_color(cros_tokens::kCrosSysSystemBaseElevatedOpaque);
+    SetBackgroundColor(cros_tokens::kCrosSysSystemBaseElevatedOpaque);
     SetBorder(std::make_unique<views::HighlightBorder>(
         params_.corner_radius,
         views::HighlightBorder::Type::kHighlightBorderOnShadow));
@@ -364,7 +364,7 @@
 
   if (init_params.translucent && chromeos::features::IsSystemBlurEnabled()) {
     CHECK(!init_params.transparent);
-    set_background_color(cros_tokens::kCrosSysSystemBaseElevated);
+    SetBackgroundColor(cros_tokens::kCrosSysSystemBaseElevated);
     layer()->SetFillsBoundsOpaquely(false);
     layer()->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
     layer()->SetBackdropFilterQuality(ColorProvider::kBackgroundBlurQuality);
diff --git a/ash/user_education/views/help_bubble_view_ash.cc b/ash/user_education/views/help_bubble_view_ash.cc
index d2b29c96..9d1d280 100644
--- a/ash/user_education/views/help_bubble_view_ash.cc
+++ b/ash/user_education/views/help_bubble_view_ash.cc
@@ -282,7 +282,7 @@
                                TranslateArrow(params.arrow),
                                views::BubbleBorder::STANDARD_SHADOW),
       id_(id) {
-  set_background_color(cros_tokens::kCrosSysDialogContainer);
+  SetBackgroundColor(cros_tokens::kCrosSysDialogContainer);
   SetCanActivate(true);
 
   // When hosted within a `views::ScrollView`, the anchor view may be
diff --git a/ash/webui/shimless_rma/backend/shimless_rma_service.cc b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
index fa100a7..baf806f 100644
--- a/ash/webui/shimless_rma/backend/shimless_rma_service.cc
+++ b/ash/webui/shimless_rma/backend/shimless_rma_service.cc
@@ -1609,7 +1609,7 @@
 void ShimlessRmaService::InstallLastFound3pDiagnosticsApp(
     InstallLastFound3pDiagnosticsAppCallback callback) {
   if (extracted_3p_diag_swbn_path_.empty() ||
-      extracted_3p_diag_swbn_path_.empty()) {
+      extracted_3p_diag_crx_path_.empty()) {
     LOG(ERROR) << "Should call GetInstallable3pDiagnosticsAppPath first";
     std::move(callback).Run(nullptr);
     return;
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 3b5e3da6..34f4bc9a 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -5555,7 +5555,6 @@
       "test/android/junit/src/org/chromium/base/test/BaseRobolectricTestListener.java",
       "test/android/junit/src/org/chromium/base/test/BaseRobolectricTestRule.java",
       "test/android/junit/src/org/chromium/base/test/BaseRobolectricTestRunner.java",
-      "test/android/junit/src/org/chromium/base/test/ShadowBuildInfo.java",
       "test/android/junit/src/org/chromium/base/test/util/BaseFlagTestRule.java",
       "test/android/junit/src/org/chromium/base/test/util/TestRunnerTestRule.java",
     ]
diff --git a/base/android/android_info.cc b/base/android/android_info.cc
index 99d736d..2b745c7f 100644
--- a/base/android/android_info.cc
+++ b/base/android/android_info.cc
@@ -53,15 +53,11 @@
 
   const char* hardware;
 
-  bool is_at_least_u;
-
   const char* codename;
 
   // Available only on android S+. For S-, this method returns empty string.
   const char* soc_manufacturer;
 
-  bool is_at_least_t;
-
   const char* abi_name;
 };
 
@@ -96,9 +92,7 @@
     const jni_zero::JavaParamRef<jstring>& socManufacturer,
     const jni_zero::JavaParamRef<jstring>& supportedAbis,
     jint sdkInt,
-    jboolean isDebugAndroid,
-    jboolean isAtleastU,
-    jboolean isAtleastT) {
+    jboolean isDebugAndroid) {
   DCHECK(!holder.has_value());
   auto java_string_to_const_char =
       [](const jni_zero::JavaParamRef<jstring>& str) {
@@ -117,12 +111,9 @@
       .is_debug_android = static_cast<bool>(isDebugAndroid),
       .version_incremental = java_string_to_const_char(versionIncremental),
       .hardware = java_string_to_const_char(hardware),
-      .is_at_least_u = static_cast<bool>(isAtleastU),
       .codename = java_string_to_const_char(codeName),
       .soc_manufacturer = java_string_to_const_char(socManufacturer),
-      .is_at_least_t = static_cast<bool>(isAtleastT),
-      .abi_name = java_string_to_const_char(supportedAbis),
-  };
+      .abi_name = java_string_to_const_char(supportedAbis)};
 }
 
 const char* device() {
@@ -173,10 +164,6 @@
   return get_android_info().hardware;
 }
 
-bool is_at_least_u() {
-  return get_android_info().is_at_least_u;
-}
-
 const char* codename() {
   return get_android_info().codename;
 }
@@ -186,10 +173,6 @@
   return get_android_info().soc_manufacturer;
 }
 
-bool is_at_least_t() {
-  return get_android_info().is_at_least_t;
-}
-
 const char* abi_name() {
   return get_android_info().abi_name;
 }
diff --git a/base/android/android_info.h b/base/android/android_info.h
index fc5da27..556b13e 100644
--- a/base/android/android_info.h
+++ b/base/android/android_info.h
@@ -59,15 +59,11 @@
 
 BASE_EXPORT const char* hardware();
 
-bool is_at_least_u();
-
 const char* codename();
 
 // Available only on android S+. For S-, this method returns empty string.
 const char* soc_manufacturer();
 
-bool is_at_least_t();
-
 const char* abi_name();
 
 }  // namespace base::android::android_info
diff --git a/base/android/apk_info.cc b/base/android/apk_info.cc
index f9e629e8..84527af9 100644
--- a/base/android/apk_info.cc
+++ b/base/android/apk_info.cc
@@ -35,7 +35,6 @@
   const char* resources_version;
   const char* installer_package_name;
   bool is_debug_app;
-  bool targets_at_least_u;
   int target_sdk_version;
 };
 
@@ -65,7 +64,6 @@
     const jni_zero::JavaParamRef<jstring>& resourcesVersion,
     const jni_zero::JavaParamRef<jstring>& installerPackageName,
     jboolean isDebugApp,
-    jboolean targetsAtleastU,
     jint targetSdkVersion) {
   DCHECK(!holder.has_value());
   auto java_string_to_const_char =
@@ -82,7 +80,6 @@
       .resources_version = java_string_to_const_char(resourcesVersion),
       .installer_package_name = java_string_to_const_char(installerPackageName),
       .is_debug_app = static_cast<bool>(isDebugApp),
-      .targets_at_least_u = static_cast<bool>(targetsAtleastU),
       .target_sdk_version = targetSdkVersion};
 }
 
@@ -126,7 +123,4 @@
   return get_apk_info().target_sdk_version;
 }
 
-bool targets_at_least_u() {
-  return get_apk_info().targets_at_least_u;
-}
 }  // namespace base::android::apk_info
diff --git a/base/android/build_info.cc b/base/android/build_info.cc
index 6d6ba20..8acac8d 100644
--- a/base/android/build_info.cc
+++ b/base/android/build_info.cc
@@ -62,10 +62,7 @@
       is_tv_(device_info::is_tv()),
       version_incremental_(android_info::version_incremental()),
       hardware_(android_info::hardware()),
-      is_at_least_t_(android_info::is_at_least_t()),
       is_automotive_(device_info::is_automotive()),
-      is_at_least_u_(android_info::is_at_least_u()),
-      targets_at_least_u_(apk_info::targets_at_least_u()),
       codename_(android_info::codename()),
       vulkan_deqp_level_(device_info::vulkan_deqp_level()),
       is_foldable_(device_info::is_foldable()),
diff --git a/base/android/build_info.h b/base/android/build_info.h
index 93ab2104..b58b2bd6 100644
--- a/base/android/build_info.h
+++ b/base/android/build_info.h
@@ -137,14 +137,8 @@
 
   const char* hardware() const { return hardware_; }
 
-  bool is_at_least_t() const { return is_at_least_t_; }
-
   bool is_automotive() const { return is_automotive_; }
 
-  bool is_at_least_u() const { return is_at_least_u_; }
-
-  bool targets_at_least_u() const { return targets_at_least_u_; }
-
   const char* codename() const { return codename_; }
 
   bool is_foldable() const { return is_foldable_; }
@@ -192,10 +186,7 @@
   const bool is_tv_;
   const char* const version_incremental_;
   const char* const hardware_;
-  const bool is_at_least_t_;
   const bool is_automotive_;
-  const bool is_at_least_u_;
-  const bool targets_at_least_u_;
   const char* const codename_;
   const int32_t vulkan_deqp_level_;
   const bool is_foldable_;
diff --git a/base/android/bundle_utils.cc b/base/android/bundle_utils.cc
index c0f024cd..c4b070a 100644
--- a/base/android/bundle_utils.cc
+++ b/base/android/bundle_utils.cc
@@ -41,9 +41,8 @@
 // contains the offset to add to the pointer, in order to find the actual
 // desired pointer address.
 //
-// # Safety
-// If the value in the pointer does not provide an offset from the pointer that
-// stays inside the same allocation, Undefined Behaviour can result.
+// PRECONDITIONS: The value in the pointer must provide an offset from the
+// pointer that stays inside the same allocation.
 UNSAFE_BUFFER_USAGE void* ReadRelPtr(int32_t* relptr) {
   // SAFETY: This relies on the caller to provide a valid pointer + value.
   return UNSAFE_BUFFERS(reinterpret_cast<char*>(relptr) + *relptr);
diff --git a/base/android/java/src/org/chromium/base/AndroidInfo.java b/base/android/java/src/org/chromium/base/AndroidInfo.java
index a9cd969..485dcf293 100644
--- a/base/android/java/src/org/chromium/base/AndroidInfo.java
+++ b/base/android/java/src/org/chromium/base/AndroidInfo.java
@@ -5,7 +5,6 @@
 package org.chromium.base;
 
 import android.os.Build;
-import android.os.Build.VERSION_CODES;
 import android.text.TextUtils;
 
 import org.jni_zero.CalledByNative;
@@ -45,9 +44,7 @@
                                 : "",
                         /* supportedAbis= */ TextUtils.join(", ", Build.SUPPORTED_ABIS),
                         /* sdkInt= */ Build.VERSION.SDK_INT,
-                        /* isDebugAndroid= */ isDebugAndroid(),
-                        /* isAtleastU= */ Build.VERSION.SDK_INT >= VERSION_CODES.UPSIDE_DOWN_CAKE,
-                        /* isAtleastT= */ Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU);
+                        /* isDebugAndroid= */ isDebugAndroid());
     }
 
     /* Truncated version of Build.FINGERPRINT (for crash reporting). */
@@ -86,8 +83,6 @@
                 String socManufacturer,
                 String supportedAbis,
                 int sdkInt,
-                boolean isDebugAndroid,
-                boolean isAtleastU,
-                boolean isAtleastT);
+                boolean isDebugAndroid);
     }
 }
diff --git a/base/android/java/src/org/chromium/base/ApkInfo.java b/base/android/java/src/org/chromium/base/ApkInfo.java
index cb020ced8..bb5ba01 100644
--- a/base/android/java/src/org/chromium/base/ApkInfo.java
+++ b/base/android/java/src/org/chromium/base/ApkInfo.java
@@ -10,7 +10,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.os.Build;
 import android.os.Process;
 
 import org.jni_zero.CalledByNative;
@@ -100,7 +99,6 @@
                         /* resourcesVersion= */ instance.mResourcesVersion,
                         /* installerPackageName= */ instance.mInstallerPackageName,
                         /* isDebugApp= */ isDebugApp(),
-                        /* targetsAtleastU= */ targetsAtLeastU(),
                         /* targetSdkVersion= */ ContextUtils.getApplicationContext()
                                 .getApplicationInfo()
                                 .targetSdkVersion);
@@ -139,17 +137,6 @@
     }
 
     /**
-     * Checks if the application targets pre-release SDK U. This must be manually maintained as the
-     * SDK goes through finalization! Avoid depending on this if possible; this is only intended for
-     * WebView.
-     */
-    public static boolean targetsAtLeastU() {
-        int target = ContextUtils.getApplicationContext().getApplicationInfo().targetSdkVersion;
-
-        return target >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
-    }
-
-    /**
      * Checks if the application targets pre-release SDK B. This must be manually maintained as the
      * SDK goes through finalization.
      */
@@ -344,7 +331,6 @@
                 String resourcesVersion,
                 String installerPackageName,
                 boolean isDebugApp,
-                boolean targetsAtleastU,
                 int targetSdkVersion);
     }
 }
diff --git a/base/android/java/src/org/chromium/base/BinderCallsListener.java b/base/android/java/src/org/chromium/base/BinderCallsListener.java
index 8349b17..55e6f881 100644
--- a/base/android/java/src/org/chromium/base/BinderCallsListener.java
+++ b/base/android/java/src/org/chromium/base/BinderCallsListener.java
@@ -36,6 +36,10 @@
     private static final String TAG = "BinderCallsListener";
     private static final String PROXY_TRANSACT_LISTENER_CLASS_NAME =
             "android.os.Binder$ProxyTransactListener";
+    private static final String NON_ANDROID_INTERFACE = "NON_ANDROID_INTERFACE";
+    private static final String EMPTY_INTERFACE = "EMPTY_INTERFACE";
+    private static final String NULL_INTERFACE = "NULL_INTERFACE";
+    private static final String UNKNOWN_INTERFACE = "UNKNOWN_INTERFACE";
 
     private static @Nullable BinderCallsListener sInstance;
 
@@ -277,13 +281,31 @@
     }
 
     private static class InterfaceInvocationHandler implements InvocationHandler {
-        private @Nullable String mCurrentInterfaceDescriptor;
+        private String mCurrentInterfaceDescriptor = EMPTY_INTERFACE;
         private @Nullable BiConsumer<String, String> mObserver;
         private int mCurrentTransactionId;
         private int mNumUploads;
         private long mTotalTimeSpentInBinderCallsMillis;
         private long mCurrentTransactionStartTimeMillis;
 
+        private static boolean isAndroidBinderInterface(String interfaceDescriptor) {
+            return (interfaceDescriptor.startsWith("com.android.")
+                            && !interfaceDescriptor.startsWith("com.android.vending"))
+                    || interfaceDescriptor.startsWith("android.");
+        }
+
+        private String getInterfaceDescriptor(IBinder binder) {
+            try {
+                String interfaceDescriptor = binder.getInterfaceDescriptor();
+                return interfaceDescriptor == null
+                        ? NULL_INTERFACE
+                        : (interfaceDescriptor.isEmpty() ? EMPTY_INTERFACE : interfaceDescriptor);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Unable to read interface descriptor.");
+            }
+            return UNKNOWN_INTERFACE;
+        }
+
         public long getTimeSpentInBinderCalls() {
             return mTotalTimeSpentInBinderCallsMillis;
         }
@@ -296,28 +318,25 @@
                     IBinder binder = (IBinder) args[0];
                     mCurrentTransactionId++;
                     mCurrentTransactionStartTimeMillis = SystemClock.uptimeMillis();
-                    try {
-                        mCurrentInterfaceDescriptor = binder.getInterfaceDescriptor();
-                    } catch (RemoteException e) {
+
+                    mCurrentInterfaceDescriptor = getInterfaceDescriptor(binder);
+                    // If we failed to read the interface descriptor, ignore it.
+                    if (mCurrentInterfaceDescriptor.equals(UNKNOWN_INTERFACE)) {
                         return null;
                     }
-                    if (mCurrentInterfaceDescriptor == null) return null;
+                    boolean shouldTrackBinderIpc =
+                            !sSlowBinderCallAllowList.contains(mCurrentInterfaceDescriptor);
+                    if (!isAndroidBinderInterface(mCurrentInterfaceDescriptor)) {
+                        mCurrentInterfaceDescriptor = NON_ANDROID_INTERFACE;
+                        shouldTrackBinderIpc = false;
+                    }
 
                     TraceEvent.begin("BinderCallsListener.invoke", mCurrentInterfaceDescriptor);
                     if (mObserver != null) {
                         mObserver.accept("onTransactStarted", mCurrentInterfaceDescriptor);
                     }
-                    // There are some Binder calls that don't have an interface descriptor (e.g.
-                    // https://crbug.com/407792383). Ignore these for now, but the empty string
-                    // check here could be the source of discrepancies in the future.
-                    boolean shouldTrackBinderIpc =
-                            !(mCurrentInterfaceDescriptor.equals("")
-                                    || sSlowBinderCallAllowList.contains(
-                                            mCurrentInterfaceDescriptor));
-                    if (shouldTrackBinderIpc) {
-                        return mCurrentTransactionId;
-                    }
-                    return null;
+
+                    return shouldTrackBinderIpc ? mCurrentTransactionId : null;
                 case "onTransactEnded":
                     TraceEvent.end("BinderCallsListener.invoke", mCurrentInterfaceDescriptor);
 
diff --git a/base/android/java/src/org/chromium/base/BuildInfo.java b/base/android/java/src/org/chromium/base/BuildInfo.java
index c45bcdeb..4b0686db 100644
--- a/base/android/java/src/org/chromium/base/BuildInfo.java
+++ b/base/android/java/src/org/chromium/base/BuildInfo.java
@@ -9,7 +9,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
 import android.os.Build;
-import android.os.Build.VERSION_CODES;
 import android.os.Process;
 
 import org.jni_zero.CalledByNative;
@@ -219,29 +218,6 @@
         return isDebugAndroid() || isDebugApp();
     }
 
-    /**
-     * Checks if the application targets the T SDK or later.
-     * @deprecated Chrome callers should just remove this test - Chrome targets T or later now.
-     * WebView callers should just inline the logic below to check the target level of the embedding
-     * App when necessary.
-     */
-    @Deprecated
-    public static boolean targetsAtLeastT() {
-        int target = ContextUtils.getApplicationContext().getApplicationInfo().targetSdkVersion;
-
-        // Now that the public SDK is upstreamed we can use the defined constant.
-        return target >= VERSION_CODES.TIRAMISU;
-    }
-
-    /**
-     * Checks if the application targets pre-release SDK U. This must be manually maintained as the
-     * SDK goes through finalization! Avoid depending on this if possible; this is only intended for
-     * WebView.
-     */
-    public static boolean targetsAtLeastU() {
-        return ApkInfo.targetsAtLeastU();
-    }
-
     @NullUnmarked
     public String getHostSigningCertSha256() {
         // We currently only make use of this certificate for calls from the storage access API
diff --git a/base/android/jni_array.cc b/base/android/jni_array.cc
index a53ad8b..69ae041 100644
--- a/base/android/jni_array.cc
+++ b/base/android/jni_array.cc
@@ -13,11 +13,12 @@
 
 namespace base::android {
 
-UNSAFE_BUFFER_USAGE ScopedJavaLocalRef<jbyteArray>
-ToJavaByteArray(JNIEnv* env, const uint8_t* bytes, size_t len) {
+ScopedJavaLocalRef<jbyteArray> ToJavaByteArray(JNIEnv* env,
+                                               const uint8_t* bytes,
+                                               size_t len) {
   return ToJavaByteArray(
       env,
-      // SAFETY: The caller must provide a valid pointer and length.
+      // SAFETY: required from caller, see UNSAFE_BUFFER_USAGE in header.
       UNSAFE_BUFFERS(base::span(bytes, len)));
 }
 
diff --git a/base/android/jni_array.h b/base/android/jni_array.h
index 64776a7..6c4f2b3 100644
--- a/base/android/jni_array.h
+++ b/base/android/jni_array.h
@@ -34,6 +34,7 @@
 }
 
 // Returns a new Java byte array converted from the given bytes array.
+// PRECONDITIONS: `bytes` must point to `len` valid bytes.
 UNSAFE_BUFFER_USAGE BASE_EXPORT ScopedJavaLocalRef<jbyteArray>
 ToJavaByteArray(JNIEnv* env, const uint8_t* bytes, size_t len);
 
diff --git a/base/containers/buffer_iterator.h b/base/containers/buffer_iterator.h
index 61abd79..e8e27c0 100644
--- a/base/containers/buffer_iterator.h
+++ b/base/containers/buffer_iterator.h
@@ -71,6 +71,7 @@
       : buffer_(buffer), remaining_(buffer) {}
 
   // TODO(crbug.com/40284755): Move all callers to use spans and remove this.
+  // PRECONDITIONS: `data` must point to `size` contiguous B elements.
   UNSAFE_BUFFER_USAGE BufferIterator(B* data, size_t size)
       : BufferIterator(
             // TODO(crbug.com/40284755): Remove this constructor entirely,
diff --git a/base/containers/flat_tree.h b/base/containers/flat_tree.h
index 937ed6b..9a98af6d 100644
--- a/base/containers/flat_tree.h
+++ b/base/containers/flat_tree.h
@@ -249,6 +249,9 @@
   template <class InputIterator>
     requires(std::input_iterator<InputIterator>)
   void insert(InputIterator first, InputIterator last);
+
+  // PRECONDITIONS: `first` and  `last` must be iterators into the
+  // same object, with `first` less than or equal to `last`.
   template <class InputIteratorPtr>
   UNSAFE_BUFFER_USAGE void insert(InputIteratorPtr* first,
                                   InputIteratorPtr* last);
@@ -755,6 +758,8 @@
       .first;
 }
 
+// PRECONDITIONS: `first` and  `last` must be iterators into the
+// same object, with `first` less than or equal to `last`.
 template <class Key, class GetKeyFromValue, class KeyCompare, class Container>
 template <class InputIteratorPtr>
 UNSAFE_BUFFER_USAGE void
diff --git a/base/containers/span.h b/base/containers/span.h
index 1dd00d71..1320eb00 100644
--- a/base/containers/span.h
+++ b/base/containers/span.h
@@ -462,9 +462,8 @@
   // Iterator + count.
   template <typename It>
     requires(internal::CompatibleIter<element_type, It>)
-  // SAFETY: `first` must point to the first of at least `count` contiguous
-  // valid elements, or the span will allow access to invalid elements,
-  // resulting in UB.
+  // PRECONDITIONS: `first` must point to the first of at least `count`
+  // contiguous valid elements.
   UNSAFE_BUFFER_USAGE constexpr explicit span(It first,
                                               StrictNumeric<size_type> count)
       : data_(to_address(first)) {
@@ -480,9 +479,8 @@
     requires(internal::CompatibleIter<element_type, It> &&
              std::sized_sentinel_for<End, It> &&
              !std::is_convertible_v<End, size_t>)
-  // SAFETY: `first` and `last` must be for the same allocation and all elements
-  // in the range [first, last) must be valid, or the span will allow access to
-  // invalid elements, resulting in UB.
+  // PRECONDITIONS: `first` and `last` must be for the same allocation and all
+  // elements in the range [first, last) must be valid.
   UNSAFE_BUFFER_USAGE constexpr explicit span(It first, End last)
       // SAFETY: The caller must guarantee that `first` and `last` point into
       // the same allocation. In this case, the extent will be the number of
@@ -959,9 +957,8 @@
   // Iterator + count.
   template <typename It>
     requires(internal::CompatibleIter<element_type, It>)
-  // SAFETY: `first` must point to the first of at least `count` contiguous
-  // valid elements, or the span will allow access to invalid elements,
-  // resulting in UB.
+  // PRECONDITIONS: `first` must point to the first of at least `count`
+  // contiguous valid elements.
   UNSAFE_BUFFER_USAGE constexpr span(It first, StrictNumeric<size_type> count)
       : data_(to_address(first)), size_(count) {
     // Non-zero `count` implies non-null `data_`. Use `SpanOrSize<T>` to
@@ -974,9 +971,8 @@
     requires(internal::CompatibleIter<element_type, It> &&
              std::sized_sentinel_for<End, It> &&
              !std::is_convertible_v<End, size_t>)
-  // SAFETY: `first` and `last` must be for the same allocation and all elements
-  // in the range [first, last) must be valid, or the span will allow access to
-  // invalid elements, resulting in UB.
+  // PRECONDITIONS: `first` and `last` must be for the same allocation and all
+  // elements in the range [first, last) must be valid.
   UNSAFE_BUFFER_USAGE constexpr span(It first, End last)
       // SAFETY: The caller must guarantee that `first` and `last` point into
       // the same allocation. In this case, `size_` will be the number of
diff --git a/base/files/file.h b/base/files/file.h
index 9ca6817..020a33a4 100644
--- a/base/files/file.h
+++ b/base/files/file.h
@@ -219,16 +219,22 @@
   // is not intended for stream oriented files but instead for cases when the
   // normal expectation is that actually |size| bytes are read unless there is
   // an error.
+  // PRECONDITIONS: `size` must be non-negative and `data` must point to at
+  // least `size` valid bytes.
   UNSAFE_BUFFER_USAGE int Read(int64_t offset, char* data, int size);
   std::optional<size_t> Read(int64_t offset, base::span<uint8_t> data);
 
   // Same as above but without seek.
+  // PRECONDITIONS: `size` must be non-negative and `data` must point to at
+  // least `size` valid bytes.
   UNSAFE_BUFFER_USAGE int ReadAtCurrentPos(char* data, int size);
   std::optional<size_t> ReadAtCurrentPos(base::span<uint8_t> data);
 
   // Reads the given number of bytes (or until EOF is reached) starting with the
   // given offset, but does not make any effort to read all data on all
   // platforms. Returns the number of bytes read, or -1/std::nullopt on error.
+  // PRECONDITIONS: `size` must be non-negative and `data` must point to at
+  // least `size` valid bytes.
   UNSAFE_BUFFER_USAGE int ReadNoBestEffort(int64_t offset,
                                            char* data,
                                            int size);
@@ -236,6 +242,8 @@
                                          base::span<uint8_t> data);
 
   // Same as above but without seek.
+  // PRECONDITIONS: `size` must be non-negative and `data` must point to at
+  // least `size` valid bytes.
   UNSAFE_BUFFER_USAGE int ReadAtCurrentPosNoBestEffort(char* data, int size);
   std::optional<size_t> ReadAtCurrentPosNoBestEffort(base::span<uint8_t> data);
 
@@ -251,16 +259,22 @@
   // all platforms. |data| can be nullptr when |size| is 0.
   // Ignores the offset and writes to the end of the file if the file was opened
   // with FLAG_APPEND.
+  // PRECONDITIONS: `size` must be non-negative and `data` must point to at
+  // least `size` valid bytes.
   UNSAFE_BUFFER_USAGE int Write(int64_t offset, const char* data, int size);
   std::optional<size_t> Write(int64_t offset, base::span<const uint8_t> data);
 
-  // Save as above but without seek.
+  // Same as above but without seek.
+  // PRECONDITIONS: `size` must be non-negative and `data` must point to at
+  // least `size` valid bytes.
   UNSAFE_BUFFER_USAGE int WriteAtCurrentPos(const char* data, int size);
   std::optional<size_t> WriteAtCurrentPos(base::span<const uint8_t> data);
 
-  // Save as above but does not make any effort to write all data on all
+  // Same as above but does not make any effort to write all data on all
   // platforms. Returns the number of bytes written, or -1/std::nullopt
   // on error.
+  // PRECONDITIONS: `size` must be non-negative and `data` must point to at
+  // least `size` valid bytes.
   UNSAFE_BUFFER_USAGE int WriteAtCurrentPosNoBestEffort(const char* data,
                                                         int size);
   std::optional<size_t> WriteAtCurrentPosNoBestEffort(
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElement.java b/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElement.java
index 548a15a..4517e8f 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElement.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/ViewElement.java
@@ -109,6 +109,19 @@
         return mViewSpec;
     }
 
+    /** Returns a {@link ViewSpec} to declare a descandant of this ViewElement. */
+    @SafeVarargs
+    public final ViewSpec<View> descendant(Matcher<View>... viewMatcher) {
+        return mViewSpec.descendant(viewMatcher);
+    }
+
+    /** Returns a {@link ViewSpec} to declare a descandant of this ViewElement. */
+    @SafeVarargs
+    public final <DescendantViewT extends View> ViewSpec<DescendantViewT> descendant(
+            Class<DescendantViewT> viewClass, Matcher<View>... viewMatcher) {
+        return mViewSpec.descendant(viewClass, viewMatcher);
+    }
+
     /** Trigger an Espresso action on this View. */
     public Transition.Trigger getPerformTrigger(ViewAction action) {
         return () -> {
diff --git a/base/test/android/javatests/src/org/chromium/base/test/transit/ViewSpec.java b/base/test/android/javatests/src/org/chromium/base/test/transit/ViewSpec.java
index ceedc38..0714fee 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/transit/ViewSpec.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/transit/ViewSpec.java
@@ -72,6 +72,15 @@
         return viewSpec(allViewMatchers);
     }
 
+    /** Create a ViewSpec for a descendant of this ViewSpec that matches multiple Matchers<View>. */
+    @SafeVarargs
+    public final <ChildViewT extends View> ViewSpec<ChildViewT> descendant(
+            Class<ChildViewT> viewClass, Matcher<View>... viewMatchers) {
+        Matcher<View>[] allViewMatchers = Arrays.copyOf(viewMatchers, viewMatchers.length + 1);
+        allViewMatchers[viewMatchers.length] = isDescendantOfA(mViewMatcher);
+        return viewSpec(viewClass, allViewMatchers);
+    }
+
     /** Creates a ViewSpec that matches this ViewSpec _and_ another Matcher<View>. */
     public final ViewSpec<View> and(Matcher<View> viewMatcher) {
         return viewSpec(viewMatcher, mViewMatcher);
@@ -93,7 +102,17 @@
         // states by their description. Espresso Matcher descriptions are not stable: the integer
         // resource ids are translated when a View is provided. See examples in
         // https://crbug.com/41494895#comment7.
-        mMatcherDescription = StringDescription.toString(mViewMatcher);
+        mMatcherDescription = removeResolvedIds(StringDescription.toString(mViewMatcher));
+    }
+
+    private static String removeResolvedIds(String matcherDescription) {
+        // Replace:
+        // "VE/view.getId() is <2130773232/org.chromium.chrome.tests:id/hub_toolbar>"
+        // with:
+        // "VE/view.getId() is <2130773232>"
+
+        // Generated ids have at least 8 digits, since they are >= 0xffffff (16777215)
+        return matcherDescription.replaceAll("<([0-9]{8,})/.*>", "<$1>");
     }
 
     /**
diff --git a/base/test/android/junit/src/org/chromium/base/test/ShadowBuildInfo.java b/base/test/android/junit/src/org/chromium/base/test/ShadowBuildInfo.java
deleted file mode 100644
index 93848c3..0000000
--- a/base/test/android/junit/src/org/chromium/base/test/ShadowBuildInfo.java
+++ /dev/null
@@ -1,34 +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.
-
-package org.chromium.base.test;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.Resetter;
-
-import org.chromium.base.BuildInfo;
-
-/** Shadow class of {@link BuildInfo} */
-@Implements(BuildInfo.class)
-public class ShadowBuildInfo {
-    private static boolean sTargetsAtLeastT;
-
-    /** Rests the changes made to static state. */
-    @Resetter
-    public static void reset() {
-        sTargetsAtLeastT = false;
-    }
-
-    /** Whether the current build is targeting at least T. */
-    @Implementation
-    public static boolean targetsAtLeastT() {
-        return sTargetsAtLeastT;
-    }
-
-    /** Sets whether the current build is targeting at least T. */
-    public static void setTargetsAtLeastT(boolean targetsAtLeastT) {
-        sTargetsAtLeastT = targetsAtLeastT;
-    }
-}
diff --git a/base/test/launcher/unit_test_launcher.cc b/base/test/launcher/unit_test_launcher.cc
index 871e8ff3..06571421 100644
--- a/base/test/launcher/unit_test_launcher.cc
+++ b/base/test/launcher/unit_test_launcher.cc
@@ -282,7 +282,7 @@
 
 #if BUILDFLAG(IS_WIN)
 
-// Safety: as is normal in command lines, argc and argv must correspond
+// PRECONDITIONS: As is normal in command lines, argc and argv must correspond
 // to one another. Otherwise there will be out-of-bounds accesses.
 UNSAFE_BUFFER_USAGE void InitGoogleTestWChar(int* argc, wchar_t** argv) {
   testing::InitGoogleTest(argc, argv);
diff --git a/base/time/time.h b/base/time/time.h
index 40a5589..e301dc8 100644
--- a/base/time/time.h
+++ b/base/time/time.h
@@ -69,7 +69,6 @@
 #include <concepts>
 #include <iosfwd>
 #include <limits>
-#include <ostream>
 #include <type_traits>
 
 #include "base/base_export.h"
@@ -103,6 +102,8 @@
 #endif
 
 #if BUILDFLAG(IS_WIN)
+#include <string>
+
 #include "base/gtest_prod_util.h"
 #include "base/win/windows_types.h"
 
diff --git a/base/tools_sanity_unittest.cc b/base/tools_sanity_unittest.cc
index 1500a6f..2b3649a 100644
--- a/base/tools_sanity_unittest.cc
+++ b/base/tools_sanity_unittest.cc
@@ -360,7 +360,9 @@
 
 #if defined(THREAD_SANITIZER)
 // A data race detector should report an error in this test.
-TEST(ToolsSanityTest, DataRace) {
+// TODO(crbug.com/416191043): Re-enable when symbol_level on sanitizer bots
+// can safely be raised again.
+TEST(ToolsSanityTest, DISABLED_DataRace) {
   // The suppression regexp must match that in base/debug/tsan_suppressions.cc.
   EXPECT_DEATH(DataRace(), "1 race:base/tools_sanity_unittest.cc");
 }
diff --git a/build/android/apk_operations.pydeps b/build/android/apk_operations.pydeps
index cde63ff..5455106a 100644
--- a/build/android/apk_operations.pydeps
+++ b/build/android/apk_operations.pydeps
@@ -48,7 +48,6 @@
 ../../third_party/catapult/devil/devil/devil_env.py
 ../../third_party/catapult/devil/devil/utils/__init__.py
 ../../third_party/catapult/devil/devil/utils/cmd_helper.py
-../../third_party/catapult/devil/devil/utils/host_utils.py
 ../../third_party/catapult/devil/devil/utils/lazy/__init__.py
 ../../third_party/catapult/devil/devil/utils/lazy/weak_constant.py
 ../../third_party/catapult/devil/devil/utils/logging_common.py
diff --git a/build/android/test_runner.pydeps b/build/android/test_runner.pydeps
index 523ae642..f87ec1b 100644
--- a/build/android/test_runner.pydeps
+++ b/build/android/test_runner.pydeps
@@ -78,7 +78,6 @@
 ../../third_party/catapult/devil/devil/utils/__init__.py
 ../../third_party/catapult/devil/devil/utils/cmd_helper.py
 ../../third_party/catapult/devil/devil/utils/file_utils.py
-../../third_party/catapult/devil/devil/utils/host_utils.py
 ../../third_party/catapult/devil/devil/utils/lazy/__init__.py
 ../../third_party/catapult/devil/devil/utils/lazy/weak_constant.py
 ../../third_party/catapult/devil/devil/utils/logging_common.py
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 2fc8d823..708c204 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -512,7 +512,13 @@
   # Linux-specific compiler flags setup.
   # ------------------------------------
   if (use_icf && (!is_apple || use_lld)) {
-    ldflags += [ "-Wl,--icf=all" ]
+    if (is_fuchsia) {
+      # TODO(crbug.com/415810137): A temporary workaround to avoid crashing
+      # blink on fuchsia.
+      ldflags += [ "-Wl,--icf=safe" ]
+    } else {
+      ldflags += [ "-Wl,--icf=all" ]
+    }
   }
 
   if (is_linux || is_chromeos) {
@@ -1727,7 +1733,6 @@
         rebase_path(clang_warning_suppression_file, root_build_dir)
     inputs = [ clang_warning_suppression_file ]
     cflags = [
-      "-Xclang",
       "--warning-suppression-mappings=" + from_build_root,
     ]
   }
diff --git a/build/config/warning_suppression.txt b/build/config/warning_suppression.txt
index 4f78ea8..1a1b2cdf 100644
--- a/build/config/warning_suppression.txt
+++ b/build/config/warning_suppression.txt
@@ -2,8 +2,36 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# Warning suppression mappings are listed here on a per-warning basis.
-# Upstream docs: https://clang.llvm.org/docs/WarningSuppressionMappings.html
-# We'll put the policy here too when it's written.
-# Don't use this file until the policy is written.
-# See crbug.com/404297941 for more information.
+# This file is used to suppress warnings based on the file they originate from,
+# as opposed to disabling warnings via -Wno flags which apply to all the files
+# involved in each compilation. For more information, see
+# https://clang.llvm.org/docs/WarningSuppressionMappings.html
+# For background information on their use in chromium, see crbug.com/404297941
+
+# Warning Suppression Policy: updates to this file should be tightly controlled,
+# for reasons discussed in crbug.com/404297941. In particular:
+#
+# 1. This file should never grow (as measured by the number of files suppressed)
+#    except when a new warning is enabled.
+# 2. This file should only be used to opt out whole directories, never
+#    individual files.
+# 3. This file should be used as a last resort; if it's possible to fix the warning or suppress
+#    it without using the file, do so.
+#    1. For first-party code, just fix it directly, or use `#pragma GCC diagnostic ignored`.
+#    2. For third-party code, first attempt to fix it upstream.
+#    3. If that's not possible, attempt to suppress the warning using `-Wno` flags in a gn file.
+# 4. All entries should have a path to eventually be removed.
+#
+# In practice, rules (3) and (4) mean that the only accepted use case for this file is to
+# speed up rolls or enable a warning slightly sooner, for cases where an upstream fix has been
+# proposed but is likely to take a long time to get merged and rolled into chromium.
+#
+# We may make an exception to the policy for extremely high-value warnings that backslide a lot
+# (such as unsafe buffers), but this is expected to be rare.
+
+# Formatting note: Don't put comments on the same line as a glob pattern! Clang
+# will get confused and the warning won't be suppressed.
+
+[unnecessary-virtual-specifier]
+# Can be removed when https://github.com/google/nearby/pull/3392 is merged and rolled
+src:*/third_party/nearby/*
\ No newline at end of file
diff --git a/build/fuchsia/linux_internal.sdk.sha1 b/build/fuchsia/linux_internal.sdk.sha1
index 5445093..5e67c8d 100644
--- a/build/fuchsia/linux_internal.sdk.sha1
+++ b/build/fuchsia/linux_internal.sdk.sha1
@@ -1 +1 @@
-27.20250501.100.1
+28.20250508.101.1
diff --git a/cc/base/features.cc b/cc/base/features.cc
index 11157a84..fb35620 100644
--- a/cc/base/features.cc
+++ b/cc/base/features.cc
@@ -106,10 +106,6 @@
              "EvictionThrottlesDraw",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kAdjustFastMainThreadThreshold,
-             "AdjustFastMainThreadThreshold",
-             base::FEATURE_DISABLED_BY_DEFAULT);
-
 BASE_FEATURE(kClearCanvasResourcesInBackground,
              "ClearCanvasResourcesInBackground",
              base::FEATURE_DISABLED_BY_DEFAULT);
@@ -137,10 +133,6 @@
              "PreserveDiscardableImageMapQuality",
              base::FEATURE_ENABLED_BY_DEFAULT);
 
-BASE_FEATURE(kWarmUpCompositor,
-             "WarmUpCompositor",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kCCSlimming, "CCSlimming", base::FEATURE_ENABLED_BY_DEFAULT);
 
 bool IsCCSlimmingEnabled() {
diff --git a/cc/base/features.h b/cc/base/features.h
index 31520bd..4e96f70 100644
--- a/cc/base/features.h
+++ b/cc/base/features.h
@@ -73,11 +73,6 @@
 // viz::Surface.
 CC_BASE_EXPORT BASE_DECLARE_FEATURE(kEvictionThrottlesDraw);
 
-// Permits adjusting the threshold we use for determining if main thread updates
-// are fast. Specifically, via a scalar on the range [0,1] that we multiply with
-// the existing threshold. I.e., |new_threshold| = |scalar| * |old_threshold|.
-CC_BASE_EXPORT BASE_DECLARE_FEATURE(kAdjustFastMainThreadThreshold);
-
 // When a LayerTreeHostImpl is not visible, clear its transferable resources
 // that haven't been imported into viz.
 CC_BASE_EXPORT BASE_DECLARE_FEATURE(kClearCanvasResourcesInBackground);
@@ -111,12 +106,6 @@
 // image map.
 CC_BASE_EXPORT BASE_DECLARE_FEATURE(kPreserveDiscardableImageMapQuality);
 
-// When enabled, the renderer asks the compositor to request warming up and
-// create FrameSink speculatively even if invisible. Currently, this is intended
-// to be used when prerender initial navigation is happening in background.
-// Please see crbug.com/41496019 for more details.
-CC_BASE_EXPORT BASE_DECLARE_FEATURE(kWarmUpCompositor);
-
 // Kill switch for a bunch of optimizations for cc-slimming project.
 // Please see crbug.com/335450599 for more details.
 CC_BASE_EXPORT BASE_DECLARE_FEATURE(kCCSlimming);
diff --git a/cc/mojo_embedder/async_layer_tree_frame_sink.cc b/cc/mojo_embedder/async_layer_tree_frame_sink.cc
index 2703317..c3b6661 100644
--- a/cc/mojo_embedder/async_layer_tree_frame_sink.cc
+++ b/cc/mojo_embedder/async_layer_tree_frame_sink.cc
@@ -285,13 +285,7 @@
   compositor_frame_sink_ptr_->SubmitCompositorFrame(
       local_surface_id_, std::move(frame), std::move(hit_test_region_list), 0);
 
-  if (base::FeatureList::IsEnabled(
-          features::kExportFrameTimingAfterFrameDone)) {
-    for (const auto& pair : timing_details_) {
-      client_->DidPresentCompositorFrame(pair.first, pair.second);
-    }
-    timing_details_.clear();
-  }
+  ExportFrameTiming();
 
   num_did_not_produce_frame_since_last_submit_ = 0;
   if (use_internal_begin_frame_source_) {
@@ -320,13 +314,8 @@
         data->set_surface_frame_trace_id(ack.trace_id);
       });
 
-  if (base::FeatureList::IsEnabled(
-          features::kExportFrameTimingAfterFrameDone)) {
-    for (const auto& pair : timing_details_) {
-      client_->DidPresentCompositorFrame(pair.first, pair.second);
-    }
-    timing_details_.clear();
-  }
+  ExportFrameTiming();
+
   if (use_internal_begin_frame_source_) {
     if (ack.preferred_frame_interval) {
       const viz::BeginFrameArgs last_args =
@@ -352,6 +341,16 @@
   }
 }
 
+void AsyncLayerTreeFrameSink::ExportFrameTiming() {
+  if (base::FeatureList::IsEnabled(
+          features::kExportFrameTimingAfterFrameDone)) {
+    for (const auto& pair : timing_details_) {
+      client_->DidPresentCompositorFrame(pair.first, pair.second);
+    }
+    timing_details_.clear();
+  }
+}
+
 std::unique_ptr<LayerContext> AsyncLayerTreeFrameSink::CreateLayerContext(
     LayerTreeHostImpl& host_impl) {
   CHECK(compositor_frame_sink_ptr_);
diff --git a/cc/mojo_embedder/async_layer_tree_frame_sink.h b/cc/mojo_embedder/async_layer_tree_frame_sink.h
index a7541f6f..73fc705 100644
--- a/cc/mojo_embedder/async_layer_tree_frame_sink.h
+++ b/cc/mojo_embedder/async_layer_tree_frame_sink.h
@@ -139,6 +139,7 @@
                              bool hit_test_data_changed) override;
   void DidNotProduceFrame(const viz::BeginFrameAck& ack,
                           FrameSkippedReason reason) override;
+  void ExportFrameTiming() override;
   std::unique_ptr<LayerContext> CreateLayerContext(
       LayerTreeHostImpl& host_impl) override;
 
diff --git a/cc/mojo_embedder/viz_layer_context.cc b/cc/mojo_embedder/viz_layer_context.cc
index 7fad3bc..a4bfc34 100644
--- a/cc/mojo_embedder/viz_layer_context.cc
+++ b/cc/mojo_embedder/viz_layer_context.cc
@@ -913,6 +913,8 @@
   update->begin_frame_args = tree.CurrentBeginFrameArgs();
   update->source_frame_number = tree.source_frame_number();
   update->trace_id = tree.trace_id().value();
+  update->primary_main_frame_item_sequence_number =
+      tree.primary_main_frame_item_sequence_number();
   update->page_scale_factor = tree.page_scale_factor()->Current(true);
   update->min_page_scale_factor = tree.min_page_scale_factor();
   update->max_page_scale_factor = tree.max_page_scale_factor();
diff --git a/cc/scheduler/scheduler.cc b/cc/scheduler/scheduler.cc
index 860e1cf..b03e0d3 100644
--- a/cc/scheduler/scheduler.cc
+++ b/cc/scheduler/scheduler.cc
@@ -35,21 +35,6 @@
 // for message latency and kernel scheduling variability.
 const base::TimeDelta kDeadlineFudgeFactor = base::Microseconds(1000);
 
-// This adjustment is applied by multiplying with the previous, begin-main-frame
-// to activate threshold. For example, if we want to consider a page fast if it
-// it takes half the threshold, we would return 0.5. Naturally, this function
-// will return values in the range [0, 1].
-double FastMainThreadThresholdAdjustment() {
-  if (base::FeatureList::IsEnabled(features::kAdjustFastMainThreadThreshold)) {
-    double result = base::GetFieldTrialParamByFeatureAsDouble(
-        features::kAdjustFastMainThreadThreshold, "Scalar", -1.0);
-    if (result >= 0.0 && result <= 1.0) {
-      return result;
-    }
-  }
-  return 1.0;
-}
-
 }  // namespace
 
 Scheduler::Scheduler(
@@ -114,7 +99,6 @@
 }
 
 void Scheduler::SetShouldWarmUp() {
-  CHECK(base::FeatureList::IsEnabled(features::kWarmUpCompositor));
   state_machine_.SetShouldWarmUp();
   ProcessScheduledActions();
 }
@@ -547,10 +531,8 @@
   base::TimeDelta bmf_to_activate_estimate_critical =
       compositor_timing_history_
           ->BeginMainFrameQueueToActivateCriticalEstimate();
-  base::TimeDelta fast_main_thread_threshold =
-      bmf_to_activate_threshold * FastMainThreadThresholdAdjustment();
   state_machine_.SetCriticalBeginMainFrameToActivateIsFast(
-      bmf_to_activate_estimate_critical < fast_main_thread_threshold);
+      bmf_to_activate_estimate_critical < bmf_to_activate_threshold);
 
   // Update the BeginMainFrame args now that we know whether the main
   // thread will be on the critical path or not.
diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc
index 9c31e8e..ccb5f6f 100644
--- a/cc/scheduler/scheduler_state_machine.cc
+++ b/cc/scheduler/scheduler_state_machine.cc
@@ -1579,7 +1579,6 @@
 }
 
 void SchedulerStateMachine::SetShouldWarmUp() {
-  CHECK(base::FeatureList::IsEnabled(features::kWarmUpCompositor));
   should_warm_up_ = true;
 }
 
diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h
index 40ef1b6..d7780929 100644
--- a/cc/scheduler/scheduler_state_machine.h
+++ b/cc/scheduler/scheduler_state_machine.h
@@ -219,10 +219,7 @@
   bool visible() const { return visible_; }
 
   // Indicates that warming up is requested to create a new LayerTreeFrameSink
-  // even if the LayerTreeHost is invisible. This is an experimental function
-  // and only used if `kWarmUpCompositor` is enabled. Currently, this will be
-  // requested only from prerendered pages. Please see crbug.com/40240492 for
-  // more details.
+  // even if the LayerTreeHost is invisible.
   void SetShouldWarmUp();
 
   void SetBeginFrameSourcePaused(bool paused);
diff --git a/cc/scheduler/scheduler_state_machine_unittest.cc b/cc/scheduler/scheduler_state_machine_unittest.cc
index 2cf449d7..da91b81 100644
--- a/cc/scheduler/scheduler_state_machine_unittest.cc
+++ b/cc/scheduler/scheduler_state_machine_unittest.cc
@@ -3632,20 +3632,10 @@
                          ScrollingSchedulerStateMachineTest,
                          testing::Bool());
 
-class WarmUpCompositorSchedulerStateMachineTest : public testing::Test {
- public:
-  WarmUpCompositorSchedulerStateMachineTest() {
-    scoped_feature_list_.InitAndEnableFeature(features::kWarmUpCompositor);
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
 // Tests that `SetShouldWarmUp()` will start initial `LayerTreeFrameSink`
 // creation even if invisible.
-TEST_F(WarmUpCompositorSchedulerStateMachineTest,
-       SetShouldWarmUpWillStartLayerTreeFrameSinkCreation) {
+TEST(SchedulerStateMachineTest,
+     SetShouldWarmUpWillStartLayerTreeFrameSinkCreation) {
   SchedulerSettings default_scheduler_settings;
   StateMachine state(default_scheduler_settings);
   state.SetVisible(false);
diff --git a/cc/scheduler/scheduler_unittest.cc b/cc/scheduler/scheduler_unittest.cc
index 8b5cb9a..54e64b9b 100644
--- a/cc/scheduler/scheduler_unittest.cc
+++ b/cc/scheduler/scheduler_unittest.cc
@@ -4178,20 +4178,9 @@
       scheduler_->state_machine().MainFrameThrottledInterval().is_zero());
 }
 
-class WarmUpCompositorSchedulerTest : public SchedulerTest {
- public:
-  WarmUpCompositorSchedulerTest() {
-    scoped_feature_list_.InitAndEnableFeature(features::kWarmUpCompositor);
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
 // Tests that `SetShouldWarmUp()` will start initial `LayerTreeFrameSink`
 // creation even if invisible.
-TEST_F(WarmUpCompositorSchedulerTest,
-       SetShouldWarmUpWillStartLayerTreeFrameSinkCreation) {
+TEST_F(SchedulerTest, SetShouldWarmUpWillStartLayerTreeFrameSinkCreation) {
   SetUpSchedulerWithNoLayerTreeFrameSink(EXTERNAL_BFS);
   scheduler_->SetVisible(false);
 
diff --git a/cc/trees/layer_tree_frame_sink.h b/cc/trees/layer_tree_frame_sink.h
index 722be481..27f255d 100644
--- a/cc/trees/layer_tree_frame_sink.h
+++ b/cc/trees/layer_tree_frame_sink.h
@@ -138,6 +138,8 @@
   virtual void DidNotProduceFrame(const viz::BeginFrameAck& ack,
                                   FrameSkippedReason reason) = 0;
 
+  virtual void ExportFrameTiming() {}
+
   // Creates a new LayerContext through which the client can control layers in
   // a GPU-side display tree.
   virtual std::unique_ptr<LayerContext> CreateLayerContext(
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 9e597544..8487d71b 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -852,16 +852,12 @@
 
 void LayerTreeHost::SetShouldWarmUp() {
   DCHECK(IsMainThread());
-  CHECK(base::FeatureList::IsEnabled(features::kWarmUpCompositor));
   should_warm_up_ = true;
   proxy_->SetShouldWarmUp();
 }
 
 bool LayerTreeHost::ShouldWarmUp() const {
   DCHECK(IsMainThread());
-  if (!base::FeatureList::IsEnabled(features::kWarmUpCompositor)) {
-    return false;
-  }
   return should_warm_up_;
 }
 
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index a10f549..600add9 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -258,8 +258,8 @@
   // Visibility and LayerTreeFrameSink -------------------------------
 
   // Sets or gets if the LayerTreeHost is visible. When not visible it will:
-  // - Not request a new LayerTreeFrameSink from the client (except
-  //   `kWarmUpCompositor` is enabled and warm-up is explicitly requested).
+  // - Not request a new LayerTreeFrameSink from the client (except the warm-up
+  //   is explicitly requested by `SetShouldWarmUp`).
   // - Stop submitting frames to the display compositor.
   // - Stop producing main frames and committing them.
   // The LayerTreeHost is not visible when first created, so this must be called
@@ -268,10 +268,7 @@
   bool IsVisible() const;
 
   // Indicates that warm-up is requested to create a new LayerTreeFrameSink
-  // even if the LayerTreeHost is invisible. This is an experimental function
-  // and only used if `kWarmUpCompositor` is enabled. Currently, this will be
-  // requested only from prerendered pages. Please see crbug.com/41496019 for
-  // more details.
+  // even if the LayerTreeHost is invisible.
   void SetShouldWarmUp();
   bool ShouldWarmUp() const;
 
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 435c8b1..31da38d 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2907,6 +2907,8 @@
   if (settings_.TreesInVizInClientProcess()) {
     UpdateDisplayTree(*frame);
 
+    layer_tree_frame_sink_->ExportFrameTiming();
+
     // For the display compositor we should have already submitted at display
     // Immediately queue a DidReceiveCompositorFrameAck.
     GetTaskRunner()->PostTask(
@@ -3239,7 +3241,11 @@
   // The swap-promises should not change the frame-token.
   DCHECK_EQ(metadata.frame_token, *next_frame_token_);
 
-  if (render_frame_metadata_observer_) {
+  // In TreesInViz mode in viz, we need to compute
+  // |last_draw_render_frame_metadata_| because it impacts HasDamage()
+  // computation.
+  if (render_frame_metadata_observer_ ||
+      settings_.trees_in_viz_in_viz_process) {
     last_draw_render_frame_metadata_ = MakeRenderFrameMetadata(frame);
     if (gfx::DelegatedInkMetadata* ink_metadata =
             metadata.delegated_ink_metadata.get()) {
@@ -3257,9 +3263,12 @@
           last_draw_render_frame_metadata_->new_vertical_scroll_direction;
     }
 
-    render_frame_metadata_observer_->OnRenderFrameSubmission(
-        *last_draw_render_frame_metadata_, &metadata,
-        active_tree()->TakeForceSendMetadataRequest());
+    // TODO(zmo): Consider plumbing the observer to viz as well.
+    if (render_frame_metadata_observer_) {
+      render_frame_metadata_observer_->OnRenderFrameSubmission(
+          *last_draw_render_frame_metadata_, &metadata,
+          active_tree()->TakeForceSendMetadataRequest());
+    }
   }
 
   if (!CommitsToActiveTree() && !metadata.latency_info.empty()) {
diff --git a/chrome/VERSION b/chrome/VERSION
index a063e48..f80e5b3 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=138
 MINOR=0
-BUILD=7168
+BUILD=7169
 PATCH=0
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index 5536e34..739d6306 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -480,6 +480,7 @@
   "java/src/org/chromium/chrome/browser/customtabs/content/TabCreationMode.java",
   "java/src/org/chromium/chrome/browser/customtabs/content/TabObserverRegistrar.java",
   "java/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandler.java",
+  "java/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandlerHistogram.java",
   "java/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchParams.java",
   "java/src/org/chromium/chrome/browser/customtabs/features/CustomTabDimensionUtils.java",
   "java/src/org/chromium/chrome/browser/customtabs/features/CustomTabNavigationBarController.java",
diff --git a/chrome/android/features/tab_ui/BUILD.gn b/chrome/android/features/tab_ui/BUILD.gn
index f83a008..9ed9df2 100644
--- a/chrome/android/features/tab_ui/BUILD.gn
+++ b/chrome/android/features/tab_ui/BUILD.gn
@@ -73,6 +73,7 @@
     "java/res/layout/archived_tabs_dialog.xml",
     "java/res/layout/archived_tabs_message_card_view.xml",
     "java/res/layout/bottom_tab_strip_toolbar.xml",
+    "java/res/layout/color_picker_icon_button_layout.xml",
     "java/res/layout/color_picker_item.xml",
     "java/res/layout/custom_message_card_item.xml",
     "java/res/layout/dynamic_bottom_tab_strip_toolbar.xml",
diff --git a/chrome/android/features/tab_ui/java/res/layout/color_picker_icon_button_layout.xml b/chrome/android/features/tab_ui/java/res/layout/color_picker_icon_button_layout.xml
new file mode 100644
index 0000000..77bff508
--- /dev/null
+++ b/chrome/android/features/tab_ui/java/res/layout/color_picker_icon_button_layout.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+    <com.google.android.material.button.MaterialButton
+        android:id="@+id/color_picker_icon"
+        style="?attr/colorPickerButtonStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:checkable="true" />
+</FrameLayout>
\ No newline at end of file
diff --git a/chrome/android/features/tab_ui/java/res/values/dimens.xml b/chrome/android/features/tab_ui/java/res/values/dimens.xml
index a33e5bf8..862ef07 100644
--- a/chrome/android/features/tab_ui/java/res/values/dimens.xml
+++ b/chrome/android/features/tab_ui/java/res/values/dimens.xml
@@ -95,6 +95,9 @@
     <dimen name="tab_group_color_icon_item_radius">9dp</dimen>
     <dimen name="tab_group_color_picker_popup_padding">10dp</dimen>
     <dimen name="tab_group_visual_data_dialog_margin_padding">3dp</dimen>
+    <dimen name="color_picker_button_stroke_inset">11dp</dimen>
+    <dimen name="color_picker_button_stroke_width">2dp</dimen>
+    <dimen name="color_picker_button_stroke_radius">9dp</dimen>
 
     <!-- Dimens for tab card label -->
     <dimen name="tab_card_label_icon_size">16dp</dimen>
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java
index a828c32..f0b5f3b 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinator.java
@@ -63,6 +63,7 @@
 import org.chromium.chrome.browser.tasks.tab_management.TabListMediator.TabActionListener;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.TabActionState;
 import org.chromium.chrome.browser.tasks.tab_management.TabProperties.UiType;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeControllerFactory;
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeUtils;
@@ -431,6 +432,12 @@
         mShadowView.init(
                 mActivity.getColor(R.color.toolbar_shadow_color), FadingShadow.POSITION_BOTTOM);
 
+        // TODO(crbug.com/410040707): Set the color in the layout file.
+        getCloseAllTabsButtonContainer()
+                .setBackgroundColor(
+                        SurfaceColorUpdateUtils.getGridTabSwitcherBackgroundColor(
+                                mActivity, /* isIncognito= */ false));
+
         // Initialize the confirmation dialog for when the last archived tab is removed.
         mActionConfirmationDialog = new ActionConfirmationDialog(mActivity, mModalDialogManager);
 
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinder.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinder.java
index 7ef8e551..ad6184f 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinder.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinder.java
@@ -9,23 +9,29 @@
 import static org.chromium.chrome.browser.tasks.tab_management.ColorPickerItemProperties.IS_INCOGNITO;
 import static org.chromium.chrome.browser.tasks.tab_management.ColorPickerItemProperties.IS_SELECTED;
 import static org.chromium.chrome.browser.tasks.tab_management.ColorPickerItemProperties.ON_CLICK_LISTENER;
+import static org.chromium.chrome.browser.tasks.tab_management.TabUiThemeProvider.getColorPickerDialogBackgroundColor;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.LayerDrawable;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewOverlay;
+import android.widget.Button;
 import android.widget.ImageView;
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.StringRes;
-import androidx.core.content.ContextCompat;
 
+import com.google.android.material.button.MaterialButton;
+
+import org.chromium.chrome.browser.theme.ThemeModuleUtils;
 import org.chromium.chrome.tab_ui.R;
-import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.tab_groups.TabGroupColorPickerUtils;
+import org.chromium.ui.drawable.BorderDrawable;
 import org.chromium.ui.modelutil.PropertyKey;
 import org.chromium.ui.modelutil.PropertyModel;
 
@@ -44,15 +50,20 @@
     public static final int INNER_LAYER = 2;
 
     static View createItemView(Context context) {
-        return LayoutInflater.from(context)
-                .inflate(R.layout.color_picker_item, /* root= */ null, false);
+        int layoutToInflate =
+                isAndroidThemeModuleEnabled()
+                        ? R.layout.color_picker_icon_button_layout
+                        : R.layout.color_picker_item;
+
+        return LayoutInflater.from(context).inflate(layoutToInflate, /* root= */ null, false);
     }
 
     static void bind(PropertyModel model, View view, PropertyKey propertyKey) {
         if (propertyKey == COLOR_ID) {
             setColorOnColorIcon(model, view);
         } else if (propertyKey == ON_CLICK_LISTENER) {
-            view.setOnClickListener((v) -> model.get(ON_CLICK_LISTENER).run());
+            view.findViewById(R.id.color_picker_icon)
+                    .setOnClickListener((v) -> model.get(ON_CLICK_LISTENER).run());
         } else if (propertyKey == IS_SELECTED) {
             refreshColorIconOnSelection(model, view);
             setAccessibilityContent(view, model.get(IS_SELECTED), model.get(COLOR_ID));
@@ -66,33 +77,72 @@
         int colorId = model.get(COLOR_ID);
 
         final @ColorInt int color = getColor(context, colorPickerType, colorId, isIncognito);
-        final @ColorInt int selectionBackgroundColor =
-                isIncognito
-                        ? ContextCompat.getColor(
-                                context, R.color.tab_group_color_picker_selection_bg_incognito)
-                        : SemanticColorUtils.getDialogBgColor(context);
 
         // Update the color icon with the indicated color id.
-        ImageView colorIcon = view.findViewById(R.id.color_picker_icon);
-        LayerDrawable layerDrawable = (LayerDrawable) colorIcon.getBackground();
-        ((GradientDrawable) layerDrawable.getDrawable(OUTER_LAYER)).setColor(color);
-        ((GradientDrawable) layerDrawable.getDrawable(SELECTION_LAYER))
-                .setColor(selectionBackgroundColor);
-        ((GradientDrawable) layerDrawable.getDrawable(INNER_LAYER)).setColor(color);
+        if (isAndroidThemeModuleEnabled()) {
+            Button colorIcon = view.findViewById(R.id.color_picker_icon);
+            colorIcon.setBackgroundTintList(ColorStateList.valueOf(color));
+        } else {
+            final @ColorInt int selectionBackgroundColor =
+                    getColorPickerDialogBackgroundColor(context, isIncognito);
+
+            ImageView colorIcon = view.findViewById(R.id.color_picker_icon);
+            LayerDrawable layerDrawable = (LayerDrawable) colorIcon.getBackground();
+            ((GradientDrawable) layerDrawable.getDrawable(OUTER_LAYER)).setColor(color);
+            ((GradientDrawable) layerDrawable.getDrawable(SELECTION_LAYER))
+                    .setColor(selectionBackgroundColor);
+            ((GradientDrawable) layerDrawable.getDrawable(INNER_LAYER)).setColor(color);
+        }
     }
 
     private static void refreshColorIconOnSelection(PropertyModel model, View view) {
-        ImageView colorIcon = view.findViewById(R.id.color_picker_icon);
-        LayerDrawable layerDrawable = (LayerDrawable) colorIcon.getBackground();
+        final View colorIcon = view.findViewById(R.id.color_picker_icon);
 
-        // Toggle the selected layer opaqueness based on the user click action.
-        int alpha = model.get(IS_SELECTED) ? 0xFF : 0;
-        layerDrawable.getDrawable(SELECTION_LAYER).setAlpha(alpha);
+        if (isAndroidThemeModuleEnabled()) {
+            ((MaterialButton) colorIcon).setChecked(model.get(IS_SELECTED));
+            colorIcon.setEnabled(!model.get(IS_SELECTED));
+
+            ViewOverlay overlay = colorIcon.getOverlay();
+
+            if (model.get(IS_SELECTED)) {
+                BorderDrawable borderDrawable = getBorderDrawable(model, view);
+                overlay.add(borderDrawable);
+            } else {
+                overlay.clear();
+            }
+        } else {
+            LayerDrawable layerDrawable = (LayerDrawable) colorIcon.getBackground();
+
+            // Toggle the selected layer opaqueness based on the user click action.
+            int alpha = model.get(IS_SELECTED) ? 0xFF : 0;
+            layerDrawable.getDrawable(SELECTION_LAYER).setAlpha(alpha);
+        }
 
         // Refresh the color item view.
         colorIcon.invalidate();
     }
 
+    private static BorderDrawable getBorderDrawable(PropertyModel model, View view) {
+        Resources res = view.getResources();
+
+        int borderWidthPx = res.getDimensionPixelSize(R.dimen.color_picker_button_stroke_width);
+        int insetPx = res.getDimensionPixelSize(R.dimen.color_picker_button_stroke_inset);
+        int borderRadiusPx = res.getDimensionPixelSize(R.dimen.color_picker_button_stroke_radius);
+        int touchTargetSize = res.getDimensionPixelSize(R.dimen.min_touch_target_size);
+
+        BorderDrawable borderDrawable =
+                new BorderDrawable(
+                        borderWidthPx,
+                        insetPx,
+                        getColorPickerDialogBackgroundColor(
+                                view.getContext(), model.get(IS_INCOGNITO)),
+                        borderRadiusPx);
+
+        // Set the bounds of the drawable to match the color button view.
+        borderDrawable.setBounds(0, 0, touchTargetSize, touchTargetSize);
+        return borderDrawable;
+    }
+
     private static @ColorInt int getColor(
             Context context,
             @ColorPickerType int colorPickerType,
@@ -107,7 +157,7 @@
     }
 
     private static void setAccessibilityContent(View view, boolean isSelected, int colorId) {
-        ImageView colorIcon = view.findViewById(R.id.color_picker_icon);
+        View colorIcon = view.findViewById(R.id.color_picker_icon);
         Resources res = view.getContext().getResources();
 
         final @StringRes int colorDescRes =
@@ -122,4 +172,8 @@
         String contentDescription = res.getString(selectedFormatDescRes, colorDesc);
         colorIcon.setContentDescription(contentDescription);
     }
+
+    private static boolean isAndroidThemeModuleEnabled() {
+        return ThemeModuleUtils.isEnabled();
+    }
 }
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 1153127d..c85db0f 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
@@ -1071,8 +1071,7 @@
             RecordUserAction.record("TabGridDialogMenu.ManageSharing");
             mDataSharingTabManager.createOrManageFlow(
                     mActivity,
-                    /* syncId= */ null,
-                    new LocalTabGroupId(tabGroupId),
+                    EitherGroupId.createLocalId(new LocalTabGroupId(tabGroupId)),
                     CollaborationServiceShareOrManageEntryPoint.ANDROID_TAB_GRID_DIALOG_MANAGE,
                     /* createGroupFinishedCallback= */ null);
         } else if (menuId == R.id.recent_activity) {
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java
index fb099d5..27a8227 100644
--- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java
+++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabUiThemeProvider.java
@@ -565,4 +565,19 @@
                 ? SemanticColorUtils.getColorSurfaceBright(context)
                 : SemanticColorUtils.getColorSurfaceContainerLow(context);
     }
+
+    /**
+     * Returns the color used by dialogs as background.
+     *
+     * @param context {@link Context} used to retrieve color.
+     * @param isIncognito Whether the color is used for incognito mode.
+     * @return The color for the dialog background.
+     */
+    public static @ColorInt int getColorPickerDialogBackgroundColor(
+            Context context, boolean isIncognito) {
+        return isIncognito
+                ? ContextCompat.getColor(
+                        context, R.color.tab_group_color_picker_selection_bg_incognito)
+                : SemanticColorUtils.getDialogBgColor(context);
+    }
 }
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 7bd5868..c5a5ee16 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
@@ -44,6 +44,7 @@
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.components.signin.identitymanager.ConsentLevel;
 import org.chromium.components.signin.identitymanager.IdentityManager;
+import org.chromium.components.tab_group_sync.EitherId.EitherGroupId;
 import org.chromium.components.tab_group_sync.LocalTabGroupId;
 import org.chromium.components.tab_group_sync.SavedTabGroup;
 import org.chromium.components.tab_group_sync.TabGroupSyncService;
@@ -322,7 +323,7 @@
         LocalTabGroupId localTabGroupId = TabGroupSyncUtils.getLocalTabGroupId(tab);
 
         dataSharingTabManager.createOrManageFlow(
-                activity, /* syncId= */ null, localTabGroupId, entry, (ignored) -> {});
+                activity, EitherGroupId.createLocalId(localTabGroupId), entry, (ignored) -> {});
     }
 
     /**
diff --git a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutPTTest.java b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutPTTest.java
index a5ed56a3..88878606 100644
--- a/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutPTTest.java
+++ b/chrome/android/features/tab_ui/javatests/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherLayoutPTTest.java
@@ -209,7 +209,6 @@
     @Test
     @MediumTest
     @Feature({"RenderTest"})
-    @DisabledTest(message = "Flaky due to NTP thumbnails not being consistently loaded")
     public void testRenderGrid_3NativeTabs() throws IOException {
         ChromeTabbedActivity cta = mCtaTestRule.getActivity();
         RegularNewTabPageStation pageStation =
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java
index d16a8bdc..c61dd6f3 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ArchivedTabsDialogCoordinatorUnitTest.java
@@ -19,6 +19,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.Activity;
+import android.graphics.drawable.ColorDrawable;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -72,6 +73,7 @@
 import org.chromium.chrome.browser.ui.edge_to_edge.EdgeToEdgeController;
 import org.chromium.chrome.browser.ui.messages.snackbar.SnackbarManager;
 import org.chromium.components.browser_ui.edge_to_edge.EdgeToEdgePadAdjuster;
+import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
 import org.chromium.components.tab_group_sync.LocalTabGroupId;
 import org.chromium.components.tab_group_sync.SavedTabGroup;
@@ -376,4 +378,24 @@
         // Assert that the tab group has a request to open from GTS.
         verify(mTabSwitcherPaneBase).requestOpenTabGroupDialog(TAB1_ID);
     }
+
+    @Test
+    @DisableFeatures(ChromeFeatureList.GRID_TAB_SWITCHER_SURFACE_COLOR_UPDATE)
+    public void testCloseAllTabsButtonBackgroundColor() {
+        mCoordinator.show(mOnTabSelectingListener);
+        FrameLayout buttonContainer = mCoordinator.getCloseAllTabsButtonContainer();
+        assertEquals(
+                SemanticColorUtils.getColorSurface(mActivity),
+                ((ColorDrawable) buttonContainer.getBackground()).getColor());
+    }
+
+    @Test
+    @EnableFeatures(ChromeFeatureList.GRID_TAB_SWITCHER_SURFACE_COLOR_UPDATE)
+    public void testCloseAllTabsButtonBackgroundColorUpdate() {
+        mCoordinator.show(mOnTabSelectingListener);
+        FrameLayout buttonContainer = mCoordinator.getCloseAllTabsButtonContainer();
+        assertEquals(
+                SemanticColorUtils.getColorSurfaceContainerHigh(mActivity),
+                ((ColorDrawable) buttonContainer.getBackground()).getColor());
+    }
 }
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinderUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinderUnitTest.java
index 4e1017a..3f57b04 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinderUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/ColorPickerItemViewBinderUnitTest.java
@@ -7,6 +7,7 @@
 import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
 
 import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertEquals;
 
 import static org.chromium.chrome.browser.tasks.tab_management.ColorPickerItemProperties.COLOR_ID;
 import static org.chromium.chrome.browser.tasks.tab_management.ColorPickerItemProperties.IS_SELECTED;
@@ -16,10 +17,11 @@
 import android.content.res.ColorStateList;
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.LayerDrawable;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageView;
 
+import com.google.android.material.button.MaterialButton;
+
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -28,6 +30,8 @@
 import org.robolectric.annotation.Config;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Features.EnableFeatures;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.components.browser_ui.styles.SemanticColorUtils;
 import org.chromium.components.tab_groups.TabGroupColorId;
 import org.chromium.components.tab_groups.TabGroupColorPickerUtils;
@@ -47,9 +51,8 @@
     @Before
     public void setUp() throws Exception {
         mActivity = Robolectric.buildActivity(Activity.class).setup().get();
-        mColorPickerItemView =
-                LayoutInflater.from(mActivity)
-                        .inflate(R.layout.color_picker_item, /* root= */ null);
+        mActivity.setTheme(R.style.Theme_BrowserUI_DayNight);
+        mColorPickerItemView = ColorPickerItemViewBinder.createItemView(mActivity);
 
         mModel =
                 ColorPickerItemProperties.create(
@@ -88,13 +91,13 @@
         LayerDrawable layerDrawable =
                 (LayerDrawable)
                         mColorPickerItemView.findViewById(R.id.color_picker_icon).getBackground();
-        Assert.assertEquals(3, layerDrawable.getNumberOfLayers());
+        assertEquals(3, layerDrawable.getNumberOfLayers());
 
         // Check outer drawable
         assertThat(layerDrawable.getDrawable(0), instanceOf(GradientDrawable.class));
         GradientDrawable drawable0 = (GradientDrawable) layerDrawable.getDrawable(0);
-        Assert.assertEquals(GradientDrawable.OVAL, drawable0.getShape());
-        Assert.assertEquals(
+        assertEquals(GradientDrawable.OVAL, drawable0.getShape());
+        assertEquals(
                 ColorStateList.valueOf(
                         TabGroupColorPickerUtils.getTabGroupColorPickerItemColor(
                                 mActivity, TabGroupColorId.BLUE, false)),
@@ -103,30 +106,45 @@
         // Check selection drawable
         assertThat(layerDrawable.getDrawable(1), instanceOf(GradientDrawable.class));
         GradientDrawable drawable1 = (GradientDrawable) layerDrawable.getDrawable(1);
-        Assert.assertEquals(GradientDrawable.OVAL, drawable1.getShape());
-        Assert.assertEquals(
+        assertEquals(GradientDrawable.OVAL, drawable1.getShape());
+        assertEquals(
                 ColorStateList.valueOf(SemanticColorUtils.getDialogBgColor(mActivity)),
                 drawable1.getColor());
 
         // Check inner drawable
         assertThat(layerDrawable.getDrawable(2), instanceOf(GradientDrawable.class));
         GradientDrawable drawable2 = (GradientDrawable) layerDrawable.getDrawable(2);
-        Assert.assertEquals(GradientDrawable.OVAL, drawable2.getShape());
-        Assert.assertEquals(
+        assertEquals(GradientDrawable.OVAL, drawable2.getShape());
+        assertEquals(
                 ColorStateList.valueOf(
                         TabGroupColorPickerUtils.getTabGroupColorPickerItemColor(
                                 mActivity, TabGroupColorId.BLUE, false)),
                 drawable2.getColor());
 
         // Check layer insets
-        Assert.assertEquals(faviconOuterInset, layerDrawable.getLayerInsetLeft(1));
-        Assert.assertEquals(faviconOuterInset, layerDrawable.getLayerInsetTop(1));
-        Assert.assertEquals(faviconOuterInset, layerDrawable.getLayerInsetRight(1));
-        Assert.assertEquals(faviconOuterInset, layerDrawable.getLayerInsetBottom(1));
-        Assert.assertEquals(faviconInnerInset, layerDrawable.getLayerInsetLeft(2));
-        Assert.assertEquals(faviconInnerInset, layerDrawable.getLayerInsetTop(2));
-        Assert.assertEquals(faviconInnerInset, layerDrawable.getLayerInsetRight(2));
-        Assert.assertEquals(faviconInnerInset, layerDrawable.getLayerInsetBottom(2));
+        assertEquals(faviconOuterInset, layerDrawable.getLayerInsetLeft(1));
+        assertEquals(faviconOuterInset, layerDrawable.getLayerInsetTop(1));
+        assertEquals(faviconOuterInset, layerDrawable.getLayerInsetRight(1));
+        assertEquals(faviconOuterInset, layerDrawable.getLayerInsetBottom(1));
+        assertEquals(faviconInnerInset, layerDrawable.getLayerInsetLeft(2));
+        assertEquals(faviconInnerInset, layerDrawable.getLayerInsetTop(2));
+        assertEquals(faviconInnerInset, layerDrawable.getLayerInsetRight(2));
+        assertEquals(faviconInnerInset, layerDrawable.getLayerInsetBottom(2));
+    }
+
+    @Test
+    @EnableFeatures({ChromeFeatureList.ANDROID_THEME_MODULE})
+    public void testColorPickerItem_color_withThemeModuleEnabled() {
+        mModel.get(COLOR_ID);
+
+        View colorButton = mColorPickerItemView.findViewById(R.id.color_picker_icon);
+        assertThat(colorButton, instanceOf(MaterialButton.class));
+
+        assertEquals(
+                ColorStateList.valueOf(
+                        TabGroupColorPickerUtils.getTabGroupColorPickerItemColor(
+                                mActivity, TabGroupColorId.BLUE, false)),
+                colorButton.getBackgroundTintList());
     }
 
     @Test
@@ -139,6 +157,16 @@
     }
 
     @Test
+    @EnableFeatures({ChromeFeatureList.ANDROID_THEME_MODULE})
+    public void testColorPickerItem_onClickListener_withThemeModuleEnabled() {
+        mModel.get(ON_CLICK_LISTENER);
+
+        View onClickListener = mColorPickerItemView.findViewById(R.id.color_picker_icon);
+        Assert.assertNotNull(onClickListener);
+        onClickListener.performClick();
+    }
+
+    @Test
     public void testColorPickerItem_isSelected() {
         ImageView imageView = mColorPickerItemView.findViewById(R.id.color_picker_icon);
         LayerDrawable layerDrawable1 = (LayerDrawable) imageView.getBackground();
@@ -155,13 +183,33 @@
                                 .accessibility_tab_group_color_picker_color_item_selected_description,
                         color);
 
-        Assert.assertEquals(0, layerDrawable1.getDrawable(1).getAlpha());
-        Assert.assertEquals(notSelectedString, imageView.getContentDescription());
+        assertEquals(0, layerDrawable1.getDrawable(1).getAlpha());
+        assertEquals(notSelectedString, imageView.getContentDescription());
 
         mModel.set(IS_SELECTED, true);
 
         LayerDrawable layerDrawable2 = (LayerDrawable) imageView.getBackground();
-        Assert.assertEquals(0xFF, layerDrawable2.getDrawable(1).getAlpha());
-        Assert.assertEquals(selectedString, imageView.getContentDescription());
+        assertEquals(0xFF, layerDrawable2.getDrawable(1).getAlpha());
+        assertEquals(selectedString, imageView.getContentDescription());
+    }
+
+    @Test
+    @EnableFeatures({ChromeFeatureList.ANDROID_THEME_MODULE})
+    public void testColorPickerItem_isSelected_withThemeModuleEnabled() {
+        MaterialButton view = mColorPickerItemView.findViewById(R.id.color_picker_icon);
+        String color =
+                mActivity.getString(R.string.accessibility_tab_group_color_picker_color_item_blue);
+        int notSelectedStringId =
+                R.string.accessibility_tab_group_color_picker_color_item_not_selected_description;
+        String notSelectedString = mActivity.getString(notSelectedStringId, color);
+        int selectedStringId =
+                R.string.accessibility_tab_group_color_picker_color_item_selected_description;
+        String selectedString = mActivity.getString(selectedStringId, color);
+
+        assertEquals(notSelectedString, view.getContentDescription());
+
+        mModel.set(IS_SELECTED, true);
+
+        assertEquals(selectedString, view.getContentDescription());
     }
 }
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 b45dd20..6388bbb 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
@@ -134,6 +134,7 @@
 import org.chromium.components.feature_engagement.Tracker;
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.components.signin.identitymanager.IdentityManager;
+import org.chromium.components.tab_group_sync.EitherId.EitherGroupId;
 import org.chromium.components.tab_group_sync.LocalTabGroupId;
 import org.chromium.components.tab_group_sync.SavedTabGroup;
 import org.chromium.components.tab_group_sync.TabGroupSyncService;
@@ -171,6 +172,8 @@
     private static final int POSITION2 = 1;
     private static final Token TAB_GROUP_ID = new Token(1L, 2L);
     private static final LocalTabGroupId LOCAL_TAB_GROUP_ID = new LocalTabGroupId(TAB_GROUP_ID);
+    private static final EitherGroupId EITHER_LOCAL_TAB_GROUP_ID =
+            EitherGroupId.createLocalId(LOCAL_TAB_GROUP_ID);
 
     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
@@ -323,13 +326,11 @@
 
         mModel.get(TabGridDialogProperties.SHARE_BUTTON_CLICK_LISTENER).onClick(null);
         verify(mDataSharingTabManager)
-                .createOrManageFlow(
-                        eq(mActivity), eq(null), eq(LOCAL_TAB_GROUP_ID), anyInt(), any());
+                .createOrManageFlow(eq(mActivity), eq(EITHER_LOCAL_TAB_GROUP_ID), anyInt(), any());
 
         mModel.get(TabGridDialogProperties.SHARE_IMAGE_TILES_CLICK_LISTENER).onClick(null);
         verify(mDataSharingTabManager, times(2))
-                .createOrManageFlow(
-                        eq(mActivity), eq(null), eq(LOCAL_TAB_GROUP_ID), anyInt(), any());
+                .createOrManageFlow(eq(mActivity), eq(EITHER_LOCAL_TAB_GROUP_ID), anyInt(), any());
 
         mModel.get(TabGridDialogProperties.SEND_FEEDBACK_RUNNABLE).run();
         ArgumentCaptor<String> categoryCaptor = ArgumentCaptor.forClass(String.class);
@@ -1525,7 +1526,7 @@
         mMediator.onToolbarMenuItemClick(R.id.manage_sharing, TAB_GROUP_ID, COLLABORATION_ID1);
         assertEquals(1, mActionTester.getActionCount("TabGridDialogMenu.ManageSharing"));
         verify(mDataSharingTabManager)
-                .createOrManageFlow(any(), eq(null), eq(LOCAL_TAB_GROUP_ID), anyInt(), eq(null));
+                .createOrManageFlow(any(), eq(EITHER_LOCAL_TAB_GROUP_ID), anyInt(), eq(null));
     }
 
     @Test
diff --git a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
index 254d03d..f087f5f 100644
--- a/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
+++ b/chrome/android/features/tab_ui/junit/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediatorUnitTest.java
@@ -4363,8 +4363,7 @@
         assertNotNull(mModelList.get(POSITION1).model.get(TabProperties.TAB_ACTION_BUTTON_DATA));
         when(mTabGroupModelFilter.getGroupLastShownTabId(TAB_GROUP_ID)).thenReturn(TAB1_ID);
         mMediator.onMenuItemClicked(R.id.share_group, TAB_GROUP_ID, /* collaborationId= */ null);
-        verify(mDataSharingTabManager)
-                .createOrManageFlow(eq(mActivity), eq(null), any(), anyInt(), any());
+        verify(mDataSharingTabManager).createOrManageFlow(eq(mActivity), any(), anyInt(), any());
     }
 
     @Test
diff --git a/chrome/android/java/res/menu/custom_tabs_menu.xml b/chrome/android/java/res/menu/custom_tabs_menu.xml
index 31416ff..8cd8621f 100644
--- a/chrome/android/java/res/menu/custom_tabs_menu.xml
+++ b/chrome/android/java/res/menu/custom_tabs_menu.xml
@@ -66,6 +66,10 @@
         <item android:id="@+id/disable_price_tracking_menu_id"
             android:title="@string/disable_price_tracking_menu_item"
             android:icon="@drawable/price_tracking_enabled_filled" />
+        <item android:id="@+id/price_insights_menu_id"
+            android:title="@string/price_insights_title"
+            android:icon="@drawable/ic_trending_down_24dp"
+            android:visible="false" />
         <item android:id="@+id/universal_install"
             android:title="@string/menu_add_to_homescreen"
             android:orderInCategory="2" />
diff --git a/chrome/android/java/res/values/attrs.xml b/chrome/android/java/res/values/attrs.xml
index 86aff98..d4b0d48 100644
--- a/chrome/android/java/res/values/attrs.xml
+++ b/chrome/android/java/res/values/attrs.xml
@@ -26,4 +26,8 @@
     <declare-styleable name="HubToolbarView">
         <attr name="newTabButtonStyle" format="reference" />
     </declare-styleable>
+
+     <declare-styleable name="TabGroupColorPickerContainer">
+        <attr name="colorPickerButtonStyle" format="reference" />
+    </declare-styleable>
 </resources>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabModelOrchestrator.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabModelOrchestrator.java
index e1cb8522..4ed3d42f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabModelOrchestrator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/ArchivedTabModelOrchestrator.java
@@ -19,7 +19,6 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.lifetime.Destroyable;
 import org.chromium.base.supplier.ObservableSupplier;
-import org.chromium.base.supplier.ObservableSupplierImpl;
 import org.chromium.base.supplier.Supplier;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.task.TaskTraits;
@@ -35,6 +34,7 @@
 import org.chromium.chrome.browser.tab.tab_restore.HistoricalTabModelObserver;
 import org.chromium.chrome.browser.tab_group_sync.TabGroupSyncServiceFactory;
 import org.chromium.chrome.browser.tab_ui.TabContentManager;
+import org.chromium.chrome.browser.tabmodel.ArchivedTabCountSupplier;
 import org.chromium.chrome.browser.tabmodel.ArchivedTabCreator;
 import org.chromium.chrome.browser.tabmodel.ArchivedTabModelSelectorHolder;
 import org.chromium.chrome.browser.tabmodel.ArchivedTabModelSelectorImpl;
@@ -132,9 +132,6 @@
     private final AsyncTabParamsManager mAsyncTabParamsManager;
     private final ObserverList<Observer> mObservers = new ObserverList<>();
     private final TabWindowManager mTabWindowManager;
-    private final ObservableSupplierImpl<Integer> mTabCountSupplier =
-            new ObservableSupplierImpl<>();
-    private final Callback<Integer> mTabCountSupplierObserver = mTabCountSupplier::set;
     // The set of {@link TabModelOrchestrators}  which have registered themselves as active for
     // declutter.
     private final List<TabbedModeTabModelOrchestrator> mActivityTabModelOrchestrators =
@@ -150,9 +147,10 @@
     private boolean mRestoreTabsCalled;
     private boolean mRescueTabsCalled;
     private CallbackController mCallbackController = new CallbackController();
-    private ObservableSupplier<Integer> mUnderlyingTabCountSupplier;
     private @Nullable HistoricalTabModelObserver mHistoricalTabModelObserver;
     private boolean mTriggerAutodeleteAfterDataCreated;
+    private @Nullable TabGroupSyncService mTabGroupSyncService;
+    private ArchivedTabCountSupplier mArchivedTabCountSupplier;
 
     /**
      * Returns the ArchivedTabModelOrchestrator that corresponds to the given profile. Must be
@@ -240,10 +238,6 @@
             mTabWindowManager.setArchivedTabModelSelector(null);
         }
 
-        if (mUnderlyingTabCountSupplier != null) {
-            mUnderlyingTabCountSupplier.removeObserver(mTabCountSupplierObserver);
-        }
-
         if (mHistoricalTabModelObserver != null) {
             mHistoricalTabModelObserver.destroy();
             mHistoricalTabModelObserver = null;
@@ -254,6 +248,11 @@
             mTabArchiver = null;
         }
 
+        if (mArchivedTabCountSupplier != null) {
+            mArchivedTabCountSupplier.destroy();
+            mArchivedTabCountSupplier = null;
+        }
+
         super.destroy();
     }
 
@@ -300,7 +299,7 @@
 
     /** Returns a supplier for the archive tab count. */
     public ObservableSupplier<Integer> getTabCountSupplier() {
-        return mTabCountSupplier;
+        return mArchivedTabCountSupplier;
     }
 
     public TabModel getTabModel() {
@@ -388,9 +387,7 @@
             observer.onTabModelCreated(model);
         }
 
-        mUnderlyingTabCountSupplier = model.getTabCountSupplier();
-        mTabCountSupplier.set(mUnderlyingTabCountSupplier.get());
-        mUnderlyingTabCountSupplier.addObserver(mTabCountSupplierObserver);
+        mArchivedTabCountSupplier = new ArchivedTabCountSupplier(model, mTabGroupSyncService);
 
         mHistoricalTabModelObserver =
                 new HistoricalTabModelObserver(
@@ -505,8 +502,7 @@
 
         mTabArchiveSettings = new TabArchiveSettings(ChromeSharedPreferences.getInstance());
         mTabArchiveSettings.addObserver(mTabArchiveSettingsObserver);
-        TabGroupSyncService tabGroupSyncService =
-                TabGroupSyncServiceFactory.getForProfile(mProfile);
+        mTabGroupSyncService = TabGroupSyncServiceFactory.getForProfile(mProfile);
         mTabArchiver =
                 new TabArchiverImpl(
                         mTabModelSelector
@@ -515,7 +511,7 @@
                         mArchivedTabCreator,
                         mTabArchiveSettings,
                         System::currentTimeMillis,
-                        tabGroupSyncService);
+                        mTabGroupSyncService);
         mTabArchiver.addObserver(mTabArchiverObserver);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java
index 72911306..18495935 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/layouts/phone/NewTabAnimationLayout.java
@@ -21,7 +21,6 @@
 import android.view.ViewGroup;
 
 import androidx.annotation.ColorInt;
-import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 import androidx.annotation.Px;
 import androidx.annotation.VisibleForTesting;
@@ -42,6 +41,7 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.fullscreen.BrowserControlsManager;
 import org.chromium.chrome.browser.hub.NewTabAnimationUtils;
+import org.chromium.chrome.browser.hub.NewTabAnimationUtils.RectStart;
 import org.chromium.chrome.browser.hub.RoundedCornerAnimatorUtil;
 import org.chromium.chrome.browser.hub.ShrinkExpandAnimator;
 import org.chromium.chrome.browser.hub.ShrinkExpandImageView;
@@ -70,10 +70,6 @@
 import org.chromium.ui.interpolators.Interpolators;
 import org.chromium.ui.resources.ResourceManager;
 
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -84,21 +80,6 @@
  * uses modern UX designs.
  */
 public class NewTabAnimationLayout extends Layout {
-    @IntDef({
-        RectStart.TOP,
-        RectStart.TOP_TOOLBAR,
-        RectStart.BOTTOM,
-        RectStart.BOTTOM_TOOLBAR,
-    })
-    @Target(ElementType.TYPE_USE)
-    @Retention(RetentionPolicy.SOURCE)
-    /*package*/ @interface RectStart {
-        int TOP = 0;
-        int TOP_TOOLBAR = 1;
-        int BOTTOM = 2;
-        int BOTTOM_TOOLBAR = 3;
-    }
-
     private static final long FOREGROUND_ANIMATION_DURATION_MS = 300L;
     private static final long FOREGROUND_FADE_DURATION_MS = 150L;
     private static final long ANIMATION_TIMEOUT_MS = 800L;
@@ -111,6 +92,7 @@
     private final ToolbarManager mToolbarManager;
     private final BrowserControlsManager mBrowserControlsManager;
     private final ObservableSupplier<Boolean> mScrimVisibilitySupplier;
+    private final CustomTabCount mCustomTabCount;
 
     private @Nullable StaticTabSceneLayer mSceneLayer;
     private AnimatorSet mTabCreatedForegroundAnimation;
@@ -121,7 +103,6 @@
     private Runnable mAnimationRunnable;
     private Runnable mTimeoutRunnable;
     private Callback<Boolean> mVisibilityObserver;
-    private CustomTabCount mCustomTabCount;
     private @TabId int mNextTabId = Tab.INVALID_TAB_ID;
     private boolean mSkipForceAnimationToFinish;
 
@@ -430,7 +411,13 @@
      * @param newTab The new {@link Tab} to animate.
      */
     private @RectStart int getForegroundRectStart(Tab oldTab, Tab newTab) {
-        // TODO(crbug.com/40933120): Account for {@code oldTab} being null.
+        @TabLaunchType int tabLaunchType = newTab.getLaunchType();
+        if (oldTab == null
+                || tabLaunchType == TabLaunchType.FROM_LONGPRESS_FOREGROUND
+                || tabLaunchType == TabLaunchType.FROM_LONGPRESS_FOREGROUND_IN_GROUP) {
+            return RectStart.CENTER;
+        }
+
         boolean oldTabHasTopToolbar = ToolbarPositionController.shouldShowToolbarOnTop(oldTab);
         boolean newTabHasTopToolbar = ToolbarPositionController.shouldShowToolbarOnTop(newTab);
 
@@ -495,6 +482,7 @@
      * @param id The id of the new tab to animate.
      * @param sourceId The id of the tab that spawned this new tab.
      * @param newIsIncognito True if the new tab is an incognito tab.
+     * @param rectStart Origin point where the animation starts.
      */
     private void tabCreatedInForeground(
             @TabId int id, @TabId int sourceId, boolean newIsIncognito, @RectStart int rectStart) {
@@ -526,40 +514,50 @@
 
         Rect initialRect = new Rect();
         Rect finalRect = new Rect();
-        RectF compositorViewportRectf = new RectF();
         Rect hostViewRect = new Rect();
-        mCompositorViewHolder.getVisibleViewport(compositorViewportRectf);
-        compositorViewportRectf.round(finalRect);
         mAnimationHostView.getGlobalVisibleRect(hostViewRect);
 
-        boolean isTopAligned = rectStart == RectStart.TOP || rectStart == RectStart.TOP_TOOLBAR;
         int radius =
                 context.getResources()
                         .getDimensionPixelSize(R.dimen.new_tab_animation_rect_corner_radius);
-        int[] startRadii;
+        int[] startRadii = new int[4];
+        Arrays.fill(startRadii, radius);
 
-        // Without adding/subtracting 1px, the origin corner shows a bit of blinking when running
-        // the animation. Doing so ensures the {@link ShrinkExpandImageView} fully covers the origin
-        // corner.
-        if (isTopAligned) {
-            startRadii = new int[] {0, radius, radius, radius};
-            mCompositorViewHolder.getWindowViewport(compositorViewportRectf);
-            finalRect.bottom = Math.round(compositorViewportRectf.bottom);
-            finalRect.top = rectStart == RectStart.TOP ? hostViewRect.top : finalRect.top - 1;
+        if (rectStart != RectStart.CENTER) {
+            RectF compositorViewportRectf = new RectF();
+            mCompositorViewHolder.getVisibleViewport(compositorViewportRectf);
+            compositorViewportRectf.round(finalRect);
+
+            // Without adding/subtracting 1px, the origin corner shows a bit of blinking when
+            // running the animation. Doing so ensures the {@link ShrinkExpandImageView} fully
+            // covers the origin corner.
+            if (rectStart == RectStart.TOP || rectStart == RectStart.TOP_TOOLBAR) {
+                startRadii[0] = 0;
+                mCompositorViewHolder.getWindowViewport(compositorViewportRectf);
+                finalRect.bottom = Math.round(compositorViewportRectf.bottom);
+                finalRect.top =
+                        rectStart == RectStart.TOP ? hostViewRect.top - 1 : finalRect.top - 1;
+            } else {
+                startRadii[2] = 0;
+                finalRect.top = hostViewRect.top;
+                finalRect.bottom =
+                        rectStart == RectStart.BOTTOM
+                                ? hostViewRect.bottom + 1
+                                : finalRect.bottom + 1;
+            }
+            if (isRtl) {
+                finalRect.right += 1;
+            } else {
+                finalRect.left -= 1;
+            }
         } else {
-            startRadii = new int[] {radius, radius, 0, radius};
-            finalRect.top = hostViewRect.top;
-            finalRect.bottom =
-                    rectStart == RectStart.BOTTOM ? hostViewRect.bottom : finalRect.bottom + 1;
+            finalRect = hostViewRect;
+            Rect compositorViewRect = new Rect();
+            mCompositorViewHolder.getGlobalVisibleRect(compositorViewRect);
+            finalRect.bottom -= compositorViewRect.top;
         }
-        if (isRtl) {
-            finalRect.right += 1;
-        } else {
-            finalRect.left -= 1;
-        }
-        // TODO(crbug.com/40933120): Make the initial rect start from the center when opening a tab
-        // from the context menu.
-        NewTabAnimationUtils.updateRects(initialRect, finalRect, isRtl, isTopAligned);
+
+        NewTabAnimationUtils.updateRects(rectStart, isRtl, initialRect, finalRect);
 
         ShrinkExpandAnimator shrinkExpandAnimator =
                 new ShrinkExpandAnimator(
@@ -612,9 +610,13 @@
         mAnimationRunnable =
                 () -> {
                     mAnimationRunnable = null;
+                    // Make View visible once the animation is ready to start.
+                    mRectView.setVisibility(View.VISIBLE);
                     mTabCreatedForegroundAnimation.start();
                 };
 
+        // {@link View#INVISIBLE} is needed to generate the geometry information.
+        mRectView.setVisibility(View.INVISIBLE);
         mAnimationHostView.addView(mRectView);
         mRectView.reset(initialRect);
         mHandler.post(mAnimationRunnable);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
index e7c6d1e..8aaabb1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/overlays/strip/TabGroupContextMenuCoordinator.java
@@ -52,6 +52,7 @@
 import org.chromium.components.collaboration.CollaborationServiceShareOrManageEntryPoint;
 import org.chromium.components.data_sharing.member_role.MemberRole;
 import org.chromium.components.embedder_support.util.UrlConstants;
+import org.chromium.components.tab_group_sync.EitherId.EitherGroupId;
 import org.chromium.components.tab_group_sync.LocalTabGroupId;
 import org.chromium.components.tab_group_sync.TabGroupSyncService;
 import org.chromium.components.tab_groups.TabGroupColorId;
@@ -225,8 +226,7 @@
             } else if (menuId == R.id.manage_sharing) {
                 dataSharingTabManager.createOrManageFlow(
                         activity,
-                        /* syncId= */ null,
-                        new LocalTabGroupId(tabGroupId),
+                        EitherGroupId.createLocalId(new LocalTabGroupId(tabGroupId)),
                         CollaborationServiceShareOrManageEntryPoint
                                 .ANDROID_TAB_GROUP_CONTEXT_MENU_MANAGE,
                         /* createGroupFinishedCallback= */ null);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
index 203c36d..b0c8cf3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabActivity.java
@@ -948,6 +948,7 @@
                 mIntentDataProvider.isOffTheRecord(),
                 isMenuIconAtStart,
                 mBaseCustomTabRootUiCoordinator.getReadAloudControllerSupplier(),
+                mBaseCustomTabRootUiCoordinator::getContextualPageActionController,
                 mIntentDataProvider.getClientPackageNameIdentitySharing() != null);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
index 0778a81..43cef22 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/BaseCustomTabRootUiCoordinator.java
@@ -81,6 +81,7 @@
 import org.chromium.chrome.browser.readaloud.ReadAloudIphController;
 import org.chromium.chrome.browser.reengagement.ReengagementNotificationController;
 import org.chromium.chrome.browser.searchwidget.SearchActivityClientImpl;
+import org.chromium.chrome.browser.segmentation_platform.ContextualPageActionController;
 import org.chromium.chrome.browser.share.ShareDelegate;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.services.SigninPreferencesManager;
@@ -413,7 +414,9 @@
                     new CustomTabToolbarButtonsCoordinator(
                             toolbar,
                             mIntentDataProvider.get(),
-                            params -> mToolbarCoordinator.get().onCustomButtonClick(params));
+                            params -> mToolbarCoordinator.get().onCustomButtonClick(params),
+                            mMinimizeDelegateSupplier.get(),
+                            mFeatureOverridesManagerSupplier.get());
 
             super.initializeToolbar();
 
@@ -567,6 +570,14 @@
         return mCustomTabHistoryIphController;
     }
 
+    public ContextualPageActionController getContextualPageActionController() {
+        return mAdaptiveToolbarUiCoordinator.getContextualPageActionController();
+    }
+
+    public void runPriceInsightsAction() {
+        mAdaptiveToolbarUiCoordinator.runPriceInsightsAction();
+    }
+
     @Override
     public int getControlContainerHeightResource() {
         return R.dimen.custom_tabs_control_container_height;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
index 460fe5dc..5d01dfb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabActivity.java
@@ -308,6 +308,8 @@
                             getTabCreator(getCurrentTabModel().isIncognito()))
                     .show(tab, ChromePageInfoHighlight.noHighlight());
             return true;
+        } else if (id == R.id.price_insights_menu_id) {
+            getBaseCustomTabRootUiCoordinator().runPriceInsightsAction();
         } else if (id == R.id.open_history_menu_id) {
             // The menu is visible only when the app-specific history is enabled. Assert that.
             assert HistoryManager.isAppSpecificHistoryEnabled();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java
index 503abc7..2c592f94 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegate.java
@@ -34,6 +34,7 @@
 import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher;
 import org.chromium.chrome.browser.readaloud.ReadAloudController;
 import org.chromium.chrome.browser.readaloud.ReadAloudFeatures;
+import org.chromium.chrome.browser.segmentation_platform.ContextualPageActionController;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
 import org.chromium.chrome.browser.toolbar.ToolbarManager;
@@ -68,6 +69,7 @@
     private final List<String> mMenuEntries;
     private final Map<String, Integer> mTitleToItemIdMap = new HashMap<String, Integer>();
     private final Map<Integer, Integer> mItemIdToIndexMap = new HashMap<Integer, Integer>();
+    private final Supplier<ContextualPageActionController> mContextualPageActionControllerSupplier;
 
     private boolean mHasClientPackage;
 
@@ -91,6 +93,7 @@
             boolean isOffTheRecord,
             boolean isStartIconMenu,
             Supplier<ReadAloudController> readAloudControllerSupplier,
+            Supplier<ContextualPageActionController> contextualPageActionControllerSupplier,
             boolean hasClientPackage) {
         super(
                 context,
@@ -112,6 +115,7 @@
         mIsIncognitoBranded = isIncognitoBranded;
         mIsOffTheRecord = isOffTheRecord;
         mIsStartIconMenu = isStartIconMenu;
+        mContextualPageActionControllerSupplier = contextualPageActionControllerSupplier;
         mHasClientPackage = hasClientPackage;
     }
 
@@ -284,6 +288,11 @@
                 // TODO(crbug.com/391931899): Also check the dev-controlled flag
                 updatePriceTrackingMenuItemRow(
                         startPriceTrackingMenuItem, stopPriceTrackingMenuItem, currentTab);
+                var cpaController = mContextualPageActionControllerSupplier.get();
+                if (cpaController != null) {
+                    menu.findItem(R.id.price_insights_menu_id)
+                            .setVisible(cpaController.hasPriceInsights());
+                }
             }
 
             boolean showOpenWith = currentTab.isNativePage() && currentTab.getNativePage().isPdf();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java
index b61ae2e..93e7687 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/CustomTabActivityNavigationController.java
@@ -475,11 +475,6 @@
         assert false : assertMsg;
     }
 
-    static void enablePredictiveBackGestureForTesting() {
-        sVersionForTesting = Build.VERSION_CODES.BAKLAVA;
-        ResettersForTesting.register(() -> sVersionForTesting = null);
-    }
-
     public BrowserServicesIntentDataProvider getIntentDataProviderForTesting() {
         return mIntentDataProvider;
     }
@@ -491,4 +486,9 @@
     public Integer getVersionForTesting() {
         return sVersionForTesting;
     }
+
+    public static void enablePredictiveBackGestureForTesting() {
+        sVersionForTesting = Build.VERSION_CODES.BAKLAVA;
+        ResettersForTesting.register(() -> sVersionForTesting = null);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandler.java
index d6199f9..ef73417 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandler.java
@@ -4,6 +4,7 @@
 
 package org.chromium.chrome.browser.customtabs.content;
 
+import static androidx.browser.trusted.LaunchHandlerClientMode.AUTO;
 import static androidx.browser.trusted.LaunchHandlerClientMode.FOCUS_EXISTING;
 import static androidx.browser.trusted.LaunchHandlerClientMode.NAVIGATE_EXISTING;
 import static androidx.browser.trusted.LaunchHandlerClientMode.NAVIGATE_NEW;
@@ -26,6 +27,9 @@
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.browserservices.ui.controller.CurrentPageVerifier;
 import org.chromium.chrome.browser.browserservices.ui.controller.Verifier;
+import org.chromium.chrome.browser.customtabs.content.WebAppLaunchHandlerHistogram.ClientModeAction;
+import org.chromium.chrome.browser.customtabs.content.WebAppLaunchHandlerHistogram.FailureReasonAction;
+import org.chromium.chrome.browser.customtabs.content.WebAppLaunchHandlerHistogram.FileHandlingAction;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.content_public.browser.WebContents;
 
@@ -110,8 +114,15 @@
             String packageName,
             @Nullable FileHandlingData fileHandlingData) {
         List<Uri> fileUris = null;
-        if (fileHandlingData != null) {
+        if (fileHandlingData != null && !fileHandlingData.uris.isEmpty()) {
+            if (fileHandlingData.uris.size() == 1) {
+                WebAppLaunchHandlerHistogram.logFileHandling(FileHandlingAction.SINGLE_FILE);
+            } else {
+                WebAppLaunchHandlerHistogram.logFileHandling(FileHandlingAction.MULTIPLE_FILES);
+            }
             fileUris = fileHandlingData.uris;
+        } else {
+            WebAppLaunchHandlerHistogram.logFileHandling(FileHandlingAction.NO_FILES);
         }
 
         return new WebAppLaunchParams(newNavigationStarted, targetUrl, packageName, fileUris);
@@ -128,6 +139,8 @@
      *     data.
      */
     public void handleInitialIntent(BrowserServicesIntentDataProvider intentDataProvider) {
+        WebAppLaunchHandlerHistogram.logClientMode(ClientModeAction.INITIAL_INTENT);
+
         WebAppLaunchParams launchParams =
                 getLaunchParams(
                         /* newNavigationStarted= */ true,
@@ -150,7 +163,9 @@
      *     data.
      */
     public void handleNewIntent(BrowserServicesIntentDataProvider intentDataProvider) {
-        @ClientMode int clientMode = getClientMode(intentDataProvider.getLaunchHandlerClientMode());
+        @ClientMode int clientModeFromIntent = intentDataProvider.getLaunchHandlerClientMode();
+        recordClientMode(clientModeFromIntent);
+        @ClientMode int clientMode = getClientMode(clientModeFromIntent);
 
         if (clientMode == NAVIGATE_NEW) {
             launchNewIntent(
@@ -176,6 +191,23 @@
         }
     }
 
+    private void recordClientMode(@ClientMode int clientMode) {
+        switch (clientMode) {
+            case NAVIGATE_EXISTING:
+                WebAppLaunchHandlerHistogram.logClientMode(ClientModeAction.MODE_NAVIGATE_EXISTING);
+                break;
+            case FOCUS_EXISTING:
+                WebAppLaunchHandlerHistogram.logClientMode(ClientModeAction.MODE_FOCUS_EXISTING);
+                break;
+            case NAVIGATE_NEW:
+                WebAppLaunchHandlerHistogram.logClientMode(ClientModeAction.MODE_NAVIGATE_NEW);
+                break;
+            case AUTO:
+                WebAppLaunchHandlerHistogram.logClientMode(ClientModeAction.MODE_AUTO);
+                break;
+        }
+    }
+
     /**
      * Launches a new instance of TWA in a separate task. In order to support navigate-new client
      * mode we need to support several running instances of the same TWA app simultaneously in
@@ -212,6 +244,8 @@
             // Launch params should not be sent to a not verified origin.
             CurrentPageVerifier.VerificationState state = mCurrentPageVerifier.getState();
             if (state == null || state.status != CurrentPageVerifier.VerificationStatus.SUCCESS) {
+                WebAppLaunchHandlerHistogram.logFailureReason(
+                        FailureReasonAction.CURRENT_PAGE_VERIFICATION_FAILED);
                 return;
             }
         }
@@ -220,7 +254,11 @@
                 .verify(launchParams.targetUrl)
                 .then(
                         (verified) -> {
-                            if (!verified) return;
+                            if (!verified) {
+                                WebAppLaunchHandlerHistogram.logFailureReason(
+                                        FailureReasonAction.TARGET_URL_VERIFICATION_FAILED);
+                                return;
+                            }
                             WebAppLaunchHandlerJni.get()
                                     .notifyLaunchQueue(
                                             mWebContents,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandlerHistogram.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandlerHistogram.java
new file mode 100644
index 0000000..f9256ba9
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/content/WebAppLaunchHandlerHistogram.java
@@ -0,0 +1,94 @@
+// 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.customtabs.content;
+
+import androidx.annotation.IntDef;
+
+import org.chromium.base.metrics.RecordHistogram;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Records a histogram that tracks usage of Launch Handler API. All actions must be kept in sync
+ * with the definition in tools/metrics/histograms/metadata/custom_tabs/enums.xml.
+ */
+public class WebAppLaunchHandlerHistogram {
+    private WebAppLaunchHandlerHistogram() {}
+
+    private static final String CLIENT_MODE_HISTOGRAM =
+            "TrustedWebActivity.LaunchHandler.ClientMode";
+
+    @IntDef({
+        ClientModeAction.INITIAL_INTENT,
+        ClientModeAction.MODE_NAVIGATE_EXISTING,
+        ClientModeAction.MODE_NAVIGATE_NEW,
+        ClientModeAction.MODE_FOCUS_EXISTING,
+        ClientModeAction.MODE_AUTO,
+        ClientModeAction.COUNT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ClientModeAction {
+        int INITIAL_INTENT = 0;
+        int MODE_NAVIGATE_EXISTING = 1;
+        int MODE_NAVIGATE_NEW = 2;
+        int MODE_FOCUS_EXISTING = 3;
+        int MODE_AUTO = 4;
+
+        /** Total count of entries. */
+        int COUNT = 5;
+    }
+
+    public static void logClientMode(@ClientModeAction int action) {
+        RecordHistogram.recordEnumeratedHistogram(
+                CLIENT_MODE_HISTOGRAM, action, ClientModeAction.COUNT);
+    }
+
+    private static final String FILE_HANDLING_HISTOGRAM =
+            "TrustedWebActivity.LaunchHandler.FileHandling";
+
+    @IntDef({
+        FileHandlingAction.NO_FILES,
+        FileHandlingAction.SINGLE_FILE,
+        FileHandlingAction.MULTIPLE_FILES,
+        FileHandlingAction.COUNT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FileHandlingAction {
+        int NO_FILES = 0;
+        int SINGLE_FILE = 1;
+        int MULTIPLE_FILES = 2;
+
+        /** Total count of entries. */
+        int COUNT = 3;
+    }
+
+    public static void logFileHandling(@FileHandlingAction int action) {
+        RecordHistogram.recordEnumeratedHistogram(
+                FILE_HANDLING_HISTOGRAM, action, FileHandlingAction.COUNT);
+    }
+
+    private static final String FAILURE_REASON_HISTOGRAM =
+            "TrustedWebActivity.LaunchHandler.FailureReason";
+
+    @IntDef({
+        FailureReasonAction.TARGET_URL_VERIFICATION_FAILED,
+        FailureReasonAction.CURRENT_PAGE_VERIFICATION_FAILED,
+        FailureReasonAction.COUNT,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FailureReasonAction {
+        int TARGET_URL_VERIFICATION_FAILED = 0;
+        int CURRENT_PAGE_VERIFICATION_FAILED = 1;
+
+        /** Total count of entries. */
+        int COUNT = 2;
+    }
+
+    public static void logFailureReason(@FailureReasonAction int action) {
+        RecordHistogram.recordEnumeratedHistogram(
+                FAILURE_REASON_HISTOGRAM, action, FailureReasonAction.COUNT);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategy.java
index 73c7a51..ddbb5fc8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabBottomSheetStrategy.java
@@ -50,6 +50,7 @@
 import org.chromium.chrome.browser.customtabs.features.partialcustomtab.ContentGestureListener.GestureState;
 import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbar;
 import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsCoordinator;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 import org.chromium.chrome.browser.fullscreen.FullscreenOptions;
 import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
@@ -346,7 +347,11 @@
                 new PartialCustomTabHandleStrategy(
                         mActivity, this::isFullHeight, () -> mStatus, this);
         toolbar.setHandleStrategy(mHandleStrategy);
-        toolbar.setMinimizeButtonEnabled(false);
+        if (ChromeFeatureList.sCctToolbarRefactor.isEnabled()) {
+            toolbarButtonsCoordinator.setMinimizeButtonVisible(false);
+        } else {
+            toolbar.setMinimizeButtonEnabled(false);
+        }
         CustomTabDragBar dragBar = mActivity.findViewById(R.id.drag_bar);
         dragBar.setHandleStrategy(mHandleStrategy);
         View dragHandle = mActivity.findViewById(R.id.drag_handle);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabFullSizeStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabFullSizeStrategy.java
index d1a4c15..ded6995 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabFullSizeStrategy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabFullSizeStrategy.java
@@ -24,6 +24,7 @@
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbar;
 import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsCoordinator;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.fullscreen.FullscreenManager;
 
 /**
@@ -71,7 +72,11 @@
             CustomTabToolbarButtonsCoordinator toolbarButtonsCoordinator) {
         super.onToolbarInitialized(
                 coordinatorView, toolbar, toolbarCornerRadius, toolbarButtonsCoordinator);
-        toolbar.setMinimizeButtonEnabled(true);
+        if (ChromeFeatureList.sCctToolbarRefactor.isEnabled()) {
+            toolbarButtonsCoordinator.setMinimizeButtonVisible(true);
+        } else {
+            toolbar.setMinimizeButtonEnabled(true);
+        }
         updateDragBarVisibility(/* dragHandlebarVisibility= */ View.GONE);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java
index 9aff197..fb3a958e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/PartialCustomTabSideSheetStrategy.java
@@ -173,7 +173,11 @@
                 toolbar.initSideSheetMaximizeButton(mIsMaximized, () -> toggleMaximize(true));
             }
         }
-        toolbar.setMinimizeButtonEnabled(false);
+        if (ChromeFeatureList.sCctToolbarRefactor.isEnabled()) {
+            mToolbarButtonsCoordinator.setMinimizeButtonVisible(false);
+        } else {
+            toolbar.setMinimizeButtonEnabled(false);
+        }
         updateDragBarVisibility(/* dragHandlebarVisibility= */ View.GONE);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
index 057eb41..231ca84 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbar.java
@@ -197,7 +197,6 @@
     private int mToolbarWidth;
     private BrowserServicesIntentDataProvider mIntentDataProvider;
     private CustomTabMinimizeDelegate mMinimizeDelegate;
-    private Boolean mEnableMinimizeButton;
     private FrameLayout mCustomButtonsParent;
     private PropertyListModel<PropertyModel, PropertyKey> mCustomActionButtonsListModel;
     private boolean mIsInMultiWindowMode;
@@ -405,8 +404,8 @@
             prepareMinimizeButton();
 
             if (mMinimizeButton != null && mMinimizeButton.getVisibility() == VISIBLE) {
-                boolean isEndPosition = mCloseButtonPosition == CLOSE_BUTTON_POSITION_END;
-                positionButton(mMinimizeButton, posParams, mDefaultIconWidth, isEndPosition);
+                // The minimize button is always start aligned.
+                positionButton(mMinimizeButton, posParams, mDefaultIconWidth, false);
             }
         }
 
@@ -791,10 +790,10 @@
     /**
      * Inflates and prepares the minimize button if it should be enabled.
      *
-     * This is only used when CCTToolbarRefactor is enabled.
+     * <p>This is only used when CCTToolbarRefactor is enabled.
      */
     private void prepareMinimizeButton() {
-        if (!isMinimizeButtonEnabled()) return;
+        if (!mMinimizeButtonEnabled) return;
 
         if (isInMultiWindowMode()) {
             if (mMinimizeButton != null) {
@@ -821,24 +820,6 @@
     }
 
     /**
-     * Whether the minimize button should be enabled. A true return value doesn't mean the minimize
-     * button will be visible on the toolbar. The minimize button will be hidden if there isn't
-     * enough space on the toolbar or if the CCT is in multi-window mode.
-     *
-     * This is only used when CCTToolbarRefactor is enabled.
-     */
-    private boolean isMinimizeButtonEnabled() {
-        if (mEnableMinimizeButton != null) return mEnableMinimizeButton;
-
-        mEnableMinimizeButton =
-                MinimizedFeatureUtils.isMinimizedCustomTabAvailable(
-                        getContext(), mFeatureOverridesManager)
-                        && MinimizedFeatureUtils.shouldEnableMinimizedCustomTabs(
-                        mIntentDataProvider);
-        return mEnableMinimizeButton;
-    }
-
-    /**
      * Inflates and prepares the minimize button if it should be enabled, when CCTToolbarRefactor is
      * disabled.
      */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarButtonsCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarButtonsCoordinator.java
index 2e1130ae..06df415a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarButtonsCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarButtonsCoordinator.java
@@ -8,15 +8,21 @@
 import static org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsProperties.DESCRIPTION;
 import static org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsProperties.ICON;
 import static org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsProperties.INDIVIDUAL_BUTTON_KEYS;
+import static org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsProperties.MINIMIZE_BUTTON;
 import static org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsProperties.SIDE_SHEET_MAXIMIZE_BUTTON;
 import static org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsProperties.VISIBLE;
 
 import android.content.Context;
 
 import org.chromium.base.Callback;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
 import org.chromium.chrome.browser.browserservices.intents.CustomButtonParams;
+import org.chromium.chrome.browser.customtabs.CustomTabFeatureOverridesManager;
+import org.chromium.chrome.browser.customtabs.features.minimizedcustomtab.CustomTabMinimizeDelegate;
+import org.chromium.chrome.browser.customtabs.features.minimizedcustomtab.MinimizedFeatureUtils;
 import org.chromium.chrome.browser.customtabs.features.partialcustomtab.PartialCustomTabSideSheetStrategy.MaximizeButtonCallback;
+import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsProperties.MinimizeButtonData;
 import org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsProperties.SideSheetMaximizeButtonData;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.ui.modelutil.ListModelChangeProcessor;
@@ -32,16 +38,31 @@
                     PropertyListModel<PropertyModel, PropertyKey>, CustomTabToolbar, PropertyKey>
             mCustomActionButtonsMcp;
     private final PropertyModel mModel;
+    private final CustomTabMinimizeDelegate mMinimizeDelegate;
+
+    /** Whether the minimize button is available for the device and the current configuration. */
+    private final boolean mMinimizeButtonAvailable;
 
     public CustomTabToolbarButtonsCoordinator(
             CustomTabToolbar view,
             BrowserServicesIntentDataProvider intentDataProvider,
-            Callback<CustomButtonParams> customButtonClickCallback) {
+            Callback<CustomButtonParams> customButtonClickCallback,
+            CustomTabMinimizeDelegate minimizeDelegate,
+            @Nullable CustomTabFeatureOverridesManager featureOverridesManager) {
         CustomTabToolbarButtonsViewBinder viewBinder = new CustomTabToolbarButtonsViewBinder();
         var customActionButtons =
                 getCustomActionButtonsModel(
                         view.getContext(), intentDataProvider, customButtonClickCallback);
-        mModel = CustomTabToolbarButtonsProperties.create(customActionButtons);
+        mMinimizeButtonAvailable =
+                MinimizedFeatureUtils.isMinimizedCustomTabAvailable(
+                                view.getContext(), featureOverridesManager)
+                        && MinimizedFeatureUtils.shouldEnableMinimizedCustomTabs(
+                                intentDataProvider);
+        mMinimizeDelegate = minimizeDelegate;
+        var minimizeButton =
+                getMinimizeButtonData(
+                        mMinimizeButtonAvailable && minimizeDelegate != null, minimizeDelegate);
+        mModel = CustomTabToolbarButtonsProperties.create(customActionButtons, minimizeButton);
         PropertyModelChangeProcessor.create(mModel, view, viewBinder);
         mCustomActionButtonsMcp =
                 new ListModelChangeProcessor<>(
@@ -72,6 +93,13 @@
         mModel.set(SIDE_SHEET_MAXIMIZE_BUTTON, buttonData);
     }
 
+    public void setMinimizeButtonVisible(boolean visible) {
+        assert ChromeFeatureList.sCctToolbarRefactor.isEnabled();
+        mModel.set(
+                MINIMIZE_BUTTON,
+                getMinimizeButtonData(mMinimizeButtonAvailable && visible, mMinimizeDelegate));
+    }
+
     static PropertyListModel<PropertyModel, PropertyKey> getCustomActionButtonsModel(
             Context context,
             BrowserServicesIntentDataProvider intentDataProvider,
@@ -92,4 +120,13 @@
         }
         return listModel;
     }
+
+    private static MinimizeButtonData getMinimizeButtonData(
+            boolean visible, CustomTabMinimizeDelegate minimizeDelegate) {
+        return new MinimizeButtonData(
+                visible,
+                v -> {
+                    if (minimizeDelegate != null) minimizeDelegate.minimize();
+                });
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarButtonsProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarButtonsProperties.java
index 6ef6615..9e0d9c0b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarButtonsProperties.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarButtonsProperties.java
@@ -65,11 +65,31 @@
     public static final WritableObjectPropertyKey<SideSheetMaximizeButtonData>
             SIDE_SHEET_MAXIMIZE_BUTTON = new WritableObjectPropertyKey<>();
 
+    public static class MinimizeButtonData {
+        /** Whether the minimize button is visible. */
+        public final boolean visible;
+
+        /** The {@link OnClickListener} to notify of the click events. */
+        public final OnClickListener clickListener;
+
+        MinimizeButtonData(boolean visible, OnClickListener clickListener) {
+            this.visible = visible;
+            this.clickListener = clickListener;
+        }
+    }
+
+    /** Property key for the minimize button. */
+    public static final WritableObjectPropertyKey<MinimizeButtonData> MINIMIZE_BUTTON =
+            new WritableObjectPropertyKey<>();
+
     public static PropertyModel create(
-            PropertyListModel<PropertyModel, PropertyKey> customActionButtons) {
-        return new PropertyModel.Builder(CUSTOM_ACTION_BUTTONS, SIDE_SHEET_MAXIMIZE_BUTTON)
+            PropertyListModel<PropertyModel, PropertyKey> customActionButtons,
+            MinimizeButtonData minimizeButtonData) {
+        return new PropertyModel.Builder(
+                        CUSTOM_ACTION_BUTTONS, SIDE_SHEET_MAXIMIZE_BUTTON, MINIMIZE_BUTTON)
                 .with(CUSTOM_ACTION_BUTTONS, customActionButtons)
                 .with(SIDE_SHEET_MAXIMIZE_BUTTON, new SideSheetMaximizeButtonData())
+                .with(MINIMIZE_BUTTON, minimizeButtonData)
                 .build();
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarButtonsViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarButtonsViewBinder.java
index 875c568..5043128 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarButtonsViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarButtonsViewBinder.java
@@ -7,6 +7,7 @@
 import static org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsProperties.CUSTOM_ACTION_BUTTONS;
 import static org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsProperties.DESCRIPTION;
 import static org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsProperties.ICON;
+import static org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsProperties.MINIMIZE_BUTTON;
 import static org.chromium.chrome.browser.customtabs.features.toolbar.CustomTabToolbarButtonsProperties.SIDE_SHEET_MAXIMIZE_BUTTON;
 
 import android.support.annotation.DrawableRes;
@@ -39,6 +40,13 @@
         } else if (propertyKey == SIDE_SHEET_MAXIMIZE_BUTTON) {
             prepareSideSheetMaximizeButton(view, model.get(SIDE_SHEET_MAXIMIZE_BUTTON));
             view.reinflateAndRepositionToolbarElements();
+        } else if (propertyKey == MINIMIZE_BUTTON) {
+            view.setMinimizeButtonEnabled(model.get(MINIMIZE_BUTTON).visible);
+            view.reinflateAndRepositionToolbarElements();
+            View minimizeButton = view.findViewById(R.id.custom_tabs_minimize_button);
+            if (minimizeButton != null) {
+                minimizeButton.setOnClickListener(model.get(MINIMIZE_BUTTON).clickListener);
+            }
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayout.java
index 3bffbae2..371c11af 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/hub/HubLayout.java
@@ -578,7 +578,7 @@
 
             initialRect = new Rect();
             NewTabAnimationUtils.updateRects(
-                    initialRect, finalRect, isRtl, /* isTopAligned= */ true);
+                    NewTabAnimationUtils.RectStart.TOP, isRtl, initialRect, finalRect);
             cornerRadius =
                     getContext()
                             .getResources()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/hub/NewTabAnimationUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/hub/NewTabAnimationUtils.java
index 6e744eb..6965e4c9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/hub/NewTabAnimationUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/hub/NewTabAnimationUtils.java
@@ -4,9 +4,11 @@
 package org.chromium.chrome.browser.hub;
 
 import android.content.Context;
+import android.graphics.Point;
 import android.graphics.Rect;
 
 import androidx.annotation.ColorInt;
+import androidx.annotation.IntDef;
 import androidx.core.content.ContextCompat;
 
 import org.chromium.build.annotations.NullMarked;
@@ -15,9 +17,31 @@
 import org.chromium.chrome.browser.theme.ThemeModuleUtils;
 import org.chromium.components.browser_ui.styles.ChromeColors;
 
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
 /** Utilities related to new tab animations. */
 @NullMarked
 public class NewTabAnimationUtils {
+    @IntDef({
+        RectStart.TOP,
+        RectStart.TOP_TOOLBAR,
+        RectStart.BOTTOM,
+        RectStart.BOTTOM_TOOLBAR,
+        RectStart.CENTER
+    })
+    @Target(ElementType.TYPE_USE)
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface RectStart {
+        int TOP = 0;
+        int TOP_TOOLBAR = 1;
+        int BOTTOM = 2;
+        int BOTTOM_TOOLBAR = 3;
+        int CENTER = 4;
+    }
+
     private static final float INITIAL_SCALE = 0.2f;
     private static final float FINAL_SCALE = 1.1f;
 
@@ -47,45 +71,57 @@
      * final {@link Rect} will be scaled by {@link #FINAL_SCALE}. This method takes into account RTL
      * and LTR layout directions.
      *
+     * @param rectStart Origin point where the animation starts.
+     * @param isRtl Whether the Layout direction is RTL or LTR.
      * @param initialRect The initial {@link Rect}. Its values will be overwritten by {@code
      *     finalRect} * {@link #INITIAL_SCALE}.
      * @param finalRect The final {@link Rect} in which the {@code initialRect} will be based on.
      *     Its values will be multiplied by {@link #FINAL_SCALE}.
-     * @param isRtl Whether the Layout direction is RTL or LTR.
-     * @param isTopAligned Whether the {@code initialRect} starts from the top or from the bottom of
-     *     {@code finalRect}.
      */
     public static void updateRects(
-            Rect initialRect, Rect finalRect, boolean isRtl, boolean isTopAligned) {
+            @RectStart int rectStart, boolean isRtl, Rect initialRect, Rect finalRect) {
         int initialWidth = Math.round(finalRect.width() * INITIAL_SCALE);
         int initialHeight = Math.round(finalRect.height() * INITIAL_SCALE);
         int finalWidth = Math.round(finalRect.width() * FINAL_SCALE);
         int finalHeight = Math.round(finalRect.height() * FINAL_SCALE);
 
-        int x;
-        if (isRtl) {
-            x = finalRect.right;
-            initialRect.left = x - initialWidth;
-            initialRect.right = x;
-            finalRect.left = x - finalWidth;
+        if (rectStart == RectStart.CENTER) {
+            Point center = new Point(finalRect.centerX(), finalRect.centerY());
+            updateCenterRect(center, initialWidth, initialHeight, initialRect);
+            updateCenterRect(center, finalWidth, finalHeight, finalRect);
         } else {
-            x = finalRect.left;
-            initialRect.left = x;
-            initialRect.right = x + initialWidth;
-            finalRect.right = x + finalWidth;
-        }
+            int x;
+            if (isRtl) {
+                x = finalRect.right;
+                initialRect.left = x - initialWidth;
+                initialRect.right = x;
+                finalRect.left = x - finalWidth;
+            } else {
+                x = finalRect.left;
+                initialRect.left = x;
+                initialRect.right = x + initialWidth;
+                finalRect.right = x + finalWidth;
+            }
 
-        int y;
-        if (isTopAligned) {
-            y = finalRect.top;
-            initialRect.top = y;
-            initialRect.bottom = y + initialHeight;
-            finalRect.bottom = y + finalHeight;
-        } else {
-            y = finalRect.bottom;
-            initialRect.top = y - initialHeight;
-            initialRect.bottom = y;
-            finalRect.top = y - finalHeight;
+            int y;
+            if (rectStart == RectStart.TOP || rectStart == RectStart.TOP_TOOLBAR) {
+                y = finalRect.top;
+                initialRect.top = y;
+                initialRect.bottom = y + initialHeight;
+                finalRect.bottom = y + finalHeight;
+            } else {
+                y = finalRect.bottom;
+                initialRect.top = y - initialHeight;
+                initialRect.bottom = y;
+                finalRect.top = y - finalHeight;
+            }
         }
     }
+
+    private static void updateCenterRect(Point center, int width, int height, Rect rect) {
+        rect.left = center.x - width / 2;
+        rect.right = rect.left + width;
+        rect.top = center.y - height / 2;
+        rect.bottom = rect.top + height;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/hub/NewTabAnimationUtilsUnitTest.java b/chrome/android/java/src/org/chromium/chrome/browser/hub/NewTabAnimationUtilsUnitTest.java
index 61df4d5..341710c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/hub/NewTabAnimationUtilsUnitTest.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/hub/NewTabAnimationUtilsUnitTest.java
@@ -21,7 +21,7 @@
         Rect finalRect = new Rect(-200, -100, 150, 200);
 
         NewTabAnimationUtils.updateRects(
-                initialRect, finalRect, /* isRtl= */ true, /* isTopAligned= */ true);
+                NewTabAnimationUtils.RectStart.TOP, /* isRtl= */ true, initialRect, finalRect);
 
         Rect expectedInitialRect = new Rect(80, -100, 150, -40);
         Rect expectedFinalRect = new Rect(-235, -100, 150, 230);
@@ -36,7 +36,7 @@
         Rect finalRect = new Rect(-200, -100, 150, 200);
 
         NewTabAnimationUtils.updateRects(
-                initialRect, finalRect, /* isRtl= */ true, /* isTopAligned= */ false);
+                NewTabAnimationUtils.RectStart.BOTTOM, /* isRtl= */ true, initialRect, finalRect);
 
         Rect expectedInitialRect = new Rect(80, 140, 150, 200);
         Rect expectedFinalRect = new Rect(-235, -130, 150, 200);
@@ -51,7 +51,7 @@
         Rect finalRect = new Rect(-200, -100, 150, 200);
 
         NewTabAnimationUtils.updateRects(
-                initialRect, finalRect, /* isRtl= */ false, /* isTopAligned= */ true);
+                NewTabAnimationUtils.RectStart.TOP, /* isRtl= */ false, initialRect, finalRect);
 
         Rect expectedInitialRect = new Rect(-200, -100, -130, -40);
         Rect expectedFinalRect = new Rect(-200, -100, 185, 230);
@@ -66,7 +66,7 @@
         Rect finalRect = new Rect(-200, -100, 150, 200);
 
         NewTabAnimationUtils.updateRects(
-                initialRect, finalRect, /* isRtl= */ false, /* isTopAligned= */ false);
+                NewTabAnimationUtils.RectStart.BOTTOM, /* isRtl= */ false, initialRect, finalRect);
 
         Rect expectedInitialRect = new Rect(-200, 140, -130, 200);
         Rect expectedFinalRect = new Rect(-200, -130, 185, 200);
@@ -74,4 +74,19 @@
         assertEquals(expectedInitialRect, initialRect);
         assertEquals(expectedFinalRect, finalRect);
     }
+
+    @Test
+    public void testUpdateRects_centerAligned() {
+        Rect initialRect = new Rect();
+        Rect finalRect = new Rect(-30, -10, 40, 30);
+
+        NewTabAnimationUtils.updateRects(
+                NewTabAnimationUtils.RectStart.CENTER, /* isRtl= */ false, initialRect, finalRect);
+
+        Rect expectedInitialRect = new Rect(-2, 6, 12, 14);
+        Rect expectedFinalRect = new Rect(-33, -12, 44, 32);
+
+        assertEquals(expectedInitialRect, initialRect);
+        assertEquals(expectedFinalRect, finalRect);
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncher.java
index 7212765..bd25a837 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncher.java
@@ -25,7 +25,6 @@
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.browserservices.intents.SessionHolder;
 import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.components.externalauth.ExternalAuthUtils;
 
@@ -126,9 +125,7 @@
      */
     public static void updateComponentEnabledState(Profile profile) {
         // TODO(peconn): Update state in a few more places (eg CustomTabsConnection#warmup).
-        boolean enable =
-                ChromeFeatureList.isEnabled(ChromeFeatureList.ALLOW_NEW_INCOGNITO_TAB_INTENTS)
-                        && IncognitoUtils.isIncognitoModeEnabled(profile);
+        boolean enable = IncognitoUtils.isIncognitoModeEnabled(profile);
 
         PostTask.postTask(TaskTraits.USER_VISIBLE, () -> setComponentEnabled(enable));
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/segmentation_platform/ContextualPageActionController.java b/chrome/android/java/src/org/chromium/chrome/browser/segmentation_platform/ContextualPageActionController.java
index 0f9ce4e..b1132b1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/segmentation_platform/ContextualPageActionController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/segmentation_platform/ContextualPageActionController.java
@@ -68,6 +68,7 @@
     private ObservableSupplier<Tab> mTabSupplier;
     private final AdaptiveToolbarButtonController mAdaptiveToolbarButtonController;
     private CurrentTabObserver mCurrentTabObserver;
+    private SignalAccumulator mSignalAccumulator;
 
     // The action provider backends.
     protected final List<ActionProvider> mActionProviders = new ArrayList<>();
@@ -140,6 +141,15 @@
         removeProviders();
     }
 
+    /**
+     * @return Whether the page is price insights eligible. The eligibility represents the most
+     *     recent price insights state, which could be from a previous page load or tab. Default is
+     *     false.
+     */
+    public boolean hasPriceInsights() {
+        return mSignalAccumulator == null ? false : mSignalAccumulator.hasPriceInsights();
+    }
+
     private void removeProviders() {
         for (ActionProvider provider : mActionProviders) {
             provider.destroy();
@@ -167,27 +177,27 @@
 
     private void collectSignals(Tab tab) {
         if (mActionProviders.isEmpty()) return;
-        final SignalAccumulator signalAccumulator =
+        mSignalAccumulator =
                 new SignalAccumulator(new Handler(Looper.getMainLooper()), tab, mActionProviders);
-        signalAccumulator.getSignals(() -> findBestAction(signalAccumulator));
+        mSignalAccumulator.getSignals(this::findBestAction);
     }
 
-    private void findBestAction(SignalAccumulator signalAccumulator) {
+    private void findBestAction() {
         Tab tab = getValidActiveTab();
         if (tab == null) return;
         InputContext inputContext = new InputContext();
         inputContext.addEntry(
                 Constants.CONTEXTUAL_PAGE_ACTIONS_PRICE_TRACKING_INPUT,
-                ProcessedValue.fromFloat(signalAccumulator.hasPriceTracking() ? 1.0f : 0.0f));
+                ProcessedValue.fromFloat(mSignalAccumulator.hasPriceTracking() ? 1.0f : 0.0f));
         inputContext.addEntry(
                 Constants.CONTEXTUAL_PAGE_ACTIONS_READER_MODE_INPUT,
-                ProcessedValue.fromFloat(signalAccumulator.hasReaderMode() ? 1.0f : 0.0f));
+                ProcessedValue.fromFloat(mSignalAccumulator.hasReaderMode() ? 1.0f : 0.0f));
         inputContext.addEntry(
                 Constants.CONTEXTUAL_PAGE_ACTIONS_PRICE_INSIGHTS_INPUT,
-                ProcessedValue.fromFloat(signalAccumulator.hasPriceInsights() ? 1.0f : 0.0f));
+                ProcessedValue.fromFloat(mSignalAccumulator.hasPriceInsights() ? 1.0f : 0.0f));
         inputContext.addEntry(
                 Constants.CONTEXTUAL_PAGE_ACTIONS_DISCOUNTS_INPUT,
-                ProcessedValue.fromFloat(signalAccumulator.hasDiscounts() ? 1.0f : 0.0f));
+                ProcessedValue.fromFloat(mSignalAccumulator.hasDiscounts() ? 1.0f : 0.0f));
         inputContext.addEntry("url", ProcessedValue.fromGURL(tab.getUrl()));
 
         ContextualPageActionControllerJni.get()
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
index a532550..ebd0cc3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarManager.java
@@ -450,6 +450,14 @@
         private final CallbackController mCallbackController = new CallbackController();
 
         @Override
+        public boolean invokeBackActionOnEscape() {
+            // Escape key presses should not navigate back in tab history. We do not also implement
+            // a custom {@link BackPressHandler#handleEscPress()} since we don't want anything to
+            // happen and for the manager to move to the next priority handler.
+            return false;
+        }
+
+        @Override
         public int handleBackPress() {
             mIsInProgress = false;
             if (mIsGestureMode && mBackGestureInProgress) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/AdaptiveToolbarUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/AdaptiveToolbarUiCoordinator.java
index bdae239..f337a598 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ui/AdaptiveToolbarUiCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/AdaptiveToolbarUiCoordinator.java
@@ -287,11 +287,21 @@
         return mButtonDataProviders;
     }
 
+    /** Returns {@link ContextualPageActionController} used for adaptive toolbar button. */
+    public ContextualPageActionController getContextualPageActionController() {
+        return mContextualPageActionController;
+    }
+
     /** Returns {@link VoiceToolbarButtonController} used for voice search button. */
     public VoiceToolbarButtonController getVoiceToolbarButtonController() {
         return mVoiceToolbarButtonController;
     }
 
+    /** Invokes Price Insights UI. */
+    public void runPriceInsightsAction() {
+        mAdaptiveToolbarButtonController.runPriceInsightsAction();
+    }
+
     /** Destroy internally used objects. */
     public void destroy() {
         if (mCurrentTabPriceTrackingStateSupplier != null) {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
index 836685ab..c2981a9 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/customtabs/CustomTabActivityTest.java
@@ -99,6 +99,7 @@
 import org.chromium.base.BuildInfo;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.IntentUtils;
+import org.chromium.base.ObserverList;
 import org.chromium.base.ServiceLoaderUtil;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.library_loader.LibraryLoader;
@@ -136,6 +137,7 @@
 import org.chromium.chrome.browser.contextmenu.ContextMenuCoordinator;
 import org.chromium.chrome.browser.customtabs.CustomTabActivityLifecycleUmaTracker.ClientIdentifierType;
 import org.chromium.chrome.browser.customtabs.CustomTabsIntentTestUtils.OnFinishedForTest;
+import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController;
 import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController.FinishReason;
 import org.chromium.chrome.browser.customtabs.features.partialcustomtab.PartialCustomTabBaseStrategy;
 import org.chromium.chrome.browser.customtabs.features.partialcustomtab.PartialCustomTabDisplayManager;
@@ -179,6 +181,7 @@
 import org.chromium.components.browser_ui.widget.CoordinatorLayoutForPointer;
 import org.chromium.components.browser_ui.widget.TintedDrawable;
 import org.chromium.components.browser_ui.widget.gesture.BackPressHandler;
+import org.chromium.components.browser_ui.widget.gesture.OnSystemNavigationObserver;
 import org.chromium.components.content_settings.CookieControlsMode;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.components.page_info.PageInfoController;
@@ -2872,6 +2875,27 @@
 
     @Test
     @SmallTest
+    @Features.EnableFeatures({ChromeFeatureList.CCT_PREDICTIVE_BACK_GESTURE})
+    public void
+            testBackPressManagerAddsSystemNavigationObserver_WhenPredictiveBackGestureIsSupported() {
+        CustomTabActivityNavigationController.enablePredictiveBackGestureForTesting();
+        Context context = getInstrumentation().getTargetContext().getApplicationContext();
+        Intent intent = CustomTabsIntentTestUtils.createMinimalCustomTabIntent(context, mTestPage);
+        mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
+
+        ObserverList<OnSystemNavigationObserver> onSystemNavigationObservers =
+                getActivity().getBackPressManagerForTesting().getObserverListForTesting();
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    Assert.assertTrue(
+                            onSystemNavigationObservers.hasObserver(
+                                    getActivity().getCustomTabActivityNavigationController()));
+                });
+    }
+
+    @Test
+    @SmallTest
     public void disableShareEntriesForAutomotive() {
         mAutomotiveRule.setIsAutomotive(true);
         Intent intent = createMinimalCustomTabIntent();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncherTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncherTest.java
index ae28864..a59f831e 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncherTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoTabLauncherTest.java
@@ -28,12 +28,10 @@
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.DisabledTest;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.Features.EnableFeatures;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
 import org.chromium.chrome.browser.customtabs.CustomTabsTestUtils;
-import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
@@ -47,7 +45,6 @@
 /** Tests for {@link IncognitoTabLauncher}. */
 @RunWith(ChromeJUnit4ClassRunner.class)
 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
-@EnableFeatures({ChromeFeatureList.ALLOW_NEW_INCOGNITO_TAB_INTENTS})
 public class IncognitoTabLauncherTest {
     @Rule
     public final FreshCtaTransitTestRule mActivityRule =
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegateUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegateUnitTest.java
index d94a6d7..bcf75498 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegateUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/CustomTabAppMenuPropertiesDelegateUnitTest.java
@@ -46,6 +46,7 @@
 import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.readaloud.ReadAloudController;
+import org.chromium.chrome.browser.segmentation_platform.ContextualPageActionController;
 import org.chromium.chrome.browser.tab.Tab;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -165,6 +166,7 @@
                         /* isOffTheRecord= */ false,
                         /* isStartIconMenu= */ true,
                         mReadAloudControllerSupplier,
+                        /* contextualPageActionControllerSupplier */ () -> null,
                         /* hasClientPackage= */ false);
         Menu menu = createMenu(context, delegate.getAppMenuLayoutId());
         delegate.prepareMenu(menu, null);
@@ -173,6 +175,42 @@
     }
 
     @Test
+    @EnableFeatures({ChromeFeatureList.CCT_ADAPTIVE_BUTTON})
+    public void enablePriceInsightsMenu() {
+        ContextualPageActionController cpac = mock(ContextualPageActionController.class);
+        doReturn(true).when(cpac).hasPriceInsights();
+
+        Context context =
+                new ContextThemeWrapper(
+                        ContextUtils.getApplicationContext(), R.style.Theme_BrowserUI_DayNight);
+        var delegate =
+                new CustomTabAppMenuPropertiesDelegate(
+                        context,
+                        mActivityTabProvider,
+                        mMultiWindowModeStateDispatcher,
+                        mTabModelSelector,
+                        mToolbarManager,
+                        mDecorView,
+                        mBookmarkModelSupplier,
+                        mVerifier,
+                        CustomTabsUiType.AUTH_TAB,
+                        /* menuEntries= */ new ArrayList<String>(),
+                        /* isOpenedByChrome= */ true,
+                        /* showShare= */ true,
+                        /* showStar= */ true,
+                        /* showDownload= */ true,
+                        /* isIncognitoBranded= */ false,
+                        /* isOffTheRecord= */ false,
+                        /* isStartIconMenu= */ true,
+                        mReadAloudControllerSupplier,
+                        () -> cpac,
+                        /* hasClientPackage= */ false);
+        Menu menu = createMenu(context, delegate.getAppMenuLayoutId());
+        delegate.prepareMenu(menu, null);
+        assertTrue(isMenuVisible(menu, R.id.price_insights_menu_id));
+    }
+
+    @Test
     public void authTabMenuItemVisibility() {
         Context context =
                 new ContextThemeWrapper(
@@ -197,6 +235,7 @@
                         /* isOffTheRecord= */ false,
                         /* isStartIconMenu= */ true,
                         mReadAloudControllerSupplier,
+                        /* contextualPageActionControllerSupplier */ () -> null,
                         /* hasClientPackage= */ false);
         Menu menu = createMenu(context, delegate.getAppMenuLayoutId());
         delegate.prepareMenu(menu, null);
@@ -236,6 +275,7 @@
                         /* isOffTheRecord= */ false,
                         /* isStartIconMenu= */ true,
                         mReadAloudControllerSupplier,
+                        /* contextualPageActionControllerSupplier */ () -> null,
                         /* hasClientPackage= */ false);
         Menu menu = createMenu(context, delegate.getAppMenuLayoutId());
         delegate.prepareMenu(menu, null);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java
index 7eea09c..8ef712b 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/CustomTabToolbarUnitTest.java
@@ -211,8 +211,12 @@
                     CustomTabToolbarButtonsCoordinator.getCustomActionButtonsModel(
                             mActivity, mIntentDataProvider, params -> {});
             mToolbar.setCustomActionButtonsListModel(model);
+            // Minimize button is enabled by default.
+            mToolbar.setMinimizeButtonEnabled(true);
+            mToolbar.reinflateAndRepositionToolbarElements();
+        } else {
+            mToolbar.setFeatureOverridesManager(mFeatureOverridesManager);
         }
-        mToolbar.setFeatureOverridesManager(mFeatureOverridesManager);
 
         mLocationBar =
                 (CustomTabLocationBar)
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt
index 5b74f799..9795381 100644
--- a/chrome/android/profiles/arm.newest.txt
+++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-138.0.7161.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-arm-138.0.7166.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index 5734eb32..b3e118f7 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-138.0.7164.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-138.0.7166.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 3f1e31a1..0176f00 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -18593,6 +18593,9 @@
       <message name="IDS_READING_LIST_TOAST_BUTTON" desc="Button on a toast notification that allows a user to open a reading list entry.">
         Open
       </message>
+      <message name="IDS_CLOSE_PINNED_TAB_TOAST_BODY" desc="Text on a toast notification that is shown when a pinned tab is being closed.">
+        Press <ph name="SHORTCUT_TEXT">$1<ex>⌘ + W</ex></ph> again to close the pinned tab
+      </message>
       <message name="IDS_TOAST_CLOSE_TOOLTIP" desc="Tooltip for the X button on a toast notification that closes the toast.">
         Close
       </message>
diff --git a/chrome/app/generated_resources_grd/IDS_CLOSE_PINNED_TAB_TOAST_BODY.png.sha1 b/chrome/app/generated_resources_grd/IDS_CLOSE_PINNED_TAB_TOAST_BODY.png.sha1
new file mode 100644
index 0000000..445a1220
--- /dev/null
+++ b/chrome/app/generated_resources_grd/IDS_CLOSE_PINNED_TAB_TOAST_BODY.png.sha1
@@ -0,0 +1 @@
+c4c4f529a56f15b99f0c6cc47437b9c66d4b840a
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp
index 5b930bf..ac6616bb 100644
--- a/chrome/app/os_settings_strings.grdp
+++ b/chrome/app/os_settings_strings.grdp
@@ -271,6 +271,9 @@
   <message name="IDS_OS_SETTINGS_FIRMWARE_UP_TO_DATE_DESCRIPTION" desc="Sublabel shown when no firmware updates are available.">
     Firmware is up to date
   </message>
+  <message name="IDS_OS_SETTINGS_FIRMWARE_DISABLED_DESCRIPTION" desc="Sublabel shown when firmware updates are disabled.">
+    Firmware updates are disabled by your administrator.
+  </message>
   <message name="IDS_OS_SETTINGS_FIRMWARE_UPDATE_AVAILABLE_DESCRIPTION" desc="Sublabel shown when firmware updates are available.">
     Update available
   </message>
@@ -779,6 +782,18 @@
   <message name="IDS_SETTINGS_INPUT_METHOD_OPTIONS_KOREAN_SYLLABLE_INPUT">
     Input a syllable at a time
   </message>
+  <message name="IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_CLEAR_PERSONALIZATION_DATA" desc="Label for a heading/button to clear persionalized data from the Japanese IME">
+   Clear personalization data
+  </message>
+  <message name="IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_DELETE_ITEMS" desc="Label for a form for users to select the type of personalized data they wany to Delete">
+   Delete the following items:
+  </message>
+  <message name="IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_CONVERSATION_HISTORY" desc="One of the types of personalization data that can be cleared from the Japanese IME">
+   Conversation history
+  </message>
+  <message name="IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_SUGGESTION_HISTORY" desc="One of the types of personalization data that can be cleared from the Japanese IME">
+   Suggestion history
+  </message>
   <message name="IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_INPUT_MODE" desc="The label for the Input Mode section of the Japanese keyboad settings. As an example, it could be set to have a value of Romaji or Kana. In Japanese, this string should be 'ローマ字入力・かな入力'.">
    Input mode
   </message>
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_FIRMWARE_DISABLED_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_FIRMWARE_DISABLED_DESCRIPTION.png.sha1
new file mode 100644
index 0000000..5381230
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_FIRMWARE_DISABLED_DESCRIPTION.png.sha1
@@ -0,0 +1 @@
+a0a3c173a22b590dd7bae9abfd6eea915212552a
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_CLEAR_PERSONALIZATION_DATA.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_CLEAR_PERSONALIZATION_DATA.png.sha1
new file mode 100644
index 0000000..ccfe6b7
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_CLEAR_PERSONALIZATION_DATA.png.sha1
@@ -0,0 +1 @@
+27f24cce4d51d472931bb2c29107edb8931439ba
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_CONVERSATION_HISTORY.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_CONVERSATION_HISTORY.png.sha1
new file mode 100644
index 0000000..ccfe6b7
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_CONVERSATION_HISTORY.png.sha1
@@ -0,0 +1 @@
+27f24cce4d51d472931bb2c29107edb8931439ba
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_DELETE_ITEMS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_DELETE_ITEMS.png.sha1
new file mode 100644
index 0000000..ccfe6b7
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_DELETE_ITEMS.png.sha1
@@ -0,0 +1 @@
+27f24cce4d51d472931bb2c29107edb8931439ba
\ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_SUGGESTION_HISTORY.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_SUGGESTION_HISTORY.png.sha1
new file mode 100644
index 0000000..ccfe6b7
--- /dev/null
+++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_SUGGESTION_HISTORY.png.sha1
@@ -0,0 +1 @@
+27f24cce4d51d472931bb2c29107edb8931439ba
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 68adbda6..5af9213 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1019,6 +1019,8 @@
     "password_manager/password_change/change_password_form_finder.h",
     "password_manager/password_change/change_password_form_waiter.cc",
     "password_manager/password_change/change_password_form_waiter.h",
+    "password_manager/password_change/model_quality_logs_uploader.cc",
+    "password_manager/password_change/model_quality_logs_uploader.h",
     "password_manager/password_change_delegate.h",
     "password_manager/password_change_delegate_impl.cc",
     "password_manager/password_change_delegate_impl.h",
@@ -4539,6 +4541,7 @@
       "//chrome/browser/importer:impl",
       "//chrome/browser/ui/webui/commerce:impl",
       "//chrome/browser/ui/webui/settings:impl",
+      "//chrome/browser/ui/webui/new_tab_footer:impl",
       "//chrome/browser/ui/webui/signin:signin_impl",
       "//chrome/browser/ui/webui/signin:signin_utils_impl",
       "//chrome/browser/ui/webui/signin:login_impl",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 119e0f4e..77a4ff9c 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3827,32 +3827,6 @@
 
 #endif  // BUILDFLAG(IS_ANDROID)
 
-const FeatureEntry::FeatureParam
-    kPrerender2WarmUpCompositorTriggerPointDidCommitLoad[] = {
-        {"trigger_point", "did_commit_load"}};
-const FeatureEntry::FeatureParam
-    kPrerender2WarmUpCompositorTriggerPointDidDispatchDOMContentLoadedEvent[] =
-        {{"trigger_point", "did_dispatch_dom_content_loaded_event"}};
-const FeatureEntry::FeatureParam
-    kPrerender2WarmUpCompositorTriggerPointDidFinishLoad[] = {
-        {"trigger_point", "did_finish_load"}};
-const FeatureEntry::FeatureVariation
-    kPrerender2WarmUpCompositorTriggerPointVariations[] = {
-        {"(on DidCommitLoad)",
-         kPrerender2WarmUpCompositorTriggerPointDidCommitLoad,
-         std::size(kPrerender2WarmUpCompositorTriggerPointDidCommitLoad),
-         nullptr},
-        {"(on DOMContentLoaded)",
-         kPrerender2WarmUpCompositorTriggerPointDidDispatchDOMContentLoadedEvent,
-         std::size(
-             kPrerender2WarmUpCompositorTriggerPointDidDispatchDOMContentLoadedEvent),
-         nullptr},
-        {"(on DidFinishLoad)",
-         kPrerender2WarmUpCompositorTriggerPointDidFinishLoad,
-         std::size(kPrerender2WarmUpCompositorTriggerPointDidFinishLoad),
-         nullptr},
-};
-
 const FeatureEntry::FeatureParam kGroupSuggestionEnableRecentlyOpenedOnly[] = {
     {"group_suggestion_enable_recently_opened", "true"},
     {"group_suggestion_enable_switch_between", "false"},
@@ -4864,9 +4838,6 @@
     {"enable-gpu-rasterization", flag_descriptions::kGpuRasterizationName,
      flag_descriptions::kGpuRasterizationDescription, kOsAll,
      MULTI_VALUE_TYPE(kEnableGpuRasterizationChoices)},
-    {"enable-fontations-backend", flag_descriptions::kFontationsFontBackendName,
-     flag_descriptions::kFontationsFontBackendDescription, kOsAll,
-     FEATURE_VALUE_TYPE(blink::features::kFontationsFontBackend)},
     {"enable-experimental-web-platform-features",
      flag_descriptions::kExperimentalWebPlatformFeaturesName,
      flag_descriptions::kExperimentalWebPlatformFeaturesDescription, kOsAll,
@@ -8990,18 +8961,6 @@
      FEATURE_VALUE_TYPE(
          blink::features::kPrerender2EarlyDocumentLifecycleUpdate)},
 
-    {"warm-up-compositor", flag_descriptions::kWarmUpCompositorName,
-     flag_descriptions::kWarmUpCompositorDescription, kOsAll,
-     FEATURE_VALUE_TYPE(features::kWarmUpCompositor)},
-
-    {"prerender2-warm-up-compositor",
-     flag_descriptions::kPrerender2WarmUpCompositorName,
-     flag_descriptions::kPrerender2WarmUpCompositorDescription, kOsAll,
-     FEATURE_WITH_PARAMS_VALUE_TYPE(
-         blink::features::kPrerender2WarmUpCompositor,
-         kPrerender2WarmUpCompositorTriggerPointVariations,
-         "Prerender2WarmUpCompositor")},
-
 #if BUILDFLAG(IS_ANDROID)
     {"prerender2-new-tab-page-android",
      flag_descriptions::kPrerender2ForNewTabPageAndroidName,
@@ -10867,6 +10826,11 @@
      FEATURE_VALUE_TYPE(
          autofill::features::kAutofillEnableSyncingOfPixBankAccounts)},
 
+    {"enable-pix-account-linking",
+     flag_descriptions::kEnablePixAccountLinkingName,
+     flag_descriptions::kEnablePixAccountLinkingDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(payments::facilitated::kEnablePixAccountLinking)},
+
     {"enable-pix-payments", flag_descriptions::kEnablePixPaymentsName,
      flag_descriptions::kEnablePixPaymentsDescription, kOsAndroid,
      FEATURE_VALUE_TYPE(payments::facilitated::kEnablePixPayments)},
@@ -12427,6 +12391,12 @@
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) ||
         // BUILDFLAG(IS_CHROME_OS)
 
+#if !BUILDFLAG(IS_ANDROID)
+    {"pinned-tab-toast-on-close", flag_descriptions::kPinnedTabToastOnCloseName,
+     flag_descriptions::kPinnedTabToastOnCloseDescription, kOsDesktop,
+     FEATURE_VALUE_TYPE(toast_features::kPinnedTabToastOnClose)},
+#endif  // !BUILDFLAG(IS_ANDROID)
+
     // Add new entries above this line.
 
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
diff --git a/chrome/browser/accessibility/accessibility_labels_service.cc b/chrome/browser/accessibility/accessibility_labels_service.cc
index d2b2ae2..9a6b3a383 100644
--- a/chrome/browser/accessibility/accessibility_labels_service.cc
+++ b/chrome/browser/accessibility/accessibility_labels_service.cc
@@ -26,7 +26,6 @@
 #include "content/public/browser/scoped_accessibility_mode.h"
 #include "content/public/browser/web_contents.h"
 #include "google_apis/google_api_keys.h"
-#include "services/data_decoder/public/cpp/data_decoder.h"
 #include "services/image_annotation/image_annotation_service.h"
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/ax_enums.mojom.h"
@@ -59,12 +58,6 @@
 
   ~ImageAnnotatorClient() override = default;
 
-  // image_annotation::Annotator::Client implementation:
-  void BindJsonParser(mojo::PendingReceiver<data_decoder::mojom::JsonParser>
-                          receiver) override {
-    data_decoder_.GetService()->BindJsonParser(std::move(receiver));
-  }
-
   std::vector<std::string> GetAcceptLanguages() override {
     std::vector<std::string> accept_languages;
     const PrefService* pref_service = profile_->GetPrefs();
@@ -113,7 +106,6 @@
 
  private:
   const raw_ptr<Profile> profile_;
-  data_decoder::DataDecoder data_decoder_;
 };
 
 }  // namespace
diff --git a/chrome/browser/actor/actor_test_util.cc b/chrome/browser/actor/actor_test_util.cc
index 17ed264e..6c78fe9 100644
--- a/chrome/browser/actor/actor_test_util.cc
+++ b/chrome/browser/actor/actor_test_util.cc
@@ -65,7 +65,9 @@
   TypeAction* type_action = action.add_action_information()->mutable_type();
   type_action->mutable_target()->set_content_node_id(content_node_id);
   type_action->set_text(text);
-  type_action->set_mode(TypeAction_TypeMode::TypeAction_TypeMode_APPEND);
+  // TODO(crbug.com/409570203): Tests should set a mode.
+  type_action->set_mode(
+      TypeAction_TypeMode::TypeAction_TypeMode_UNKNOWN_TYPE_MODE);
   type_action->set_follow_by_enter(follow_by_enter);
   return action;
 }
diff --git a/chrome/browser/actor/tools/tools_browsertest.cc b/chrome/browser/actor/tools/tools_browsertest.cc
index d1604c9..987fa80 100644
--- a/chrome/browser/actor/tools/tools_browsertest.cc
+++ b/chrome/browser/actor/tools/tools_browsertest.cc
@@ -433,6 +433,64 @@
   EXPECT_TRUE(result.Get());
 }
 
+// Ensure that the default mode is for the type tool to replace any existing
+// text in the targeted element.
+IN_PROC_BROWSER_TEST_F(ActorToolsTest, TypeTool_ReplacesText) {
+  const GURL url = embedded_test_server()->GetURL("/actor/input.html");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
+
+  ASSERT_TRUE(ExecJs(web_contents(),
+                     "document.getElementById('input').value = 'foo bar'"));
+  std::optional<int> input_id = GetDOMNodeId(*main_frame(), "#input");
+  ASSERT_TRUE(input_id);
+
+  std::string typed_string = "abc";
+  BrowserAction action =
+      MakeType(input_id.value(), typed_string, /*follow_by_enter=*/false);
+
+  TestFuture<bool> result;
+  actor_coordinator().Act(action, result.GetCallback());
+  EXPECT_TRUE(result.Get());
+  EXPECT_EQ(typed_string,
+            EvalJs(web_contents(), "document.getElementById('input').value"));
+}
+
+// Ensure that if the page moves focus immediately to a different input box, the
+// type tool correctly operates on the new input box.
+IN_PROC_BROWSER_TEST_F(ActorToolsTest, TypeTool_FocusMovesFocus) {
+  const GURL url = embedded_test_server()->GetURL("/actor/input.html");
+  ASSERT_TRUE(content::NavigateToURL(web_contents(), url));
+
+  // Setup the first input box to immediately move focus to the second input
+  // box. Ensure the existing text in the second box is replaced.
+  ASSERT_TRUE(ExecJs(web_contents(),
+                     R"JS(
+                        let input = document.getElementById('input');
+                        let input2 = document.getElementById('input2');
+                        input2.value = 'foo bar';
+                        input.addEventListener('focus', () => {
+                          input2.focus();
+                        });
+                      )JS"));
+  std::optional<int> input_id = GetDOMNodeId(*main_frame(), "#input");
+  ASSERT_TRUE(input_id);
+
+  std::string typed_string = "abc";
+  BrowserAction action =
+      MakeType(input_id.value(), typed_string, /*follow_by_enter=*/false);
+
+  TestFuture<bool> result;
+  actor_coordinator().Act(action, result.GetCallback());
+  EXPECT_TRUE(result.Get());
+
+  // Since focusing the first input causes the second input to become focused,
+  // the tool should operate on the second input.
+  EXPECT_EQ("",
+            EvalJs(web_contents(), "document.getElementById('input').value"));
+  EXPECT_EQ(typed_string,
+            EvalJs(web_contents(), "document.getElementById('input2').value"));
+}
+
 // ===============================================
 // Mouse Move Tool
 // ===============================================
diff --git a/chrome/browser/apps/app_service/BUILD.gn b/chrome/browser/apps/app_service/BUILD.gn
index 22021d7b..b7335ae 100644
--- a/chrome/browser/apps/app_service/BUILD.gn
+++ b/chrome/browser/apps/app_service/BUILD.gn
@@ -69,6 +69,7 @@
     "//chrome/browser/resources:app_icon_resources",
     "//chrome/browser/sync",
     "//chrome/browser/ui:browser_navigator_params_headers",
+    "//chrome/browser/ui/browser_window",
     "//chrome/browser/ui/tabs:tab_enums",
     "//chrome/browser/web_applications",
     "//chrome/browser/web_applications:features",
diff --git a/chrome/browser/apps/app_service/launch_utils.cc b/chrome/browser/apps/app_service/launch_utils.cc
index 9c2dc8e..e3768dd 100644
--- a/chrome/browser/apps/app_service/launch_utils.cc
+++ b/chrome/browser/apps/app_service/launch_utils.cc
@@ -19,10 +19,10 @@
 #include "chrome/browser/apps/app_service/intent_util.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_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/tabs/tab_enums.h"
 #include "chrome/browser/web_applications/link_capturing_features.h"
 #include "chrome/common/chrome_switches.h"
@@ -33,6 +33,8 @@
 #include "components/services/app_service/public/cpp/app_update.h"
 #include "components/services/app_service/public/cpp/intent_util.h"
 #include "components/sessions/core/session_id.h"
+#include "components/tabs/public/tab_interface.h"
+#include "content/public/browser/web_contents.h"
 #include "extensions/common/constants.h"
 #include "mojo/public/cpp/bindings/struct_ptr.h"
 #include "storage/browser/file_system/file_system_url.h"
@@ -267,12 +269,14 @@
     return SessionID::InvalidValue().id();
   }
 
-  Browser* browser = chrome::FindBrowserWithTab(web_contents);
+  const tabs::TabInterface* tab =
+      tabs::TabInterface::GetFromContents(web_contents);
+  const BrowserWindowInterface* browser = tab->GetBrowserWindowInterface();
   if (!browser) {
     return SessionID::InvalidValue().id();
   }
 
-  return browser->session_id().id();
+  return browser->GetSessionID().id();
 }
 
 #if BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h b/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h
index fee3712..95dd498 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h
@@ -88,17 +88,17 @@
 extern const int kDurationBuckets;
 extern const int kUsageTimeBuckets;
 
-constexpr char kArcHistogramName[] = "Arc";
-constexpr char kCrostiniHistogramName[] = "Crostini";
-constexpr char kChromeAppHistogramName[] = "ChromeApp";
-constexpr char kWebAppHistogramName[] = "WebApp";
-constexpr char kPluginVmHistogramName[] = "PluginVm";
-constexpr char kRemoteHistogramName[] = "RemoteApp";
-constexpr char kBorealisHistogramName[] = "Borealis";
-constexpr char kSystemWebAppHistogramName[] = "SystemWebApp";
-constexpr char kChromeBrowserHistogramName[] = "ChromeBrowser";
-constexpr char kExtensionHistogramName[] = "Extension";
-constexpr char kBruschettaHistogramName[] = "Bruschetta";
+inline constexpr char kArcHistogramName[] = "Arc";
+inline constexpr char kCrostiniHistogramName[] = "Crostini";
+inline constexpr char kChromeAppHistogramName[] = "ChromeApp";
+inline constexpr char kWebAppHistogramName[] = "WebApp";
+inline constexpr char kPluginVmHistogramName[] = "PluginVm";
+inline constexpr char kRemoteHistogramName[] = "RemoteApp";
+inline constexpr char kBorealisHistogramName[] = "Borealis";
+inline constexpr char kSystemWebAppHistogramName[] = "SystemWebApp";
+inline constexpr char kChromeBrowserHistogramName[] = "ChromeBrowser";
+inline constexpr char kExtensionHistogramName[] = "Extension";
+inline constexpr char kBruschettaHistogramName[] = "Bruschetta";
 
 // Determines what app type a web app should be logged as based on its launch
 // container and app id. In particular, web apps in tabs are logged as part of
diff --git a/chrome/browser/apps/app_service/policy_util.h b/chrome/browser/apps/app_service/policy_util.h
index 186fae33..f1bbedd8 100644
--- a/chrome/browser/apps/app_service/policy_util.h
+++ b/chrome/browser/apps/app_service/policy_util.h
@@ -35,7 +35,7 @@
 namespace apps_util {
 
 #if BUILDFLAG(IS_CHROMEOS)
-constexpr char kVirtualTaskPrefix[] = "VirtualTask/";
+inline constexpr char kVirtualTaskPrefix[] = "VirtualTask/";
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 // Checks whether |policy_id| specifies a Chrome App.
diff --git a/chrome/browser/ash/arc/file_system_watcher/arc_file_system_watcher_util.h b/chrome/browser/ash/arc/file_system_watcher/arc_file_system_watcher_util.h
index 774c78b..42a89ffa 100644
--- a/chrome/browser/ash/arc/file_system_watcher/arc_file_system_watcher_util.h
+++ b/chrome/browser/ash/arc/file_system_watcher/arc_file_system_watcher_util.h
@@ -11,7 +11,7 @@
 
 // The removable media path in ChromeOS. This is the actual directory to be
 // watched.
-constexpr base::FilePath::CharType kCrosRemovableMediaDir[] =
+inline constexpr base::FilePath::CharType kCrosRemovableMediaDir[] =
     FILE_PATH_LITERAL("/media/removable");
 
 // The prefix for device label used in Android paths for removable media.
diff --git a/chrome/browser/ash/arc/input_overlay/constants.h b/chrome/browser/ash/arc/input_overlay/constants.h
index 0f28439..c1b867a6 100644
--- a/chrome/browser/ash/arc/input_overlay/constants.h
+++ b/chrome/browser/ash/arc/input_overlay/constants.h
@@ -29,7 +29,7 @@
 // Maximum of actions size.
 inline constexpr size_t kMaxActionCount = 50;
 
-constexpr char16_t kUnknownBind[] = u"?";
+inline constexpr char16_t kUnknownBind[] = u"?";
 
 // Directions from up, left, down, right.
 constexpr int kDirection[kActionMoveKeysSize][kAxisSize] = {{0, -1},
diff --git a/chrome/browser/ash/arc/input_overlay/ui/delete_edit_shortcut.cc b/chrome/browser/ash/arc/input_overlay/ui/delete_edit_shortcut.cc
index 1da39182..761f420 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/delete_edit_shortcut.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/delete_edit_shortcut.cc
@@ -47,7 +47,7 @@
                                       // TODO(b/329895423): Add shadow.
                                       views::BubbleBorder::NO_SHADOW),
       controller_(controller) {
-  set_background_color(cros_tokens::kCrosSysSystemBaseElevatedOpaque);
+  SetBackgroundColor(cros_tokens::kCrosSysSystemBaseElevatedOpaque);
   set_margins(gfx::Insets(12));
   set_corner_radius(20);
   set_close_on_deactivate(false);
diff --git a/chrome/browser/ash/arc/input_overlay/ui/rich_nudge.cc b/chrome/browser/ash/arc/input_overlay/ui/rich_nudge.cc
index 27633e8..e2ac218 100644
--- a/chrome/browser/ash/arc/input_overlay/ui/rich_nudge.cc
+++ b/chrome/browser/ash/arc/input_overlay/ui/rich_nudge.cc
@@ -35,7 +35,7 @@
                                       views::BubbleBorder::FLOAT,
                                       views::BubbleBorder::NO_SHADOW) {
   set_parent_window(parent_window);
-  set_background_color(SK_ColorTRANSPARENT);
+  SetBackgroundColor(SK_ColorTRANSPARENT);
   set_margins(gfx::Insets());
   set_close_on_deactivate(false);
   set_accept_events(false);
diff --git a/chrome/browser/ash/auth/BUILD.gn b/chrome/browser/ash/auth/BUILD.gn
index 697db1ef..1ffce35 100644
--- a/chrome/browser/ash/auth/BUILD.gn
+++ b/chrome/browser/ash/auth/BUILD.gn
@@ -22,7 +22,6 @@
   deps = [
     "//ash/constants",
     "//base",
-    "//chrome/browser:browser_process",
     "//chrome/browser/ash/login/quick_unlock",
     "//chrome/browser/ash/login/users",
     "//chrome/browser/ash/profiles",
diff --git a/chrome/browser/ash/auth/DEPS b/chrome/browser/ash/auth/DEPS
index 0804487..36f5e29 100644
--- a/chrome/browser/ash/auth/DEPS
+++ b/chrome/browser/ash/auth/DEPS
@@ -13,6 +13,5 @@
   "+chrome/browser/ash/login/quick_unlock",
   "+chrome/browser/ash/login/users",
   "+chrome/browser/ash/profiles",
-  "+chrome/browser/browser_process.h",
   "+chrome/browser/profiles",
 ]
diff --git a/chrome/browser/ash/auth/cryptohome_pin_engine.cc b/chrome/browser/ash/auth/cryptohome_pin_engine.cc
index 704995b..912854a 100644
--- a/chrome/browser/ash/auth/cryptohome_pin_engine.cc
+++ b/chrome/browser/ash/auth/cryptohome_pin_engine.cc
@@ -5,11 +5,12 @@
 #include "chrome/browser/ash/auth/cryptohome_pin_engine.h"
 
 #include "ash/constants/ash_pref_names.h"
+#include "base/check_deref.h"
 #include "base/check_op.h"
 #include "base/containers/contains.h"
+#include "base/memory/raw_ref.h"
 #include "chrome/browser/ash/login/users/chrome_user_manager_util.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
-#include "chrome/browser/browser_process.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chromeos/ash/components/login/auth/auth_performer.h"
 #include "components/account_id/account_id.h"
@@ -52,8 +53,8 @@
 }
 
 // Read the salt from local state.
-std::string GetUserSalt(const AccountId& account_id) {
-  user_manager::KnownUser known_user(g_browser_process->local_state());
+std::string GetUserSalt(PrefService& local_state, const AccountId& account_id) {
+  user_manager::KnownUser known_user(&local_state);
   if (const std::string* salt =
           known_user.FindStringPath(account_id, prefs::kQuickUnlockPinSalt)) {
     return *salt;
@@ -63,8 +64,10 @@
 
 }  // namespace
 
-CryptohomePinEngine::CryptohomePinEngine(ash::AuthPerformer* auth_performer)
-    : auth_performer_(auth_performer),
+CryptohomePinEngine::CryptohomePinEngine(PrefService* local_state,
+                                         ash::AuthPerformer* auth_performer)
+    : local_state_(CHECK_DEREF(local_state)),
+      auth_performer_(auth_performer),
       auth_factor_editor_(ash::UserDataAuthClient::Get()) {}
 
 CryptohomePinEngine::~CryptohomePinEngine() = default;
@@ -121,7 +124,7 @@
     const cryptohome::RawPin& pin,
     std::unique_ptr<UserContext> user_context,
     AuthOperationCallback callback) {
-  auto salt = GetUserSalt(user_context->GetAccountId());
+  auto salt = GetUserSalt(local_state_.get(), user_context->GetAccountId());
   auth_performer_->AuthenticateWithPin(*pin, salt, std::move(user_context),
                                        std::move(callback));
 }
diff --git a/chrome/browser/ash/auth/cryptohome_pin_engine.h b/chrome/browser/ash/auth/cryptohome_pin_engine.h
index e597c7e..4a37999 100644
--- a/chrome/browser/ash/auth/cryptohome_pin_engine.h
+++ b/chrome/browser/ash/auth/cryptohome_pin_engine.h
@@ -10,11 +10,14 @@
 #include <string>
 
 #include "base/functional/callback_forward.h"
+#include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
 #include "chromeos/ash/components/cryptohome/common_types.h"
 #include "chromeos/ash/components/login/auth/auth_factor_editor.h"
 #include "chromeos/ash/components/login/auth/auth_performer.h"
 
+class PrefService;
+
 namespace ash {
 
 class UserContext;
@@ -26,7 +29,9 @@
  public:
   enum class Purpose { kAny, kUnlock, kWebAuthn };
 
-  explicit CryptohomePinEngine(ash::AuthPerformer* auth_performer);
+  // `local_state` must be non-null and must outlive `this`.
+  CryptohomePinEngine(PrefService* local_state,
+                      ash::AuthPerformer* auth_performer);
   CryptohomePinEngine(const CryptohomePinEngine&) = delete;
   CryptohomePinEngine& operator=(const CryptohomePinEngine&) = delete;
   virtual ~CryptohomePinEngine();
@@ -64,6 +69,8 @@
                                      std::unique_ptr<UserContext> user_context,
                                      std::optional<AuthenticationError> error);
 
+  const raw_ref<PrefService> local_state_;
+
   // Non owning pointer
   const raw_ptr<ash::AuthPerformer> auth_performer_;
 
diff --git a/chrome/browser/ash/cert_provisioning/BUILD.gn b/chrome/browser/ash/cert_provisioning/BUILD.gn
index dc4b4c4..6d341d3 100644
--- a/chrome/browser/ash/cert_provisioning/BUILD.gn
+++ b/chrome/browser/ash/cert_provisioning/BUILD.gn
@@ -55,6 +55,7 @@
     "//chromeos/ash/components/cryptohome",
     "//chromeos/ash/components/dbus/attestation",
     "//chromeos/ash/components/dbus/attestation:attestation_proto",
+    "//chromeos/ash/components/kcer",
     "//chromeos/dbus/common",
     "//chromeos/dbus/tpm_manager",
     "//chromeos/dbus/tpm_manager:tpm_manager_proto",
diff --git a/chrome/browser/ash/cert_provisioning/cert_provisioning_worker_dynamic.cc b/chrome/browser/ash/cert_provisioning/cert_provisioning_worker_dynamic.cc
index 0aff375..0cd67919 100644
--- a/chrome/browser/ash/cert_provisioning/cert_provisioning_worker_dynamic.cc
+++ b/chrome/browser/ash/cert_provisioning/cert_provisioning_worker_dynamic.cc
@@ -34,6 +34,7 @@
 #include "chrome/browser/ash/platform_keys/platform_keys_service_factory.h"
 #include "chrome/browser/chromeos/platform_keys/platform_keys.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chromeos/ash/components/kcer/kcer_utils.h"
 #include "components/policy/core/common/cloud/device_management_service.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "content/public/browser/browser_context.h"
@@ -835,7 +836,23 @@
     return;
   }
 
-  signature_ = std::move(signature);
+  if (signature_algorithm_ ==
+      em::CertProvSignatureAlgorithm::SIGNATURE_ALGORITHM_ECDSA_SHA256) {
+    base::expected<std::vector<uint8_t>, kcer::Error> asn1_ec_signature =
+        kcer::ReencodeEcSignatureAsAsn1(signature);
+    if (!asn1_ec_signature.has_value()) {
+      failure_message_no_pii_ =
+          base::StringPrintf("Failed to re-encode ECC signature, error: %d",
+                             static_cast<int>(asn1_ec_signature.error()));
+      FINAL_STATE_EXPECTED(
+          UpdateState(FROM_HERE, CertProvisioningWorkerState::kFailed));
+      return;
+    }
+    signature_ = std::move(asn1_ec_signature).value();
+  } else {
+    signature_ = std::move(signature);
+  }
+
   RETURN_ON_FINAL_STATE(
       UpdateState(FROM_HERE, CertProvisioningWorkerState::kSignCsrFinished));
   DoStep();
diff --git a/chrome/browser/ash/cert_provisioning/cert_provisioning_worker_dynamic_unittest.cc b/chrome/browser/ash/cert_provisioning/cert_provisioning_worker_dynamic_unittest.cc
index 887a4a0..e4d5fda 100644
--- a/chrome/browser/ash/cert_provisioning/cert_provisioning_worker_dynamic_unittest.cc
+++ b/chrome/browser/ash/cert_provisioning/cert_provisioning_worker_dynamic_unittest.cc
@@ -171,6 +171,12 @@
 constexpr char kChallenge[] = "fake_va_challenge_1";
 constexpr char kChallengeResponse[] = "fake_va_challenge_response_1";
 constexpr char kSignatureBase64[] = "AQIDBAU=";
+// The signature was recorded from the code under test. A real CA successfully
+// issued a cert for it, so it should be correct. It is not related to the
+// kPublicKeyEcBase64 key, but it shouldn't matter for these unit tests.
+constexpr char kEccSignatureAsn1Base64[] =
+    "MEQCIHRmp42nHk9m/rx4cITQE7lkYG9NVFXQQgQHHOzmbMZhAiB/c/"
+    "D3K3fFFeprb+IKs4cYLzX5d3JsGDXAca/eCzyaTg==";
 constexpr unsigned int kNonVaKeyModulusLengthBits = 2048;
 constexpr char kEcNamedCurve[] = "P-256";
 
@@ -228,6 +234,22 @@
   return std::vector<uint8_t>({1, 2, 3, 4, 5});
 }
 
+std::vector<uint8_t> GetEccSignatureRawBin() {
+  // The raw values from the kEccSignatureAsn1Base64 signature (concatenated to
+  // each other) without the ASN.1 structure. This should be a realistic example
+  // of what is returned from the SIgnEcdsa method.
+  return base::Base64Decode(
+             "dGanjaceT2b+vHhwhNATuWRgb01UVdBCBAcc7OZsxmF/c/"
+             "D3K3fFFeprb+IKs4cYLzX5d3JsGDXAca/eCzyaTg==")
+      .value();
+}
+
+std::string GetEccSignatureAsn1Str() {
+  std::vector<uint8_t> asn1_signature =
+      base::Base64Decode(kEccSignatureAsn1Base64).value();
+  return std::string(asn1_signature.begin(), asn1_signature.end());
+}
+
 std::vector<uint8_t> GetCertProfileIdBin() {
   // -1 because of '\0'.
   return std::vector<uint8_t>(kCertProfileId,
@@ -418,7 +440,8 @@
   {                                                                         \
     EXPECT_CALL(*platform_keys_service_, SIGN_FUNC)                         \
         .Times(1)                                                           \
-        .WillOnce(RunOnceCallback<4>(GetSignatureBin(), Status::kSuccess)); \
+        .WillOnce(                                                          \
+            RunOnceCallback<4>(GetEccSignatureRawBin(), Status::kSuccess)); \
   }
 
 #define EXPECT_IMPORT_CERTIFICATE_OK(IMPORT_FUNC)        \
@@ -1006,7 +1029,7 @@
 
     EXPECT_UPLOAD_PROOF_OF_POSSESSION(
         UploadProofOfPossession(Eq(std::ref(provisioning_process)),
-                                GetSignatureStr(),
+                                GetEccSignatureAsn1Str(),
                                 /*callback=*/_),
         NoDataResultOk());
 
@@ -1343,7 +1366,7 @@
 
     EXPECT_UPLOAD_PROOF_OF_POSSESSION(
         UploadProofOfPossession(Eq(std::ref(provisioning_process)),
-                                GetSignatureStr(),
+                                GetEccSignatureAsn1Str(),
                                 /*callback=*/_),
         NoDataResultOk());
 
@@ -1779,7 +1802,7 @@
         chromeos::platform_keys::HASH_ALGORITHM_SHA256, /*callback=*/_));
     EXPECT_UPLOAD_PROOF_OF_POSSESSION(
         UploadProofOfPossession(Eq(std::ref(provisioning_process)),
-                                GetSignatureStr(),
+                                GetEccSignatureAsn1Str(),
                                 /*callback=*/_),
         NoDataResultOk());
     EXPECT_GET_NEXT_INSTRUCTION(
@@ -1957,7 +1980,7 @@
 
     EXPECT_UPLOAD_PROOF_OF_POSSESSION(
         UploadProofOfPossession(Eq(std::ref(provisioning_process)),
-                                GetSignatureStr(),
+                                GetEccSignatureAsn1Str(),
                                 /*callback=*/_),
         NoDataResultOk());
 
@@ -2260,7 +2283,7 @@
 
     EXPECT_UPLOAD_PROOF_OF_POSSESSION(
         UploadProofOfPossession(Eq(std::ref(provisioning_process)),
-                                GetSignatureStr(),
+                                GetEccSignatureAsn1Str(),
                                 /*callback=*/_),
         NoDataResultOk());
 
@@ -2415,7 +2438,7 @@
 
     EXPECT_UPLOAD_PROOF_OF_POSSESSION(
         UploadProofOfPossession(Eq(std::ref(provisioning_process)),
-                                GetSignatureStr(),
+                                GetEccSignatureAsn1Str(),
                                 /*callback=*/_),
         NoDataResultOk());
 
@@ -2659,7 +2682,7 @@
 
     EXPECT_UPLOAD_PROOF_OF_POSSESSION(
         UploadProofOfPossession(Eq(std::ref(provisioning_process)),
-                                GetSignatureStr(),
+                                GetEccSignatureAsn1Str(),
                                 /*callback=*/_),
         NoDataResultOk());
 
@@ -3047,7 +3070,7 @@
 
     EXPECT_UPLOAD_PROOF_OF_POSSESSION(
         UploadProofOfPossession(Eq(std::ref(provisioning_process)),
-                                GetSignatureStr(),
+                                GetEccSignatureAsn1Str(),
                                 /*callback=*/_),
         NoDataResultOk());
 
@@ -3346,7 +3369,7 @@
 
     EXPECT_UPLOAD_PROOF_OF_POSSESSION(
         UploadProofOfPossession(Eq(std::ref(provisioning_process)),
-                                GetSignatureStr(),
+                                GetEccSignatureAsn1Str(),
                                 /*callback=*/_),
         NoDataResultOk());
 
@@ -4306,7 +4329,7 @@
 
     EXPECT_UPLOAD_PROOF_OF_POSSESSION(
         UploadProofOfPossession(Eq(std::ref(provisioning_process)),
-                                GetSignatureStr(),
+                                GetEccSignatureAsn1Str(),
                                 /*callback=*/_),
         base::unexpected(
             DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
@@ -4317,7 +4340,7 @@
   {
     EXPECT_UPLOAD_PROOF_OF_POSSESSION(
         UploadProofOfPossession(Eq(std::ref(provisioning_process)),
-                                GetSignatureStr(),
+                                GetEccSignatureAsn1Str(),
                                 /*callback=*/_),
         NoDataResultOk());
     FastForwardBy(kRequestRetryInitialDelay + kSmallDelay);
@@ -5334,12 +5357,12 @@
             "state": 6
           }
         })",
-        process_id.c_str(), kSignatureBase64, kPublicKeyEcBase64));
+        process_id.c_str(), kEccSignatureAsn1Base64, kPublicKeyEcBase64));
     EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1);
 
     EXPECT_UPLOAD_PROOF_OF_POSSESSION(
         UploadProofOfPossession(Eq(std::ref(provisioning_process)),
-                                GetSignatureStr(),
+                                GetEccSignatureAsn1Str(),
                                 /*callback=*/_),
         base::unexpected(
             DmStatusError(policy::DM_STATUS_TEMPORARY_UNAVAILABLE)));
@@ -5367,7 +5390,7 @@
     testing::InSequence seq;
     EXPECT_UPLOAD_PROOF_OF_POSSESSION(
         UploadProofOfPossession(Eq(std::ref(provisioning_process)),
-                                GetSignatureStr(),
+                                GetEccSignatureAsn1Str(),
                                 /*callback=*/_),
         NoDataResultOk());
 
diff --git a/chrome/browser/ash/crosapi/structured_metrics_service_ash.h b/chrome/browser/ash/crosapi/structured_metrics_service_ash.h
index 32a4ba8b..552a0c35 100644
--- a/chrome/browser/ash/crosapi/structured_metrics_service_ash.h
+++ b/chrome/browser/ash/crosapi/structured_metrics_service_ash.h
@@ -12,9 +12,12 @@
 
 namespace crosapi {
 
-// Implements the StructuredMetricsService mojo interface to record events.
-// Wrapper to validate and record structured metrics received from lacros. Lives
-// on the UI thread.
+// Implements the StructuredMetricsService mojo interface to record events from
+// AshStructuredMetricsDelegate. Wrapper to validate and record structured
+// metrics. Lives on the UI thread. Although both AshStructuredMetricsDelegate
+// and StructuredMetricsServiceAsh live in the same Ash process, instantiating a
+// mojo pipe adds little overhead and provides lots of benefits out of the box
+// (ie message buffer).
 class StructuredMetricsServiceAsh final
     : public mojom::StructuredMetricsService {
  public:
diff --git a/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc b/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc
index 37c807d..d99113c 100644
--- a/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/ash/extensions/autotest_private/autotest_private_api.cc
@@ -4820,7 +4820,9 @@
 };
 
 AutotestPrivateInstallPWAForCurrentURLFunction::
-    AutotestPrivateInstallPWAForCurrentURLFunction() = default;
+    AutotestPrivateInstallPWAForCurrentURLFunction()
+    : auto_accept_pwa_install_confirmation_(
+          web_app::SetAutoAcceptPWAInstallConfirmationForTesting()) {}
 AutotestPrivateInstallPWAForCurrentURLFunction::
     ~AutotestPrivateInstallPWAForCurrentURLFunction() = default;
 
@@ -4869,7 +4871,6 @@
       base::BindOnce(
           &AutotestPrivateInstallPWAForCurrentURLFunction::PWAInstalled, this));
 
-  web_app::SetAutoAcceptPWAInstallConfirmationForTesting(true);
   if (!chrome::ExecuteCommand(browser, IDC_INSTALL_PWA)) {
     return Respond(Error("Failed to execute INSTALL_PWA command"));
   }
@@ -4877,13 +4878,11 @@
 
 void AutotestPrivateInstallPWAForCurrentURLFunction::PWAInstalled(
     const webapps::AppId& app_id) {
-  web_app::SetAutoAcceptPWAInstallConfirmationForTesting(false);
   Respond(WithArguments(app_id));
   timeout_timer_.Stop();
 }
 
 void AutotestPrivateInstallPWAForCurrentURLFunction::PWATimeout() {
-  web_app::SetAutoAcceptPWAInstallConfirmationForTesting(false);
   Respond(Error("Install PWA timed out"));
 }
 
diff --git a/chrome/browser/ash/extensions/autotest_private/autotest_private_api.h b/chrome/browser/ash/extensions/autotest_private/autotest_private_api.h
index 4ea5b95..5bb0d21 100644
--- a/chrome/browser/ash/extensions/autotest_private/autotest_private_api.h
+++ b/chrome/browser/ash/extensions/autotest_private/autotest_private_api.h
@@ -1222,6 +1222,7 @@
   std::unique_ptr<PWABannerObserver> banner_observer_;
   std::unique_ptr<PWAInstallManagerObserver> install_mananger_observer_;
   base::OneShotTimer timeout_timer_;
+  base::AutoReset<bool> auto_accept_pwa_install_confirmation_;
 };
 
 class AutotestPrivateActivateAcceleratorFunction : public ExtensionFunction {
diff --git a/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_setup_screen.cc b/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_setup_screen.cc
index 825c82c..95b2551 100644
--- a/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_setup_screen.cc
+++ b/chrome/browser/ash/login/screens/osauth/cryptohome_recovery_setup_screen.cc
@@ -57,7 +57,9 @@
       view_(std::move(view)),
       exit_callback_(std::move(exit_callback)),
       auth_performer_(UserDataAuthClient::Get()),
-      cryptohome_pin_engine_(&auth_performer_) {}
+      // TODO(crbug.com/404133029): Remove g_browser_process usage.
+      cryptohome_pin_engine_(g_browser_process->local_state(),
+                             &auth_performer_) {}
 
 CryptohomeRecoverySetupScreen::~CryptohomeRecoverySetupScreen() = default;
 
diff --git a/chrome/browser/ash/login/screens/pin_setup_screen.cc b/chrome/browser/ash/login/screens/pin_setup_screen.cc
index dd2485b..a241263 100644
--- a/chrome/browser/ash/login/screens/pin_setup_screen.cc
+++ b/chrome/browser/ash/login/screens/pin_setup_screen.cc
@@ -22,6 +22,7 @@
 #include "chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h"
 #include "chrome/browser/ash/login/users/chrome_user_manager_util.h"
 #include "chrome/browser/ash/login/wizard_context.h"
+#include "chrome/browser/browser_process.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/ui/webui/ash/login/pin_setup_screen_handler.h"
@@ -127,7 +128,9 @@
       view_(std::move(view)),
       exit_callback_(exit_callback),
       auth_performer_(UserDataAuthClient::Get()),
-      cryptohome_pin_engine_(&auth_performer_) {
+      // TODO(crbug.com/404133029): Remove g_browser_process usage.
+      cryptohome_pin_engine_(g_browser_process->local_state(),
+                             &auth_performer_) {
   DCHECK(view_);
 
   quick_unlock::PinBackend::GetInstance()->HasLoginSupport(base::BindOnce(
diff --git a/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.cc b/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.cc
index 372ad570..26cc0a47 100644
--- a/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.cc
+++ b/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.cc
@@ -22,6 +22,7 @@
 #include "components/omnibox/browser/actions/omnibox_extension_action.h"
 #include "components/omnibox/browser/autocomplete_input.h"
 #include "components/omnibox/browser/autocomplete_match_classification.h"
+#include "components/omnibox/browser/suggestion_group_util.h"
 #include "components/omnibox/browser/unscoped_extension_provider.h"
 #include "components/omnibox/browser/vector_icons.h"  // nogncheck
 #include "extensions/browser/extension_util.h"
@@ -32,6 +33,10 @@
 constexpr size_t kMaxSuggestionsPerExtension = 4;
 // LINT.ThenChange(//components/omnibox/browser/autocomplete_grouper_sections.cc)
 
+// Unscoped Extension suggestions are grouped after all other suggestions. But
+// they still need to score within top N suggestions to be shown.
+constexpr int kUnscopedExtensionRelevance = 2000;
+
 constexpr auto kReservedGroupIdMap =
     base::MakeFixedFlatMap<size_t, omnibox::GroupId>(
         {{0, omnibox::GROUP_UNSCOPED_EXTENSION_1},
@@ -65,6 +70,9 @@
     std::set<std::string> unscoped_mode_extension_ids) {
   CHECK(extension_suggest_matches_.empty());
   CHECK(extension_id_to_group_id_map_.empty());
+  first_suggestion_relevance_ =
+      input.IsZeroSuggest() ? omnibox::kUnscopedExtensionZeroSuggestRelevance
+                            : kUnscopedExtensionRelevance;
 
   for (const std::string& extension_id : unscoped_mode_extension_ids) {
     if (!IsEnabledExtension(extension_id)) {
@@ -136,10 +144,10 @@
   group.set_header_text(base::UTF16ToUTF8(template_url->keyword()));
   provider_->AddToSuggestionGroupsMap(current_group_id, std::move(group));
 
-  int first_relevance = 10000000;
   for (const auto& suggestion : suggestions) {
-    extension_suggest_matches_.push_back(
-        CreateAutocompleteMatch(suggestion, --first_relevance, extension_id));
+    CHECK_GE(first_suggestion_relevance_, 0);
+    extension_suggest_matches_.push_back(CreateAutocompleteMatch(
+        suggestion, first_suggestion_relevance_--, extension_id));
   }
 
   ACMatches* matches = provider_->matches();
diff --git a/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.h b/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.h
index 5572b6fe..c16461ca 100644
--- a/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.h
+++ b/chrome/browser/autocomplete/unscoped_extension_provider_delegate_impl.h
@@ -82,6 +82,10 @@
   // incoming later with a stale request ID.
   int current_request_id_ = 0;
 
+  // The first relevance score to assign to the suggestions for the current
+  // request for suggestions.
+  int first_suggestion_relevance_ = 0;
+
   // Current list of matches received from the extensions. Used to update the
   // list of matches in the provider.
   std::vector<AutocompleteMatch> extension_suggest_matches_;
diff --git a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/AddressEditorMediator.java b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/AddressEditorMediator.java
index df81a7a..1ad31d6a 100644
--- a/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/AddressEditorMediator.java
+++ b/chrome/browser/autofill/android/java/src/org/chromium/chrome/browser/autofill/editors/AddressEditorMediator.java
@@ -205,7 +205,7 @@
                         .with(FOOTER_MESSAGE, getRecordTypeNoticeText())
                         .with(DELETE_CONFIRMATION_TITLE, getDeleteConfirmationTitle())
                         .with(DELETE_CONFIRMATION_TEXT, getDeleteConfirmationText())
-                        .with(SHOW_REQUIRED_INDICATOR, false)
+                        .with(SHOW_REQUIRED_INDICATOR, true)
                         .with(
                                 EDITOR_FIELDS,
                                 buildEditorFieldList(
diff --git a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/AddressEditorTest.java b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/AddressEditorTest.java
index 09c91c9..37d18c01 100644
--- a/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/AddressEditorTest.java
+++ b/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/AddressEditorTest.java
@@ -11,8 +11,8 @@
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.nullValue;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -271,7 +271,7 @@
             @Nullable String expectedRecordTypeNotice) {
         assertNotNull(editorModel);
 
-        assertFalse(editorModel.get(SHOW_REQUIRED_INDICATOR));
+        assertTrue(editorModel.get(SHOW_REQUIRED_INDICATOR));
         assertEquals(expectedDeleteTitle, editorModel.get(DELETE_CONFIRMATION_TITLE));
         assertEquals(expectedDeleteText, editorModel.get(DELETE_CONFIRMATION_TEXT));
         assertEquals(expectedRecordTypeNotice, editorModel.get(FOOTER_MESSAGE));
diff --git a/chrome/browser/autofill/automated_tests/cache_replayer.h b/chrome/browser/autofill/automated_tests/cache_replayer.h
index 8b9610cc..4492ae5 100644
--- a/chrome/browser/autofill/automated_tests/cache_replayer.h
+++ b/chrome/browser/autofill/automated_tests/cache_replayer.h
@@ -49,7 +49,7 @@
 // using the cached responses from the wpr archive. The valid values match the
 // enum AutofillServerBehaviorType below. Options are:
 // SavedCache, ProductionServer, or OnlyLocalHeuristics.
-constexpr char kAutofillServerBehaviorParam[] = "autofill-server-type";
+inline constexpr char kAutofillServerBehaviorParam[] = "autofill-server-type";
 enum class AutofillServerBehaviorType {
   kSavedCache,          // Uses cached responses. This is the Default.
   kProductionServer,    // Connects to live Autofill Server for recommendations.
diff --git a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManager.java b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManager.java
index ae24396..1137b53 100644
--- a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManager.java
+++ b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManager.java
@@ -105,7 +105,6 @@
             }
         }
 
-        if (mFallbackOnBackPressed != null) mFallbackOnBackPressed.run();
         assert !failed : "Callback is enabled but didn't consume the esc.";
         return null;
     }
@@ -490,6 +489,10 @@
         mLastCalledHandlerType = -1;
     }
 
+    public ObserverList<OnSystemNavigationObserver> getObserverListForTesting() {
+        return mOnSystemNavigationObservers;
+    }
+
     public static String getHistogramForTesting() {
         return HISTOGRAM;
     }
diff --git a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerTest.java b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerTest.java
index d7057e6..399828b3 100644
--- a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerTest.java
+++ b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerTest.java
@@ -464,6 +464,48 @@
                 });
     }
 
+    @Test
+    @SmallTest
+    public void testEscapePressesDoNotUseFallback() {
+        BackPressManager manager = new BackPressManager();
+
+        EscModifyingBackPressHandler h1 =
+                ThreadUtils.runOnUiThreadBlocking(
+                        () -> new EscModifyingBackPressHandler(Boolean.FALSE));
+        EscModifyingBackPressHandler h2 =
+                ThreadUtils.runOnUiThreadBlocking(() -> new EscModifyingBackPressHandler(null));
+
+        // Fail if the BackPressManager calls the fallback method, which it shouldn't.
+        manager.setFallbackOnBackPressed(
+                () -> {
+                    assert false
+                            : "BackPressManager should not call fallback on escape key presses.";
+                });
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    manager.addHandler(h1, 2);
+                    manager.addHandler(h2, 4);
+                    h1.getHandleBackPressChangedSupplier().set(true);
+                    h2.getHandleBackPressChangedSupplier().set(true);
+                });
+
+        ThreadUtils.runOnUiThreadBlocking(
+                () -> {
+                    Assert.assertNull(
+                            "Manager should not have found any handlers to consume Esc.",
+                            manager.processEscapeKeyEvent());
+                    Assert.assertEquals(
+                            "Handler did not execute custom esc key code even though it will fail.",
+                            1,
+                            h1.getCallbackHelper().getCallCount());
+                    Assert.assertEquals(
+                            "Handler did not execute custom esc key code even though it will fail.",
+                            1,
+                            h2.getCallbackHelper().getCallCount());
+                });
+    }
+
     // Trigger back press ignoring built-in assertion errors.
     private void triggerBackPressWithoutAssertionError(BackPressManager manager) {
         ThreadUtils.runOnUiThreadBlocking(
diff --git a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerUnitTest.java b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerUnitTest.java
index d73d482e..916f34e 100644
--- a/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerUnitTest.java
+++ b/chrome/browser/back_press/android/java/src/org/chromium/chrome/browser/back_press/BackPressManagerUnitTest.java
@@ -595,6 +595,37 @@
                 h4.getCallbackHelper().getCallCount());
     }
 
+    @Test
+    public void testEscapePressesDoNotUseFallback() {
+        BackPressManager manager = new BackPressManager();
+        EscapeBackPressHandlerFailure h1 = new EscapeBackPressHandlerFailure();
+        EscapeBackPressHandlerFailure h2 = new EscapeBackPressHandlerFailure();
+
+        // Fail if the BackPressManager calls the fallback method, which it shouldn't.
+        manager.setFallbackOnBackPressed(
+                () -> {
+                    assert false
+                            : "BackPressManager should not call fallback on escape key presses.";
+                });
+
+        manager.addHandler(h1, 3);
+        manager.addHandler(h2, 6);
+        h1.getHandleBackPressChangedSupplier().set(true);
+        h2.getHandleBackPressChangedSupplier().set(true);
+
+        Assert.assertNull(
+                "Manager should not have found any handlers to consume Esc.",
+                manager.processEscapeKeyEvent());
+        Assert.assertEquals(
+                "Handler did not execute custom esc key code even though it will fail.",
+                1,
+                h1.getCallbackHelper().getCallCount());
+        Assert.assertEquals(
+                "Handler did not execute back press code even though it will fall through.",
+                1,
+                h2.getCallbackHelper().getCallCount());
+    }
+
     private int getHandlerCount(BackPressManager manager) {
         int count = 0;
         for (BackPressHandler handler : manager.getHandlersForTesting()) {
diff --git a/chrome/browser/banners/app_banner_manager_desktop_browsertest.cc b/chrome/browser/banners/app_banner_manager_desktop_browsertest.cc
index 03e7dbd..3a03b68 100644
--- a/chrome/browser/banners/app_banner_manager_desktop_browsertest.cc
+++ b/chrome/browser/banners/app_banner_manager_desktop_browsertest.cc
@@ -53,7 +53,9 @@
 class AppBannerManagerDesktopBrowserTest
     : public AppBannerManagerBrowserTestBase {
  public:
-  AppBannerManagerDesktopBrowserTest() = default;
+  AppBannerManagerDesktopBrowserTest()
+      : auto_accept_pwa_install_confirmation_(
+            web_app::SetAutoAcceptPWAInstallConfirmationForTesting()) {}
 
   void SetUp() override {
     TestAppBannerManagerDesktop::SetUp();
@@ -61,19 +63,16 @@
   }
 
   void SetUpOnMainThread() override {
-    web_app::SetAutoAcceptPWAInstallConfirmationForTesting(true);
-
     AppBannerManagerBrowserTestBase::SetUpOnMainThread();
   }
 
-  void TearDown() override {
-    web_app::SetAutoAcceptPWAInstallConfirmationForTesting(false);
-  }
-
   AppBannerManagerDesktopBrowserTest(
       const AppBannerManagerDesktopBrowserTest&) = delete;
   AppBannerManagerDesktopBrowserTest& operator=(
       const AppBannerManagerDesktopBrowserTest&) = delete;
+
+ private:
+  base::AutoReset<bool> auto_accept_pwa_install_confirmation_;
 };
 
 IN_PROC_BROWSER_TEST_F(AppBannerManagerDesktopBrowserTest,
@@ -230,10 +229,8 @@
   }
 
   // Install the app via the menu instead of the banner.
-  web_app::SetAutoAcceptPWAInstallConfirmationForTesting(true);
   browser()->command_controller()->ExecuteCommand(IDC_INSTALL_PWA);
   manager->AwaitAppInstall();
-  web_app::SetAutoAcceptPWAInstallConfirmationForTesting(false);
 
   EXPECT_FALSE(manager->IsPromptAvailableForTesting());
 
@@ -261,11 +258,9 @@
   }
 
   // Install the app via the menu instead of the banner.
-  web_app::SetAutoAcceptPWAInstallConfirmationForTesting(true);
   browser()->window()->ExecutePageActionIconForTesting(
       PageActionIconType::kPwaInstall);
   manager->AwaitAppInstall();
-  web_app::SetAutoAcceptPWAInstallConfirmationForTesting(false);
 
   EXPECT_FALSE(manager->IsPromptAvailableForTesting());
 
@@ -375,10 +370,8 @@
   }
 
   // Install the app via the menu instead of the banner.
-  web_app::SetAutoAcceptPWAInstallConfirmationForTesting(true);
   browser()->command_controller()->ExecuteCommand(IDC_INSTALL_PWA);
   manager->AwaitAppInstall();
-  web_app::SetAutoAcceptPWAInstallConfirmationForTesting(false);
 
   EXPECT_FALSE(manager->IsPromptAvailableForTesting());
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index e360fc26..0b3e177 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -4779,9 +4779,6 @@
     parts->OverrideWebPreferences(web_contents, main_frame_site, web_prefs);
   }
 
-  // TODO(crbug.com/395838064): Cleanup WebSQL WebPreference.
-  web_prefs->databases_enabled = false;
-
   web_prefs->prefers_default_scrollbar_styles =
       prefs->GetBoolean(prefs::kPrefersDefaultScrollbarStyles);
 #if BUILDFLAG(IS_ANDROID)
@@ -5399,15 +5396,17 @@
 
 void ChromeContentBrowserClient::CreateThrottlesForNavigation(
     content::NavigationThrottleRegistry& registry) {
-  // MetricsNavigationThrottle requires that it runs before NavigationThrottles
-  // that may delay or cancel navigations, so only NavigationThrottles that
-  // don't delay or cancel navigations (e.g. throttles that are only observing
-  // callbacks without affecting navigation behavior) should be added before
-  // MetricsNavigationThrottle.
   content::NavigationHandle& handle = registry.GetNavigationHandle();
   if (handle.IsInMainFrame()) {
-    registry.AddThrottle(
-        page_load_metrics::MetricsNavigationThrottle::Create(&handle));
+    // MetricsNavigationThrottle requires that it runs before
+    // NavigationThrottles that may delay or cancel navigations, so only
+    // NavigationThrottles that don't delay or cancel navigations (e.g.
+    // throttles that are only observing callbacks without affecting navigation
+    // behavior) should be added before MetricsNavigationThrottle.
+    // TODO(https://crbug.com/412524375): This assumption is fragile. This
+    // should be cared by adding an attribute flag to
+    // NavigationThrottleRegistry::AddThrottle().
+    page_load_metrics::MetricsNavigationThrottle::CreateAndAdd(registry);
   }
 
 #if BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/chromeos/platform_keys/extension_key_permissions_service.h b/chrome/browser/chromeos/platform_keys/extension_key_permissions_service.h
index bb3a071..5b31d032 100644
--- a/chrome/browser/chromeos/platform_keys/extension_key_permissions_service.h
+++ b/chrome/browser/chromeos/platform_keys/extension_key_permissions_service.h
@@ -47,7 +47,7 @@
 //   }
 //
 // Do not change this constant as clients will lose their existing state.
-const char kStateStorePlatformKeys[] = "PlatformKeys";
+inline constexpr char kStateStorePlatformKeys[] = "PlatformKeys";
 
 using ExtensionKeyPermissionQueryCallback =
     base::OnceCallback<void(bool allowed)>;
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_clipboard_bubble_constants.h b/chrome/browser/chromeos/policy/dlp/dlp_clipboard_bubble_constants.h
index f790351..6a36c52 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_clipboard_bubble_constants.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_clipboard_bubble_constants.h
@@ -8,24 +8,26 @@
 namespace policy {
 
 // Clipboard ARC toast ID in block mode.
-constexpr char kClipboardBlockArcToastId[] = "clipboard_dlp_block_arc";
+inline constexpr char kClipboardBlockArcToastId[] = "clipboard_dlp_block_arc";
 
 // Clipboard ARC toast ID in warning mode.
-constexpr char kClipboardWarnArcToastId[] = "clipboard_dlp_warn_arc";
+inline constexpr char kClipboardWarnArcToastId[] = "clipboard_dlp_warn_arc";
 
 // Clipboard Crostini toast ID in block mode.
-constexpr char kClipboardBlockCrostiniToastId[] =
+inline constexpr char kClipboardBlockCrostiniToastId[] =
     "clipboard_dlp_block_crostini";
 
 // Clipboard Crostini toast ID in warning mode.
-constexpr char kClipboardWarnCrostiniToastId[] = "clipboard_dlp_warn_crostini";
+inline constexpr char kClipboardWarnCrostiniToastId[] =
+    "clipboard_dlp_warn_crostini";
 
 // Clipboard Plugin VM toast ID in block mode.
-constexpr char kClipboardBlockPluginVmToastId[] =
+inline constexpr char kClipboardBlockPluginVmToastId[] =
     "clipboard_dlp_block_plugin_vm";
 
 // Clipboard Plugin VM toast ID in warning mode.
-constexpr char kClipboardWarnPluginVmToastId[] = "clipboard_dlp_warn_plugin_vm";
+inline constexpr char kClipboardWarnPluginVmToastId[] =
+    "clipboard_dlp_warn_plugin_vm";
 
 // The duration of the clipboard bubble shown on blocked paste.
 constexpr int kClipboardDlpBlockDurationMs = 6000;
diff --git a/chrome/browser/chromeos/policy/dlp/dlp_policy_constants.h b/chrome/browser/chromeos/policy/dlp/dlp_policy_constants.h
index 4b8e7cb..ea79d20 100644
--- a/chrome/browser/chromeos/policy/dlp/dlp_policy_constants.h
+++ b/chrome/browser/chromeos/policy/dlp/dlp_policy_constants.h
@@ -12,7 +12,7 @@
 namespace dlp {
 
 // Link to the Help Center article about Data Leak Prevention.
-constexpr char kDlpLearnMoreUrl[] =
+inline constexpr char kDlpLearnMoreUrl[] =
     "https://support.google.com/chrome/a/?p=chromeos_datacontrols";
 
 }  // namespace dlp
diff --git a/chrome/browser/chromeos/reporting/metric_reporting_prefs.h b/chrome/browser/chromeos/reporting/metric_reporting_prefs.h
index 715ab58..defe870b 100644
--- a/chrome/browser/chromeos/reporting/metric_reporting_prefs.h
+++ b/chrome/browser/chromeos/reporting/metric_reporting_prefs.h
@@ -18,27 +18,28 @@
 
 // A list pref that specifies allowlisted URLs for website activity event
 // reporting.
-constexpr char kReportWebsiteActivityAllowlist[] =
+inline constexpr char kReportWebsiteActivityAllowlist[] =
     "reporting.report_website_activity_allowlist";
 
 // A list pref that specifies allowlisted URLs for website telemetry reporting.
-constexpr char kReportWebsiteTelemetryAllowlist[] =
+inline constexpr char kReportWebsiteTelemetryAllowlist[] =
     "reporting.report_website_telemetry_allowlist";
 
 // A list pref that controls website telemetry data types being reported.
-constexpr char kReportWebsiteTelemetry[] = "reporting.report_website_telemetry";
+inline constexpr char kReportWebsiteTelemetry[] =
+    "reporting.report_website_telemetry";
 
 // An integer pref that controls the collection frequency of website telemetry
 // data.
-constexpr char kReportWebsiteTelemetryCollectionRateMs[] =
+inline constexpr char kReportWebsiteTelemetryCollectionRateMs[] =
     "reporting.report_website_telemetry_collection_rate_ms";
 
 // A dictionary pref that tracks foreground website usage for URLs with the
 // current user profile.
-constexpr char kWebsiteUsage[] = "reporting.website_usage";
+inline constexpr char kWebsiteUsage[] = "reporting.website_usage";
 
 // Website telemetry types tracked by the `ReportWebsiteTelemetry` policy.
-constexpr char kWebsiteTelemetryUsageType[] = "usage";
+inline constexpr char kWebsiteTelemetryUsageType[] = "usage";
 
 void RegisterProfilePrefs(::user_prefs::PrefRegistrySyncable* registry);
 
diff --git a/chrome/browser/chromeos/upload_office_to_cloud/upload_office_to_cloud.h b/chrome/browser/chromeos/upload_office_to_cloud/upload_office_to_cloud.h
index 7df03ca8..5aca318e 100644
--- a/chrome/browser/chromeos/upload_office_to_cloud/upload_office_to_cloud.h
+++ b/chrome/browser/chromeos/upload_office_to_cloud/upload_office_to_cloud.h
@@ -20,9 +20,9 @@
 
 namespace cloud_upload {
 
-constexpr char kCloudUploadPolicyAllowed[] = "allowed";
-constexpr char kCloudUploadPolicyDisallowed[] = "disallowed";
-constexpr char kCloudUploadPolicyAutomated[] = "automated";
+inline constexpr char kCloudUploadPolicyAllowed[] = "allowed";
+inline constexpr char kCloudUploadPolicyDisallowed[] = "disallowed";
+inline constexpr char kCloudUploadPolicyAutomated[] = "automated";
 
 void RegisterProfilePrefs(PrefRegistrySimple* registry);
 
diff --git a/chrome/browser/collaboration/BUILD.gn b/chrome/browser/collaboration/BUILD.gn
index c9e3511..991b0f7 100644
--- a/chrome/browser/collaboration/BUILD.gn
+++ b/chrome/browser/collaboration/BUILD.gn
@@ -97,6 +97,7 @@
       "//chrome/test/android:chrome_java_integration_test_support",
       "//components/collaboration/public:java",
       "//components/data_sharing/public:public_java",
+      "//components/saved_tab_groups/public:java",
       "//third_party/androidx:androidx_annotation_annotation_java",
       "//third_party/androidx:androidx_test_runner_java",
       "//third_party/hamcrest:hamcrest_core_java",
diff --git a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationServiceFactoryTest.java b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationServiceFactoryTest.java
index 135789b..d2bf1cc 100644
--- a/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationServiceFactoryTest.java
+++ b/chrome/browser/collaboration/android/java/src/org/chromium/chrome/browser/collaboration/CollaborationServiceFactoryTest.java
@@ -36,6 +36,7 @@
 import org.chromium.components.collaboration.SyncStatus;
 import org.chromium.components.data_sharing.GroupData;
 import org.chromium.components.data_sharing.member_role.MemberRole;
+import org.chromium.components.tab_group_sync.EitherId.EitherGroupId;
 import org.chromium.url.GURL;
 
 import java.util.concurrent.CountDownLatch;
@@ -64,13 +65,13 @@
                     @Override
                     public void startShareOrManageFlow(
                             CollaborationControllerDelegate delegate,
-                            String syncId,
+                            EitherGroupId eitherId,
                             @CollaborationServiceShareOrManageEntryPoint int entry) {}
 
                     @Override
                     public void startLeaveOrDeleteFlow(
                             CollaborationControllerDelegate delegate,
-                            String syncId,
+                            EitherGroupId eitherId,
                             @CollaborationServiceLeaveOrDeleteEntryPoint int entry) {}
 
                     @Override
diff --git a/chrome/browser/contextual_cueing/zero_state_suggestions_page_data.cc b/chrome/browser/contextual_cueing/zero_state_suggestions_page_data.cc
index c0cf375..c749f51 100644
--- a/chrome/browser/contextual_cueing/zero_state_suggestions_page_data.cc
+++ b/chrome/browser/contextual_cueing/zero_state_suggestions_page_data.cc
@@ -6,6 +6,7 @@
 
 #include "base/metrics/histogram_functions.h"
 #include "base/metrics/histogram_macros_local.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "chrome/browser/content_extraction/inner_text.h"
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 9bab51f..1c6375c 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
@@ -64,6 +64,7 @@
 import org.chromium.components.data_sharing.configs.DataSharingStringConfig;
 import org.chromium.components.data_sharing.configs.DataSharingUiConfig;
 import org.chromium.components.data_sharing.configs.DataSharingUiConfig.DataSharingUserAction;
+import org.chromium.components.tab_group_sync.EitherId.EitherGroupId;
 import org.chromium.components.tab_group_sync.LocalTabGroupId;
 import org.chromium.components.tab_group_sync.SavedTabGroup;
 import org.chromium.components.tab_group_sync.SavedTabGroupTab;
@@ -537,40 +538,35 @@
         }
         createOrManageFlow(
                 activity,
-                /* syncId= */ null,
-                new LocalTabGroupId(assumeNonNull(tab.getTabGroupId())),
+                EitherGroupId.createLocalId(
+                        new LocalTabGroupId(assumeNonNull(tab.getTabGroupId()))),
                 entryPoint,
                 createGroupFinishedCallback);
     }
 
     /**
-     * Creates a collaboration group.
+     * Creates or manage a collaboration group.
      *
      * @param activity The activity in which the group is to be created.
-     * @param syncId The sync ID of the tab group.
-     * @param localTabGroupId The tab group ID of the tab in the local tab group model.
+     * @param eitherId The sync ID or local tab group ID of the tab group.
+     * @param entry The entry point of the flow.
      * @param createGroupFinishedCallback Callback invoked when the creation flow is finished.
      */
     public void createOrManageFlow(
             Activity activity,
-            @Nullable String syncId,
-            @Nullable LocalTabGroupId localTabGroupId,
+            EitherGroupId eitherId,
             @CollaborationServiceShareOrManageEntryPoint int entry,
             @Nullable Callback<Boolean> createGroupFinishedCallback) {
         DataSharingMetrics.recordShareActionFlowState(
                 DataSharingMetrics.ShareActionStateAndroid.SHARE_TRIGGERED);
 
-        SavedTabGroup existingGroup = getSavedTabGroupForEitherId(syncId, localTabGroupId);
-        assert existingGroup != null : "Group not found in TabGroupSyncService.";
-
         // TODO(haileywang): Ensure createGroupFinishedCallback is called when the creation is
         // finished.
         mCurrentDelegate =
                 mCollaborationControllerDelegateFactory.create(
                         FlowType.SHARE_OR_MANAGE, /* switchToTabSwitcherCallback= */ null);
         assumeNonNull(mCollaborationService);
-        mCollaborationService.startShareOrManageFlow(
-                mCurrentDelegate, assumeNonNull(existingGroup.syncId), entry);
+        mCollaborationService.startShareOrManageFlow(mCurrentDelegate, eitherId, entry);
     }
 
     /**
@@ -901,17 +897,17 @@
                 () ->
                         createOrManageFlow(
                                 activity,
-                                existingGroup.syncId,
-                                /* localTabGroupId= */ null,
+                                EitherGroupId.createSyncId(assumeNonNull(existingGroup.syncId)),
                                 CollaborationServiceShareOrManageEntryPoint.RECENT_ACTIVITY,
                                 /* createGroupFinishedCallback= */ null);
+        assumeNonNull(existingGroup.syncId);
         RecentActivityActionHandler recentActivityActionHandler =
                 new RecentActivityActionHandlerImpl(
                         tabGroupSyncService,
                         mTabModelSelectorSupplier.get(),
                         mDataSharingTabGroupsDelegate,
                         collaborationId,
-                        assumeNonNull(existingGroup.syncId),
+                        existingGroup.syncId,
                         manageSharingCallback);
 
         Runnable showFullActivityRunnable =
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 74016d16..98bfcc0 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
@@ -69,6 +69,7 @@
 import org.chromium.components.data_sharing.ParseUrlStatus;
 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtilsJni;
+import org.chromium.components.tab_group_sync.EitherId.EitherGroupId;
 import org.chromium.components.tab_group_sync.LocalTabGroupId;
 import org.chromium.components.tab_group_sync.SavedTabGroup;
 import org.chromium.components.tab_group_sync.SyncedGroupTestHelper;
@@ -238,14 +239,11 @@
     public void testShareOrManageFlowWithCollaborationService() {
         doReturn(mProfile).when(mProfile).getOriginalProfile();
         doReturn(mSavedTabGroup).when(mTabGroupSyncService).getGroup(LOCAL_ID);
+        EitherGroupId either_id = EitherGroupId.createLocalId(LOCAL_ID);
         mDataSharingTabManager.createOrManageFlow(
-                mActivity,
-                /* syncId= */ null,
-                LOCAL_ID,
-                CollaborationServiceShareOrManageEntryPoint.UNKNOWN,
-                null);
+                mActivity, either_id, CollaborationServiceShareOrManageEntryPoint.UNKNOWN, null);
 
-        verify(mCollaborationService).startShareOrManageFlow(any(), eq(SYNC_GROUP_ID1), anyInt());
+        verify(mCollaborationService).startShareOrManageFlow(any(), eq(either_id), anyInt());
     }
 
     @Test
@@ -274,7 +272,8 @@
                 mCreateGroupFinishedCallback);
 
         verify(mTabGroupModelFilter).createSingleTabGroup(eq(mTab));
-        verify(mCollaborationService).startShareOrManageFlow(any(), eq(SYNC_GROUP_ID1), anyInt());
+        verify(mCollaborationService)
+                .startShareOrManageFlow(any(), eq(EitherGroupId.createLocalId(LOCAL_ID)), anyInt());
     }
 
     @Test
@@ -302,7 +301,8 @@
                 CollaborationServiceShareOrManageEntryPoint.UNKNOWN,
                 mCreateGroupFinishedCallback);
 
-        verify(mCollaborationService).startShareOrManageFlow(any(), eq(SYNC_GROUP_ID1), anyInt());
+        verify(mCollaborationService)
+                .startShareOrManageFlow(any(), eq(EitherGroupId.createLocalId(LOCAL_ID)), anyInt());
     }
 
     @Test
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImpl.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImpl.java
index 9d6a4c5..baf58a47 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImpl.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImpl.java
@@ -46,7 +46,7 @@
 import org.chromium.components.messages.MessageDispatcherProvider;
 import org.chromium.components.messages.MessageIdentifier;
 import org.chromium.components.messages.PrimaryActionClickBehavior;
-import org.chromium.components.tab_group_sync.LocalTabGroupId;
+import org.chromium.components.tab_group_sync.EitherId.EitherGroupId;
 import org.chromium.components.tab_group_sync.SavedTabGroup;
 import org.chromium.components.tab_group_sync.TabGroupSyncService;
 import org.chromium.ui.base.WindowAndroid;
@@ -359,7 +359,6 @@
             Runnable onSuccess) {
         @Nullable String collaborationId = MessageUtils.extractCollaborationId(message);
         @Nullable String syncId = MessageUtils.extractSyncTabGroupId(message);
-        @Nullable Token localId = MessageUtils.extractTabGroupId(message);
         String buttonText = activity.getString(R.string.data_sharing_browser_message_manage);
         GroupMember groupMember = MessageUtils.extractMember(message);
         Runnable openManageSharingRunnable =
@@ -367,13 +366,11 @@
                     // TODO(crbug.com/379148260): Use shared #isCollaborationIdValid.
                     if (TextUtils.isEmpty(collaborationId)) return;
                     if (TextUtils.isEmpty(syncId)) return;
-                    if (localId == null) return;
                     if (mTabGroupSyncService.getGroup(syncId) == null) return;
 
                     dataSharingTabManager.createOrManageFlow(
                             activity,
-                            syncId,
-                            new LocalTabGroupId(localId),
+                            EitherGroupId.createSyncId(syncId),
                             CollaborationServiceShareOrManageEntryPoint.ANDROID_MESSAGE,
                             /* createGroupFinishedCallback= */ null);
                 };
diff --git a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImplUnitTest.java b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImplUnitTest.java
index 310ab704..2f8d23ad 100644
--- a/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImplUnitTest.java
+++ b/chrome/browser/data_sharing/android/java/src/org/chromium/chrome/browser/data_sharing/InstantMessageDelegateImplUnitTest.java
@@ -314,7 +314,7 @@
         Supplier<Integer> action = propertyModel.get(ON_PRIMARY_ACTION);
         assertNotNull(action);
         assertEquals(DISMISS_IMMEDIATELY, action.get().intValue());
-        verify(mDataSharingTabManager).createOrManageFlow(any(), any(), any(), anyInt(), any());
+        verify(mDataSharingTabManager).createOrManageFlow(any(), any(), anyInt(), any());
     }
 
     @Test
@@ -329,8 +329,7 @@
         Supplier<Integer> action = propertyModel.get(ON_PRIMARY_ACTION);
         assertNotNull(action);
         assertEquals(DISMISS_IMMEDIATELY, action.get().intValue());
-        verify(mDataSharingTabManager, never())
-                .createOrManageFlow(any(), any(), any(), anyInt(), any());
+        verify(mDataSharingTabManager, never()).createOrManageFlow(any(), any(), anyInt(), any());
     }
 
     @Test
diff --git a/chrome/browser/download/insecure_download_blocking.cc b/chrome/browser/download/insecure_download_blocking.cc
index 6d8b743..fadce51 100644
--- a/chrome/browser/download/insecure_download_blocking.cc
+++ b/chrome/browser/download/insecure_download_blocking.cc
@@ -24,6 +24,7 @@
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/download_item_utils.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/url_constants.h"
 #include "net/base/url_util.h"
 #include "services/network/public/cpp/is_potentially_trustworthy.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
@@ -352,6 +353,10 @@
            !download_delivered_securely) &&
           !net::IsLocalhost(dl_url);
     }
+
+    is_user_initiated_on_webui_ =
+        item->GetTabUrl().SchemeIs(content::kChromeUIScheme) &&
+        download_source == DownloadSource::CONTEXT_MENU;
   }
 
   std::optional<url::Origin> initiator_;
@@ -364,6 +369,8 @@
   bool is_mixed_content_;
   // Was the download initiated by an insecure origin or delivered insecurely?
   bool is_insecure_download_;
+  // Was the download initiated by a user on a chrome:// WebUI?
+  bool is_user_initiated_on_webui_;
 };
 
 // Check if |extension| is contained in the comma separated |extension_list|.
@@ -405,6 +412,10 @@
     return;
   }
 
+  if (data.is_user_initiated_on_webui_) {
+    return;
+  }
+
   rfh->AddMessageToConsole(
       blink::mojom::ConsoleMessageLevel::kError,
       base::StringPrintf(
diff --git a/chrome/browser/enterprise/client_certificates/certificate_store_factory.cc b/chrome/browser/enterprise/client_certificates/certificate_store_factory.cc
index 5f0a131..9f227f91 100644
--- a/chrome/browser/enterprise/client_certificates/certificate_store_factory.cc
+++ b/chrome/browser/enterprise/client_certificates/certificate_store_factory.cc
@@ -9,7 +9,9 @@
 #include "base/no_destructor.h"
 #include "chrome/browser/enterprise/client_certificates/cert_utils.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/enterprise/client_certificates/core/features.h"
 #include "components/enterprise/client_certificates/core/leveldb_certificate_store.h"
+#include "components/enterprise/client_certificates/core/prefs_certificate_store.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/storage_partition.h"
@@ -38,8 +40,16 @@
 CertificateStoreFactory::BuildServiceInstanceForBrowserContext(
     content::BrowserContext* context) const {
   auto* profile = Profile::FromBrowserContext(context);
+  if (!profile) {
+    return nullptr;
+  }
 
-  if (!profile || !profile->GetDefaultStoragePartition() ||
+  if (features::IsManagedUserClientCertificateInPrefsEnabled()) {
+    return std::make_unique<PrefsCertificateStore>(profile->GetPrefs(),
+                                                   CreatePrivateKeyFactory());
+  }
+
+  if (!profile->GetDefaultStoragePartition() ||
       !profile->GetDefaultStoragePartition()->GetProtoDatabaseProvider()) {
     return nullptr;
   }
diff --git a/chrome/browser/enterprise/reporting/cloud_profile_reporting_browsertest.cc b/chrome/browser/enterprise/reporting/cloud_profile_reporting_browsertest.cc
index 63e6f54..538cab1 100644
--- a/chrome/browser/enterprise/reporting/cloud_profile_reporting_browsertest.cc
+++ b/chrome/browser/enterprise/reporting/cloud_profile_reporting_browsertest.cc
@@ -36,7 +36,8 @@
   void SetUpOnMainThread() override {
     Profile* profile = chrome_test_utils::GetProfile(this);
     EnableProfileManagement(profile);
-    EnableReportingPolicy(profile);
+    profile->GetPrefs()->SetBoolean(kCloudProfileReportingEnabled, true);
+    SetReportingPolicy(profile, /*enabled=*/true);
   }
 
   void EnableProfileManagement(Profile* profile) {
@@ -54,8 +55,8 @@
         /*service=*/nullptr, std::move(client));
   }
 
-  void EnableReportingPolicy(Profile* profile) {
-    profile->GetPrefs()->SetBoolean(kCloudProfileReportingEnabled, true);
+  void SetReportingPolicy(Profile* profile, bool enabled) {
+    profile->GetPrefs()->SetBoolean(kCloudProfileReportingEnabled, enabled);
   }
 };
 
@@ -71,4 +72,64 @@
                   ReportScheduler::kTriggerTimer);
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+class CloudProfileReportingServiceTestDesktop
+    : public CloudProfileReportingServiceTest,
+      public testing::WithParamInterface<
+          // Two boolean variables represents whether profile reporting and
+          // signals reporting is enabled
+          testing::tuple<bool, bool>> {
+ public:
+  CloudProfileReportingServiceTestDesktop() = default;
+  ~CloudProfileReportingServiceTestDesktop() override = default;
+
+  void SetUpOnMainThread() override {
+    Profile* profile = chrome_test_utils::GetProfile(this);
+    EnableProfileManagement(profile);
+    SetReportingPolicy(profile, profile_reporting_enabled());
+    profile->GetPrefs()->SetBoolean(kUserSecuritySignalsReporting,
+                                    signals_reporting_enabled());
+  }
+
+  bool profile_reporting_enabled() { return testing::get<0>(GetParam()); }
+  bool signals_reporting_enabled() { return testing::get<1>(GetParam()); }
+};
+
+IN_PROC_BROWSER_TEST_P(CloudProfileReportingServiceTestDesktop,
+                       VerifyReportingConfig) {
+  base::RunLoop().RunUntilIdle();
+  ReportScheduler* report_scheduler =
+      CloudProfileReportingServiceFactory::GetForProfile(
+          chrome_test_utils::GetProfile(this))
+          ->report_scheduler();
+  ASSERT_TRUE(report_scheduler);
+
+  auto active_trigger = report_scheduler->GetActiveTriggerForTesting();
+  auto active_config = report_scheduler->GetActiveGenerationConfigForTesting();
+
+  if (signals_reporting_enabled() && profile_reporting_enabled()) {
+    EXPECT_EQ(active_trigger, ReportScheduler::kTriggerTimer);
+    EXPECT_EQ(active_config.security_signals_mode,
+              SecuritySignalsMode::kSignalsAttached);
+  } else if (profile_reporting_enabled()) {
+    EXPECT_EQ(active_trigger, ReportScheduler::kTriggerTimer);
+    EXPECT_EQ(active_config.security_signals_mode,
+              SecuritySignalsMode::kNoSignals);
+  } else if (signals_reporting_enabled()) {
+    EXPECT_EQ(active_trigger, ReportScheduler::kTriggerSecurity);
+    EXPECT_EQ(active_config.security_signals_mode,
+              SecuritySignalsMode::kSignalsOnly);
+  } else {
+    EXPECT_EQ(active_trigger, ReportScheduler::kTriggerNone);
+  }
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         CloudProfileReportingServiceTestDesktop,
+                         testing::Combine(
+                             /*profile_reporting_enabled=*/testing::Bool(),
+                             /*signals_reporting_enabled=*/testing::Bool()));
+
+#endif  // !BUILDFLAG(IS_ANDROID)
+
 }  // namespace enterprise_reporting
diff --git a/chrome/browser/enterprise/reporting/cloud_profile_reporting_service_factory.cc b/chrome/browser/enterprise/reporting/cloud_profile_reporting_service_factory.cc
index 0ddc529..b36c4f9 100644
--- a/chrome/browser/enterprise/reporting/cloud_profile_reporting_service_factory.cc
+++ b/chrome/browser/enterprise/reporting/cloud_profile_reporting_service_factory.cc
@@ -37,6 +37,7 @@
 
   return std::make_unique<CloudProfileReportingService>(profile);
 }
+
 bool CloudProfileReportingServiceFactory::ServiceIsCreatedWithBrowserContext()
     const {
   return true;
diff --git a/chrome/browser/enterprise/signals/profile_signals_collector.cc b/chrome/browser/enterprise/signals/profile_signals_collector.cc
index 4bfd36a..3fe59ff2 100644
--- a/chrome/browser/enterprise/signals/profile_signals_collector.cc
+++ b/chrome/browser/enterprise/signals/profile_signals_collector.cc
@@ -79,6 +79,20 @@
 #if BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
   signal_response.realtime_url_check_mode =
       connectors_service_->GetAppliedRealTimeUrlCheck();
+  signal_response.file_downloaded_providers =
+      connectors_service_->GetAnalysisServiceProviderNames(
+          enterprise_connectors::FILE_DOWNLOADED);
+  signal_response.file_attached_providers =
+      connectors_service_->GetAnalysisServiceProviderNames(
+          enterprise_connectors::FILE_ATTACHED);
+  signal_response.bulk_data_entry_providers =
+      connectors_service_->GetAnalysisServiceProviderNames(
+          enterprise_connectors::BULK_DATA_ENTRY);
+  signal_response.print_providers =
+      connectors_service_->GetAnalysisServiceProviderNames(
+          enterprise_connectors::PRINT);
+  signal_response.security_event_providers =
+      connectors_service_->GetReportingServiceProviderNames();
 #endif  // BUILDFLAG(ENTERPRISE_CLOUD_CONTENT_ANALYSIS)
 
   response.profile_signals_response = std::move(signal_response);
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn
index 667a623f..825cc89 100644
--- a/chrome/browser/extensions/BUILD.gn
+++ b/chrome/browser/extensions/BUILD.gn
@@ -47,6 +47,8 @@
     "activity_log/database_string_table.h",
     "activity_log/fullstream_ui_policy.cc",
     "activity_log/fullstream_ui_policy.h",
+    "api/bookmarks/bookmarks_api.cc",
+    "api/bookmarks/bookmarks_api.h",
     "api/bookmarks/bookmarks_api_watcher.cc",
     "api/bookmarks/bookmarks_api_watcher.h",
     "api/bookmarks_core/bookmarks_function.cc",
@@ -163,6 +165,8 @@
     "bookmarks/bookmarks_error_constants.h",
     "bookmarks/bookmarks_features.cc",
     "bookmarks/bookmarks_features.h",
+    "bookmarks/bookmarks_helpers.cc",
+    "bookmarks/bookmarks_helpers.h",
     "chrome_app_icon.cc",
     "chrome_app_icon.h",
     "chrome_app_icon_delegate.h",
@@ -464,6 +468,7 @@
     "//components/services/unzip/content",
     "//components/spellcheck/browser",
     "//components/supervised_user/core/browser",
+    "//components/tabs:public",
     "//components/update_client",
     "//components/update_client:common_impl",
     "//components/user_prefs",
@@ -517,8 +522,6 @@
       "api/automation_internal/chrome_automation_internal_api_delegate.h",
       "api/bookmark_manager_private/bookmark_manager_private_api.cc",
       "api/bookmark_manager_private/bookmark_manager_private_api.h",
-      "api/bookmarks/bookmarks_api.cc",
-      "api/bookmarks/bookmarks_api.h",
       "api/chrome_device_permissions_prompt.h",
       "api/chrome_extensions_api_client.cc",
       "api/chrome_extensions_api_client.h",
@@ -658,8 +661,6 @@
       "api/tabs/tabs_windows_api.h",
       "api/tabs/windows_event_router.cc",
       "api/tabs/windows_event_router.h",
-      "bookmarks/bookmarks_helpers.cc",
-      "bookmarks/bookmarks_helpers.h",
       "browser_extension_window_controller.cc",
       "browser_extension_window_controller.h",
       "chrome_app_icon_loader.cc",
diff --git a/chrome/browser/extensions/DEPS b/chrome/browser/extensions/DEPS
index 96aec20..e23247e 100644
--- a/chrome/browser/extensions/DEPS
+++ b/chrome/browser/extensions/DEPS
@@ -4,6 +4,7 @@
   "+components/guest_view/common",
   "+components/security_state/content",
   "+components/live_caption",
+  "+components/tabs/public",
   "+dbus",
   "+extensions/strings/grit/extensions_strings.h",
   "+services/network/public",
diff --git a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
index 0106c95..3bef5db 100644
--- a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
+++ b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc
@@ -657,10 +657,7 @@
 
   ExtensionTabUtil::OpenTabParams options;
   options.url = node->url().spec();
-  if (params->params.has_value()) {
-    options.active = params->params.value().active;
-    options.split = params->params.value().split;
-  }
+  options.active = params->active;
   options.bookmark_id = node->id();
 
   auto result =
diff --git a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc
index 7332397..cbceebd 100644
--- a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc
@@ -109,7 +109,7 @@
 TEST_F(BookmarkManagerPrivateApiUnitTest, RunOpenInNewTabFunction) {
   auto new_tab_function =
       base::MakeRefCounted<BookmarkManagerPrivateOpenInNewTabFunction>();
-  std::string args = base::StringPrintf(R"(["%s"])", node_id().c_str());
+  std::string args = base::StringPrintf(R"(["%s", false])", node_id().c_str());
   ASSERT_TRUE(
       api_test_utils::RunFunction(new_tab_function.get(), args, profile()));
 
@@ -122,7 +122,7 @@
       base::MakeRefCounted<BookmarkManagerPrivateOpenInNewTabFunction>();
   std::string node_id =
       base::NumberToString(model()->bookmark_bar_node()->id());
-  std::string args = base::StringPrintf(R"(["%s"])", node_id.c_str());
+  std::string args = base::StringPrintf(R"(["%s", false])", node_id.c_str());
   EXPECT_EQ("Cannot open a folder in a new tab.",
             api_test_utils::RunFunctionAndReturnError(new_tab_function.get(),
                                                       args, profile()));
diff --git a/chrome/browser/extensions/api/bookmarks/bookmarks_api_unittest.cc b/chrome/browser/extensions/api/bookmarks/bookmarks_api_unittest.cc
index ca7caa2..cf723ebb 100644
--- a/chrome/browser/extensions/api/bookmarks/bookmarks_api_unittest.cc
+++ b/chrome/browser/extensions/api/bookmarks/bookmarks_api_unittest.cc
@@ -133,6 +133,10 @@
   EXPECT_EQ(result_node.index, model()->other_node()->children().size() - 1);
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+// TODO(crbug.com/414844449): Port to desktop Android once default visible
+// bookmarks behavior is clarified.
+
 // Tests that attempting to create a bookmark with no parent folder specified
 // succeeds and uses the account bookmarks folder when the user is signed in
 // with bookmarks in transport mode.
@@ -155,6 +159,7 @@
   EXPECT_EQ(result_node.index,
             model()->account_other_node()->children().size() - 1);
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Tests creating a bookmark with a valid parent specified.
 TEST_F(BookmarksApiUnittest, Create_ValidParent) {
@@ -249,6 +254,9 @@
   ASSERT_TRUE(model()->mobile_node()->IsVisible());
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+// TODO(crbug.com/414844449): Port to desktop Android once default visible
+// bookmarks behavior is clarified.
 TEST_F(BookmarksApiUnittest, Create_NonVisibleParent) {
   // The mobile node is not visible, because it is empty.
   ASSERT_FALSE(model()->mobile_node()->IsVisible());
@@ -262,6 +270,7 @@
 
   EXPECT_EQ(error, bookmarks_errors::kNoParentError);
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 TEST_F(BookmarksApiUnittest,
        Get_SucceedsForLocalPermanentFolderWhenNoAccountFolders) {
@@ -295,6 +304,9 @@
   EXPECT_THAT(result, ResultMatchesNodes(expected_nodes));
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+// TODO(crbug.com/414844449): Port to desktop Android once default visible
+// bookmarks behavior is clarified.
 TEST_F(BookmarksApiUnittest,
        Get_ReturnsEmptyForNonVisibleFolderNoVisibilityEnforcement) {
   base::test::ScopedFeatureList scoped_feature_list;
@@ -326,6 +338,7 @@
 
   EXPECT_EQ(error, extensions::bookmarks_errors::kNoNodeError);
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 TEST_F(BookmarksApiUnittest, Get_FailsForNonExistentId) {
   auto get_function = base::MakeRefCounted<BookmarksGetFunction>();
@@ -335,6 +348,9 @@
   EXPECT_EQ(error, extensions::bookmarks_errors::kNoNodeError);
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+// TODO(crbug.com/414844449): Port to desktop Android once default visible
+// bookmarks behavior is clarified.
 TEST_F(BookmarksApiUnittest,
        GetChildren_ReturnsEmptyForNonVisibleFolderNoVisibilityEnforcement) {
   base::test::ScopedFeatureList scoped_feature_list;
@@ -366,6 +382,7 @@
 
   EXPECT_EQ(error, extensions::bookmarks_errors::kNoNodeError);
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 TEST_F(BookmarksApiUnittest, GetChildren_FailsForNonExistentId) {
   auto get_function = base::MakeRefCounted<BookmarksGetChildrenFunction>();
@@ -375,6 +392,9 @@
   EXPECT_EQ(error, extensions::bookmarks_errors::kNoNodeError);
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+// TODO(crbug.com/414844449): Port to desktop Android once default visible
+// bookmarks behavior is clarified.
 TEST_F(BookmarksApiUnittest,
        GetSubTree_ReturnsEmptyForNonVisibleFolderNoVisibilityEnforcement) {
   base::test::ScopedFeatureList scoped_feature_list;
@@ -406,6 +426,7 @@
 
   EXPECT_EQ(error, extensions::bookmarks_errors::kNoNodeError);
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 TEST_F(BookmarksApiUnittest, GetSubTree_FailsForNonExistentId) {
   auto get_function = base::MakeRefCounted<BookmarksGetSubTreeFunction>();
@@ -426,6 +447,9 @@
   EXPECT_THAT(result, ResultMatchesNodes(expected_nodes));
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+// TODO(crbug.com/414844449): Port to desktop Android once default visible
+// bookmarks behavior is clarified.
 TEST_F(BookmarksApiUnittest, Search_NonVisibleFolderNotReturned) {
   // Set the title of the folder node to a fixed value.
   model()->SetTitle(model()->mobile_node(), u"Mobile Bookmarks",
@@ -540,6 +564,7 @@
   EXPECT_EQ(result_node.index, 0);
   EXPECT_EQ(model()->account_other_node()->children()[0].get(), folder_node());
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Tests that attempting to move a bookmark to a non-folder parent does
 // not add the bookmark to that parent.
@@ -574,6 +599,9 @@
   ASSERT_TRUE(url_node->children().empty());
 }
 
+#if !BUILDFLAG(IS_ANDROID)
+// TODO(crbug.com/414844449): Port to desktop Android once default visible
+// bookmarks behavior is clarified.
 TEST_F(BookmarksApiUnittest, Move_NonVisibleParentNoVisibilityEnforcement) {
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitAndDisableFeature(
@@ -614,6 +642,7 @@
 
   EXPECT_EQ(error, bookmarks_errors::kNoParentError);
 }
+#endif  // !BUILDFLAG(IS_ANDROID)
 
 // Tests that attempting to move a folder to itself returns an error.
 TEST_F(BookmarksApiUnittest, Move_FolderToItself) {
diff --git a/chrome/browser/extensions/api/cookies/cookies_api.cc b/chrome/browser/extensions/api/cookies/cookies_api.cc
index 4c77b172..e284ce2 100644
--- a/chrome/browser/extensions/api/cookies/cookies_api.cc
+++ b/chrome/browser/extensions/api/cookies/cookies_api.cc
@@ -217,11 +217,13 @@
   // When an off-the-record spinoff of |profile_| is created, start listening
   // for cookie changes there. The OTR receiver should never be bound, since
   // there wasn't previously an OTR profile.
-  CHECK(!otr_receiver_.is_bound());
+  if (off_the_record->IsPrimaryOTRProfile()) {
+    DCHECK(!otr_receiver_.is_bound());
 #if !BUILDFLAG(IS_ANDROID)
-  otr_profile_observation_.Observe(off_the_record);
+    otr_profile_observation_.Observe(off_the_record);
 #endif
-  BindToCookieManager(&otr_receiver_, off_the_record);
+    BindToCookieManager(&otr_receiver_, off_the_record);
+  }
 }
 
 void CookiesEventRouter::OnProfileWillBeDestroyed(Profile* profile) {
diff --git a/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc b/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc
index b6164b2..e50a02d 100644
--- a/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc
+++ b/chrome/browser/extensions/api/crash_report_private/crash_report_private_apitest.cc
@@ -93,6 +93,7 @@
   const std::optional<MockCrashEndpoint::Report>& last_report() {
     return crash_endpoint_->last_report();
   }
+  void clear_last_report() { crash_endpoint_->clear_last_report(); }
   raw_ptr<const Extension, DanglingUntriaged> extension_;
   std::unique_ptr<MockCrashEndpoint> crash_endpoint_;
   std::unique_ptr<ScopedMockChromeJsErrorReportProcessor> processor_;
@@ -259,12 +260,14 @@
   const std::optional<MockCrashEndpoint::Report>& report = last_report();
 
   // Ensure error is not reported since devtools is open.
+  clear_last_report();
   EXPECT_EQ("", ExecuteScriptInBackgroundPage(extension_->id(), kTestScript));
   ASSERT_FALSE(report);
 
   DevToolsWindowTesting::CloseDevToolsWindow(devtools_window);
 
   // Ensure error is not reported after devtools has been closed.
+  clear_last_report();
   EXPECT_EQ("", ExecuteScriptInBackgroundPage(extension_->id(), kTestScript));
   ASSERT_FALSE(report);
 }
diff --git a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
index fa1ff0c..83b5afa 100644
--- a/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
+++ b/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_browsertest.cc
@@ -7165,19 +7165,9 @@
 }
 
 // Tests that Protected Audience requests can be blocked by the
-// declarativeNetRequest API, and that if they try to redirect requests, the
-// request is blocked by the Protected Audience logic, which doesn't allow
-// redirects, instead of being redirected.
-// Flaky on Mac and Win bots, see also crbug.com/414462480
-#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-#define MAYBE_ProtectedAudienceNetworkRequestsBlockRequests \
-  DISABLED_ProtectedAudienceNetworkRequestsBlockRequests
-#else
-#define MAYBE_ProtectedAudienceNetworkRequestsBlockRequests \
-  ProtectedAudienceNetworkRequestsBlockRequests
-#endif
+// declarativeNetRequest API.
 IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
-                       MAYBE_ProtectedAudienceNetworkRequestsBlockRequests) {
+                       ProtectedAudienceNetworkRequestsBlockRequests) {
   privacy_sandbox::ScopedPrivacySandboxAttestations scoped_attestations(
       privacy_sandbox::PrivacySandboxAttestations::CreateForTesting());
   // Mark all Privacy Sandbox APIs as attested since the test case is testing
@@ -7265,10 +7255,74 @@
       FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
   run_loop.Run();
   EXPECT_EQ(0u, GetAndResetRequestsToServer().count(bidder_report_url));
+}
 
-  // Load a second extension which redirects requests for the bidding script
-  // (not the report URL, which the first extension blocks) to a URL that serves
-  // an identical bidding script.
+// Tests that if the declarativeNetRequest API tries to redirect Protected
+// Audience requests, the request is blocked by the Protected Audience logic,
+// which doesn't allow redirects, instead of being redirected.
+IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest,
+                       ProtectedAudienceNetworkRequestsBlockRedirect) {
+  privacy_sandbox::ScopedPrivacySandboxAttestations scoped_attestations(
+      privacy_sandbox::PrivacySandboxAttestations::CreateForTesting());
+  // Mark all Privacy Sandbox APIs as attested since the test case is testing
+  // behaviors not related to attestations.
+  privacy_sandbox::PrivacySandboxAttestations::GetInstance()
+      ->SetAllPrivacySandboxAttestedForTesting(true);
+
+  ASSERT_TRUE(https_server()->Start());
+
+  PrivacySandboxSettingsFactory::GetForProfile(profile())
+      ->SetAllPrivacySandboxAllowedForTesting();
+
+  NavigateToURL(https_server()->GetURL("/interest_group/fenced_frame.html"));
+
+  GURL bidding_logic_url =
+      https_server()->GetURL("/interest_group/bidding_logic.js");
+  GURL decision_logic_url =
+      https_server()->GetURL("/interest_group/decision_logic.js");
+  GURL bidder_report_url = https_server()->GetURL("/echo?bidder_report");
+  GURL decision_report_url = https_server()->GetURL("/echo?decision_report");
+
+  // Add an interest group.
+  EXPECT_EQ("done", content::EvalJs(
+                        web_contents(),
+                        content::JsReplace(
+                            R"(
+          (function() {
+            navigator.joinAdInterestGroup({
+              name: 'cars',
+              owner: $1,
+              biddingLogicURL: $2,
+              userBiddingSignals: [],
+              ads: [{
+                renderURL: 'https://example.com/render',
+                metadata: {ad: 'metadata', here: [1, 2, 3]}
+              }]
+            }, /*joinDurationSec=*/ 300);
+            return 'done';
+          })();
+        )",
+                            url::Origin::Create(bidding_logic_url).Serialize(),
+                            bidding_logic_url)));
+
+  std::string run_auction_command = content::JsReplace(
+      R"(
+             (async function() {
+               let config = await navigator.runAdAuction({
+                 seller: $1,
+                 decisionLogicURL: $2,
+                 interestGroupBuyers: [$1],
+               });
+               document.querySelector('fencedframe').config =
+                  new FencedFrameConfig(config);
+               return config;
+             })()
+          )",
+      url::Origin::Create(decision_logic_url).Serialize(),
+      decision_logic_url.spec());
+
+  // Add an extension which redirects requests for the bidding script to a URL
+  // that serves an identical bidding script.
   TestRule redirect_bidding_logic_rule = CreateGenericRule();
   redirect_bidding_logic_rule.condition->url_filter =
       bidding_logic_url.spec() + "^";
diff --git a/chrome/browser/extensions/api/developer_private/developer_private_functions_shared.cc b/chrome/browser/extensions/api/developer_private/developer_private_functions_shared.cc
index fd28858e..bcfd7ae 100644
--- a/chrome/browser/extensions/api/developer_private/developer_private_functions_shared.cc
+++ b/chrome/browser/extensions/api/developer_private/developer_private_functions_shared.cc
@@ -7,6 +7,7 @@
 #include "base/barrier_closure.h"
 #include "base/files/file_util.h"
 #include "base/memory/ref_counted.h"
+#include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/thread_pool.h"
 #include "chrome/browser/devtools/devtools_window.h"
diff --git a/chrome/browser/extensions/api/image_writer_private/operation.cc b/chrome/browser/extensions/api/image_writer_private/operation.cc
index 9e0be495..2dc7ca87 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation.cc
+++ b/chrome/browser/extensions/api/image_writer_private/operation.cc
@@ -29,8 +29,6 @@
 
 namespace {
 
-const int kMD5BufferSize = 1024;
-
 // Returns true if the file at |image_path| is an archived image.
 bool IsArchive(const base::FilePath& image_path) {
   return ZipExtractor::IsZipFile(image_path) ||
@@ -256,9 +254,6 @@
 
 void Operation::GetMD5SumOfFile(
     const base::FilePath& file_path,
-    int64_t file_size,
-    int progress_offset,
-    int progress_scale,
     base::OnceCallback<void(const std::string&)> callback) {
   DCHECK(IsRunningInCorrectSequence());
   if (IsCancelled()) {
@@ -273,16 +268,14 @@
     return;
   }
 
-  if (file_size <= 0) {
-    file_size = file.GetLength();
-    if (file_size < 0) {
-      Error(error::kImageOpenError);
-      return;
-    }
+  int64_t file_size = file.GetLength();
+  if (file_size < 0) {
+    Error(error::kImageOpenError);
+    return;
   }
 
   PostTask(base::BindOnce(&Operation::MD5Chunk, this, std::move(file), 0,
-                          file_size, progress_offset, progress_scale,
+                          base::checked_cast<size_t>(file_size),
                           std::move(callback)));
 }
 
@@ -292,10 +285,8 @@
 
 void Operation::MD5Chunk(
     base::File file,
-    int64_t bytes_processed,
-    int64_t bytes_total,
-    int progress_offset,
-    int progress_scale,
+    size_t bytes_processed,
+    size_t bytes_total,
     base::OnceCallback<void(const std::string&)> callback) {
   DCHECK(IsRunningInCorrectSequence());
   if (IsCancelled())
@@ -303,8 +294,8 @@
 
   CHECK_LE(bytes_processed, bytes_total);
 
-  int read_size = std::min(bytes_total - bytes_processed,
-                           static_cast<int64_t>(kMD5BufferSize));
+  std::array<uint8_t, 1024> buffer;
+  size_t read_size = std::min(bytes_total - bytes_processed, buffer.size());
 
   if (read_size == 0) {
     // Nothing to read, we are done.
@@ -312,22 +303,19 @@
     base::MD5Final(&digest, &md5_context_);
     std::move(callback).Run(base::MD5DigestToBase16(digest));
   } else {
-    auto buffer = base::HeapArray<char>::Uninit(kMD5BufferSize);
-    int len =
-        file.Read(bytes_processed, base::as_writable_bytes(buffer.as_span()))
-            .value_or(0);
+    int64_t offset = base::checked_cast<int64_t>(bytes_processed);
+    auto target = base::span(buffer).first(read_size);
 
-    if (len == read_size) {
+    if (file.ReadAndCheck(offset, target)) {
       // Process data.
-      base::MD5Update(&md5_context_, std::string_view(buffer.data(), len));
-      int percent_curr =
-          ((bytes_processed + len) * progress_scale) / bytes_total +
-          progress_offset;
+      base::MD5Update(&md5_context_, target);
+      bytes_processed += read_size;
+      int percent_curr = (bytes_processed * kProgressComplete) / bytes_total;
       SetProgress(percent_curr);
 
-      PostTask(base::BindOnce(
-          &Operation::MD5Chunk, this, std::move(file), bytes_processed + len,
-          bytes_total, progress_offset, progress_scale, std::move(callback)));
+      PostTask(base::BindOnce(&Operation::MD5Chunk, this, std::move(file),
+                              bytes_processed, bytes_total,
+                              std::move(callback)));
       // Skip closing the file.
       return;
     } else {
diff --git a/chrome/browser/extensions/api/image_writer_private/operation.h b/chrome/browser/extensions/api/image_writer_private/operation.h
index d62800a..37103c1c 100644
--- a/chrome/browser/extensions/api/image_writer_private/operation.h
+++ b/chrome/browser/extensions/api/image_writer_private/operation.h
@@ -147,9 +147,6 @@
   // sum.  `progress_offset` is an percentage that will be added to the progress
   // of the MD5 sum before updating `progress_` but after scaling.
   void GetMD5SumOfFile(const base::FilePath& file,
-                       int64_t file_size,
-                       int progress_offset,
-                       int progress_scale,
                        base::OnceCallback<void(const std::string&)> callback);
 
   bool IsRunningInCorrectSequence() const;
@@ -207,10 +204,8 @@
 
   // Incrementally calculates the MD5 sum of a file.
   void MD5Chunk(base::File file,
-                int64_t bytes_processed,
-                int64_t bytes_total,
-                int progress_offset,
-                int progress_scale,
+                size_t bytes_processed,
+                size_t bytes_total,
                 const base::OnceCallback<void(const std::string&)> callback);
 
   // Callbacks for Extractor.
diff --git a/chrome/browser/extensions/api/image_writer_private/test_utils.h b/chrome/browser/extensions/api/image_writer_private/test_utils.h
index a0733d5..87a753da 100644
--- a/chrome/browser/extensions/api/image_writer_private/test_utils.h
+++ b/chrome/browser/extensions/api/image_writer_private/test_utils.h
@@ -35,7 +35,7 @@
 class ImageWriterFakeImageBurnerClient;
 #endif
 
-const char kDummyExtensionId[] = "DummyExtension";
+inline constexpr char kDummyExtensionId[] = "DummyExtension";
 
 // Default file size to use in tests.  Currently 32kB.
 const size_t kTestFileSize = 32 * 1024;
@@ -44,7 +44,7 @@
 // Pattern to use in the device file.
 const uint8_t kDevicePattern = 0xAA;  // 10101010
 // Disk file system type
-const char kTestFileSystemType[] = "vfat";
+inline constexpr char kTestFileSystemType[] = "vfat";
 
 // A mock around the operation manager for tracking callbacks.  Note that there
 // are non-virtual methods on this class that should not be called in tests.
diff --git a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc
index 1d55f1b..a7b8434 100644
--- a/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc
+++ b/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc
@@ -181,7 +181,7 @@
 
   SetStage(image_writer_api::Stage::kVerifyDownload);
 
-  GetMD5SumOfFile(image_path_, 0, 0, kProgressComplete,
+  GetMD5SumOfFile(image_path_,
                   base::BindOnce(&WriteFromUrlOperation::VerifyDownloadCompare,
                                  this, std::move(continuation)));
 }
diff --git a/chrome/browser/extensions/api/management/management_apitest.cc b/chrome/browser/extensions/api/management/management_apitest.cc
index 5e7ea951..120cb69 100644
--- a/chrome/browser/extensions/api/management/management_apitest.cc
+++ b/chrome/browser/extensions/api/management/management_apitest.cc
@@ -261,7 +261,8 @@
              });
            });)";
 
-    web_app::SetAutoAcceptPWAInstallConfirmationForTesting(true);
+    auto auto_accept_pwa_install_confirmation =
+        web_app::SetAutoAcceptPWAInstallConfirmationForTesting();
     const GURL start_url = https_test_server_.GetURL(web_app_start_url);
     webapps::AppId web_app_id =
         web_app::GenerateAppId(/*manifest_id_path=*/std::nullopt, start_url);
@@ -284,8 +285,6 @@
               web_app::proto::INSTALLED_WITH_OS_INTEGRATION);
     EXPECT_EQ(2, static_cast<int>(
                      provider->ui_manager().GetNumWindowsForApp(web_app_id)));
-
-    web_app::SetAutoAcceptPWAInstallConfirmationForTesting(false);
   }
 
   net::EmbeddedTestServer https_test_server_;
diff --git a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
index 0d04d61..dadd99f5 100644
--- a/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
+++ b/chrome/browser/extensions/api/omnibox/omnibox_api_interactive_test.cc
@@ -1775,36 +1775,49 @@
   // Check if the suggestion is received (+1 for the IPH).
   ASSERT_EQ(5U, result.size()) << AutocompleteResultAsString(result);
 
-  // Each extension suggestion header should match the extension name that
-  // it came from. Extension suggestions should also be grouped together.
+  // Suggestions from the same extension should be grouped together and their
+  // group id header should match the extension name.
   std::set<std::u16string> extension_names = {u"alpha", u"dog"};
+  std::set<omnibox::GroupId> extension_group_ids = {
+      omnibox::GROUP_UNSCOPED_EXTENSION_1, omnibox::GROUP_UNSCOPED_EXTENSION_2};
   {
-    EXPECT_EQ(AutocompleteProvider::TYPE_UNSCOPED_EXTENSION,
-              result.match_at(0).provider->type());
-    EXPECT_EQ(omnibox::GROUP_UNSCOPED_EXTENSION_1,
-              result.match_at(0).suggestion_group_id);
+    EXPECT_THAT(AutocompleteProvider::TYPE_UNSCOPED_EXTENSION,
+                testing::Eq(result.match_at(0).provider->type()));
+    EXPECT_THAT(extension_group_ids,
+                testing::Contains(result.match_at(0).suggestion_group_id));
     EXPECT_THAT(extension_names,
                 testing::Contains(result.GetHeaderForSuggestionGroup(
-                    *result.match_at(1).suggestion_group_id)));
-    extension_names.erase(result.GetHeaderForSuggestionGroup(
-        *result.match_at(1).suggestion_group_id));
+                    result.match_at(0).suggestion_group_id.value())));
+
+    EXPECT_THAT(AutocompleteProvider::TYPE_UNSCOPED_EXTENSION,
+                testing::Eq(result.match_at(1).provider->type()));
+    EXPECT_THAT(extension_group_ids,
+                testing::Contains(result.match_at(1).suggestion_group_id));
+    EXPECT_THAT(extension_names,
+                testing::Contains(result.GetHeaderForSuggestionGroup(
+                    result.match_at(1).suggestion_group_id.value())));
   }
-  // The third and fourth match should be from the other extension.
+
+  extension_group_ids.erase(result.match_at(1).suggestion_group_id.value());
+  extension_names.erase(result.GetHeaderForSuggestionGroup(
+      result.match_at(1).suggestion_group_id.value()));
+
   {
-    EXPECT_EQ(AutocompleteProvider::TYPE_UNSCOPED_EXTENSION,
-              result.match_at(2).provider->type());
-    EXPECT_EQ(omnibox::GROUP_UNSCOPED_EXTENSION_2,
-              result.match_at(2).suggestion_group_id);
+    EXPECT_THAT(AutocompleteProvider::TYPE_UNSCOPED_EXTENSION,
+                testing::Eq(result.match_at(2).provider->type()));
+    EXPECT_THAT(extension_group_ids,
+                testing::Contains(result.match_at(2).suggestion_group_id));
     EXPECT_THAT(extension_names,
                 testing::Contains(result.GetHeaderForSuggestionGroup(
-                    *result.match_at(2).suggestion_group_id)));
-    EXPECT_EQ(AutocompleteProvider::TYPE_UNSCOPED_EXTENSION,
-              result.match_at(3).provider->type());
-    EXPECT_EQ(omnibox::GROUP_UNSCOPED_EXTENSION_2,
-              result.match_at(3).suggestion_group_id);
+                    result.match_at(2).suggestion_group_id.value())));
+
+    EXPECT_THAT(AutocompleteProvider::TYPE_UNSCOPED_EXTENSION,
+                testing::Eq(result.match_at(3).provider->type()));
+    EXPECT_THAT(extension_group_ids,
+                testing::Contains(result.match_at(3).suggestion_group_id));
     EXPECT_THAT(extension_names,
                 testing::Contains(result.GetHeaderForSuggestionGroup(
-                    *result.match_at(3).suggestion_group_id)));
+                    result.match_at(3).suggestion_group_id.value())));
   }
 }
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index 4ac2686..10af690fd 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -64,7 +64,6 @@
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/recently_audible_helper.h"
-#include "chrome/browser/ui/tabs/split_tab_data.h"
 #include "chrome/browser/ui/tabs/tab_enums.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
@@ -88,6 +87,7 @@
 #include "components/tab_groups/tab_group_color.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "components/tabs/public/split_tab_data.h"
 #include "components/tabs/public/split_tab_id.h"
 #include "components/translate/core/browser/language_state.h"
 #include "components/translate/core/common/language_detection_details.h"
diff --git a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
index aa4c5914..4fc79ca 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api_unittest.cc
@@ -25,8 +25,6 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
-#include "chrome/browser/ui/tabs/split_tab_collection.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -39,6 +37,8 @@
 #include "components/saved_tab_groups/public/types.h"
 #include "components/sessions/content/session_tab_helper.h"
 #include "components/tab_groups/tab_group_id.h"
+#include "components/tabs/public/split_tab_collection.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/web_contents_tester.h"
diff --git a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
index 8bab3abf..198e134 100644
--- a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "extensions/browser/api/web_request/web_request_api.h"
+
 #include <stddef.h>
 #include <stdint.h>
 
@@ -41,10 +43,10 @@
 #include "extensions/browser/api/declarative_net_request/test_utils.h"
 #include "extensions/browser/api/web_request/extension_web_request_event_router.h"
 #include "extensions/browser/api/web_request/upload_data_presenter.h"
-#include "extensions/browser/api/web_request/web_request_api.h"
 #include "extensions/browser/api/web_request/web_request_api_constants.h"
 #include "extensions/browser/api/web_request/web_request_api_helpers.h"
 #include "extensions/browser/api/web_request/web_request_info.h"
+#include "extensions/buildflags/buildflags.h"
 #include "extensions/common/api/declarative_net_request.h"
 #include "extensions/common/api/web_request.h"
 #include "extensions/common/constants.h"
@@ -57,6 +59,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom-forward.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace helpers = extension_web_request_api_helpers;
 namespace keys = extension_web_request_api_constants;
 namespace web_request = extensions::api::web_request;
diff --git a/chrome/browser/extensions/api/web_request/web_request_event_details_unittest.cc b/chrome/browser/extensions/api/web_request/web_request_event_details_unittest.cc
index 88bac723..fafae55 100644
--- a/chrome/browser/extensions/api/web_request/web_request_event_details_unittest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_event_details_unittest.cc
@@ -2,20 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "extensions/browser/api/web_request/web_request_event_details.h"
+
 #include <memory>
 
 #include "base/check_deref.h"
 #include "base/values.h"
 #include "extensions/browser/api/web_request/web_request_api_constants.h"
 #include "extensions/browser/api/web_request/web_request_api_helpers.h"
-#include "extensions/browser/api/web_request/web_request_event_details.h"
 #include "extensions/browser/api/web_request/web_request_info.h"
+#include "extensions/buildflags/buildflags.h"
 #include "google_apis/gaia/gaia_urls.h"
 #include "net/http/http_response_headers.h"
 #include "net/http/http_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
+static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
+
 namespace extensions {
 
 TEST(WebRequestEventDetailsTest, SetResponseHeaders) {
diff --git a/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.cc b/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.cc
index 83a0936a..28127d7 100644
--- a/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.cc
+++ b/chrome/browser/extensions/desktop_android/desktop_android_extensions_browser_client.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/profiles/profile_selections.h"
+#include "components/signin/core/browser/signin_header_helper.h"
 #include "components/update_client/update_client.h"
 #include "components/value_store/value_store_factory.h"
 #include "components/version_info/version_info.h"
@@ -42,6 +43,7 @@
 #include "extensions/browser/updater/scoped_extension_updater_keep_alive.h"
 #include "extensions/browser/url_request_util.h"
 #include "extensions/common/features/feature_channel.h"
+#include "google_apis/gaia/gaia_urls.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 
 using content::BrowserContext;
@@ -103,6 +105,20 @@
         *Profile::FromBrowserContext(context), factory, observer);
   }
 
+  // The following code is used to support chrome.webRequest api for
+  // CalculateOnHeadersReceivedDelta(), until ChromeExtensionAPIClient is ported
+  // for desktop android.
+  bool ShouldHideResponseHeader(const GURL& url,
+                                const std::string& header_name) const override {
+    // Gaia may send a OAUth2 authorization code in the Dice response header,
+    // which could allow an extension to generate a refresh token for the
+    // account.
+    return url.host_piece() ==
+               GaiaUrls::GetInstance()->gaia_url().host_piece() &&
+           base::CompareCaseInsensitiveASCII(header_name,
+                                             signin::kDiceResponseHeader) == 0;
+  }
+
  private:
   std::unique_ptr<MessagingDelegate> messaging_delegate_;
 };
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc
index 0e1e7f7..8ec335e9 100644
--- a/chrome/browser/extensions/extension_tab_util.cc
+++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -14,8 +14,8 @@
 
 #include "chrome/browser/extensions/window_controller_list.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "components/sessions/content/session_tab_helper.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/navigation_handle.h"
@@ -297,10 +297,6 @@
   // will override this default.
   bool active = params.active.value_or(true);
 
-  // Default to unsplit for the new tab. The presence of the 'split' property
-  // will override this default.
-  bool split = params.split.value_or(false);
-
   // Default to not pinning the tab. Setting the 'pinned' property to true
   // will override this default.
   bool pinned = params.pinned.value_or(false);
@@ -379,11 +375,6 @@
   if (active)
     navigate_params.navigated_or_inserted_contents->SetInitialFocus();
 
-  if (split) {
-    tab_strip->AddToNewSplit({new_index},
-                             split_tabs::SplitTabLayout::kVertical);
-  }
-
   ExtensionTabUtil::ScrubTabBehavior scrub_tab_behavior =
       ExtensionTabUtil::GetScrubTabBehavior(
           function->extension(), function->source_context_type(),
diff --git a/chrome/browser/extensions/extension_tab_util.h b/chrome/browser/extensions/extension_tab_util.h
index c79667d..8785b77 100644
--- a/chrome/browser/extensions/extension_tab_util.h
+++ b/chrome/browser/extensions/extension_tab_util.h
@@ -106,7 +106,6 @@
     std::optional<int> opener_tab_id;
     std::optional<std::string> url;
     std::optional<bool> active;
-    std::optional<bool> split;
     std::optional<bool> pinned;
     std::optional<int> index;
     std::optional<int> bookmark_id;
diff --git a/chrome/browser/extensions/extension_webkit_preferences.cc b/chrome/browser/extensions/extension_webkit_preferences.cc
index dff7cd5..9615662 100644
--- a/chrome/browser/extensions/extension_webkit_preferences.cc
+++ b/chrome/browser/extensions/extension_webkit_preferences.cc
@@ -33,7 +33,6 @@
   }
 
   if (extension->is_platform_app()) {
-    webkit_prefs->databases_enabled = false;
     webkit_prefs->local_storage_enabled = false;
     webkit_prefs->sync_xhr_in_documents_enabled = false;
     webkit_prefs->cookie_enabled = false;
diff --git a/chrome/browser/extensions/service_worker_tracking_browsertest.cc b/chrome/browser/extensions/service_worker_tracking_browsertest.cc
index d05f14d8..76aec9e9 100644
--- a/chrome/browser/extensions/service_worker_tracking_browsertest.cc
+++ b/chrome/browser/extensions/service_worker_tracking_browsertest.cc
@@ -114,7 +114,9 @@
     extension_ = nullptr;
   }
 
-  void LoadServiceWorkerExtension() {
+  virtual std::string GetExtensionPageContent() const { return "<p>page</p>"; }
+
+  virtual void LoadServiceWorkerExtension() {
     // Load a basic extension with a service worker and wait for the worker to
     // start running.
     static constexpr char kManifest[] =
@@ -143,7 +145,7 @@
     test_dir->WriteManifest(kManifest);
     test_dir->WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundScript);
     test_dir->WriteFile(FILE_PATH_LITERAL("extension_page_tab.html"),
-                        "<p>page</p>");
+                        GetExtensionPageContent());
     ExtensionTestMessageListener extension_oninstall_listener_fired(
         "installed listener fired");
     const Extension* extension = LoadExtension(
@@ -176,6 +178,14 @@
 
   const Extension* extension() { return extension_; }
 
+  TestExtensionDir* test_extension_dir() {
+    if (test_extension_dirs_.size() != 1) {
+      ADD_FAILURE() << "Expected exactly one test extension directory";
+      return nullptr;
+    }
+    return test_extension_dirs_.front().get();
+  }
+
   raw_ptr<const Extension> extension_;
   // Ensure `TestExtensionDir`s live past the test helper methods finishing.
   std::vector<std::unique_ptr<TestExtensionDir>> test_extension_dirs_;
@@ -783,6 +793,96 @@
   EXPECT_FALSE(web_contents->IsCrashed());
 }
 
+// Tests tracking behavior of the main extension service worker when an
+// additional service worker is registered by the extension for a sub-scope
+// via `navigator.serviceWorker.register()` from an extension page.
+class ServiceWorkerSubScopeWorkerTrackingBrowserTest
+    : public ServiceWorkerIdTrackingBrowserTest {
+ protected:
+  std::string GetExtensionPageContent() const override {
+    return R"(<script src="/page.js"></script>)";
+  }
+
+  void LoadServiceWorkerExtension() override {
+    ServiceWorkerIdTrackingBrowserTest::LoadServiceWorkerExtension();
+
+    // Code for a service worker that will be registered for a sub-scope
+    // of the extension root scope. This service worker is not allowed
+    // access to extension APIs, as it's not listed in the manifest.
+    {
+      base::ScopedAllowBlockingForTesting allow_blocking;
+      base::CreateDirectory(test_extension_dir()->UnpackedPath().Append(
+          FILE_PATH_LITERAL("subscope")));
+      test_extension_dir()->WriteFile(FILE_PATH_LITERAL("subscope/sw.js"), R"(
+          console.log("subscope service worker");
+      )");
+    }
+
+    // Code for the script that will be executed as part of the extension page.
+    // This registers the previously defined service worker.
+    test_extension_dir()->WriteFile(FILE_PATH_LITERAL("page.js"), R"(
+        navigator.serviceWorker.register("subscope/sw.js").then(function() {
+          // Wait until the service worker is active.
+          return navigator.serviceWorker.ready;
+        }).then(function(r) {
+          console.log("registration successful");
+        }).catch(function(err) {
+          console.log("registration error: " + err.message);
+        });
+    )");
+  }
+};
+
+// Tests that stopping a service worker that was registered for
+// a sub-scope via `navigation.serviceWorker.register()`, rather
+// than being declared in the extension's manifest does not influence the
+// tracking of the main extension service worker. Regression test for
+// crbug.com/395536907.
+IN_PROC_BROWSER_TEST_F(ServiceWorkerSubScopeWorkerTrackingBrowserTest,
+                       StoppingSubScopeWorkerDoesNotAffectExtensionWorker) {
+  ASSERT_NO_FATAL_FAILURE(LoadServiceWorkerExtensionAndOpenExtensionTab());
+
+  // Wait for a console message that confirms the service worker for
+  // the sub-scope has been registered. Note that we can't use
+  // ExtensionTestMessageListener here since extension APIs are not
+  // available.
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  content::WebContentsConsoleObserver console_observer(web_contents);
+  console_observer.SetPattern("registration successful");
+  ASSERT_TRUE(console_observer.Wait());
+
+  // Confirm that we are tracking the main extension service worker.
+  std::optional<WorkerId> extension_service_worker_id =
+      GetWorkerIdForExtension();
+  ASSERT_TRUE(extension_service_worker_id);
+
+  // Check that there's 2 service workers running in total.
+  content::ServiceWorkerContext* sw_context =
+      GetServiceWorkerContext(profile());
+  ASSERT_TRUE(sw_context);
+  EXPECT_EQ(sw_context->GetRunningServiceWorkerInfos().size(), 2ul);
+  // One of them should be the main extension service worker.
+  EXPECT_TRUE(sw_context->GetRunningServiceWorkerInfos().contains(
+      extension_service_worker_id->version_id));
+
+  // Stop the sub-scope service worker.
+  TestServiceWorkerTaskQueueObserver untracked_observer;
+  GURL sub_scope(extension()->url().spec() + "subscope/");
+  content::StopServiceWorkerForScope(sw_context, sub_scope, base::DoNothing());
+  // Wait until the code responsible for untracking workers is called.
+  untracked_observer.WaitForUntrackServiceWorkerState(sub_scope);
+
+  // Verify that the main extension service worker is still tracked as running
+  // by the task queue.
+  ServiceWorkerTaskQueue::WorkerState* worker_state = GetWorkerState();
+  EXPECT_EQ(worker_state->browser_state(),
+            ServiceWorkerTaskQueue::BrowserState::kReady);
+  EXPECT_EQ(worker_state->renderer_state(),
+            ServiceWorkerTaskQueue::RendererState::kActive);
+  EXPECT_TRUE(worker_state->worker_id());
+}
+
 }  // namespace
 
 }  // namespace extensions
diff --git a/chrome/browser/fast_checkout/fast_checkout_trigger_validator.h b/chrome/browser/fast_checkout/fast_checkout_trigger_validator.h
index 72e5a1c..d16a275 100644
--- a/chrome/browser/fast_checkout/fast_checkout_trigger_validator.h
+++ b/chrome/browser/fast_checkout/fast_checkout_trigger_validator.h
@@ -8,7 +8,7 @@
 #include "components/autofill/core/browser/foundations/browser_autofill_manager.h"
 #include "components/autofill/core/browser/integrators/fast_checkout/fast_checkout_enums.h"
 
-constexpr char kUmaKeyFastCheckoutTriggerOutcome[] =
+inline constexpr char kUmaKeyFastCheckoutTriggerOutcome[] =
     "Autofill.FastCheckout.TriggerOutcome";
 
 // Checks whether a Fast Checkout run should be permitted or not.
diff --git a/chrome/browser/feedback/system_logs/log_sources/related_website_sets_source_unittest.cc b/chrome/browser/feedback/system_logs/log_sources/related_website_sets_source_unittest.cc
index e899857..2a4c43c3 100644
--- a/chrome/browser/feedback/system_logs/log_sources/related_website_sets_source_unittest.cc
+++ b/chrome/browser/feedback/system_logs/log_sources/related_website_sets_source_unittest.cc
@@ -145,11 +145,9 @@
   SetGlobalSets(net::GlobalFirstPartySets(
       base::Version("0.0"),
       {{primary1_site,
-        {net::FirstPartySetEntry(primary1_site, net::SiteType::kPrimary,
-                                 std::nullopt)}},
+        {net::FirstPartySetEntry(primary1_site, net::SiteType::kPrimary)}},
        {associate_site,
-        {net::FirstPartySetEntry(primary1_site, net::SiteType::kAssociated,
-                                 0)}}},
+        {net::FirstPartySetEntry(primary1_site, net::SiteType::kAssociated)}}},
       {{primary1_cctld, primary1_site}}));
 
   // The context config of the profile adds a new set:
@@ -159,10 +157,10 @@
       net::FirstPartySetsContextConfig::Create(
           {{primary2_site,
             net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                primary2_site, net::SiteType::kPrimary, std::nullopt))},
+                primary2_site, net::SiteType::kPrimary))},
            {service_site,
             net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                primary2_site, net::SiteType::kService, std::nullopt))}})
+                primary2_site, net::SiteType::kService))}})
           .value());
 
   service()->InitForTesting();
@@ -200,23 +198,19 @@
       base::Version("0.0"),
       {
           {primary,
-           {net::FirstPartySetEntry(primary, net::SiteType::kPrimary,
-                                    std::nullopt)}},
+           {net::FirstPartySetEntry(primary, net::SiteType::kPrimary)}},
           {associated3,
-           {net::FirstPartySetEntry(primary, net::SiteType::kAssociated, 2)}},
+           {net::FirstPartySetEntry(primary, net::SiteType::kAssociated)}},
           {associated1,
-           {net::FirstPartySetEntry(primary, net::SiteType::kAssociated, 0)}},
+           {net::FirstPartySetEntry(primary, net::SiteType::kAssociated)}},
           {associated2,
-           {net::FirstPartySetEntry(primary, net::SiteType::kAssociated, 1)}},
+           {net::FirstPartySetEntry(primary, net::SiteType::kAssociated)}},
           {service2,
-           {net::FirstPartySetEntry(primary, net::SiteType::kService,
-                                    std::nullopt)}},
+           {net::FirstPartySetEntry(primary, net::SiteType::kService)}},
           {service1,
-           {net::FirstPartySetEntry(primary, net::SiteType::kService,
-                                    std::nullopt)}},
+           {net::FirstPartySetEntry(primary, net::SiteType::kService)}},
           {service3,
-           {net::FirstPartySetEntry(primary, net::SiteType::kService,
-                                    std::nullopt)}},
+           {net::FirstPartySetEntry(primary, net::SiteType::kService)}},
       },
       {}));
 
diff --git a/chrome/browser/first_party_sets/first_party_sets_policy_service_unittest.cc b/chrome/browser/first_party_sets/first_party_sets_policy_service_unittest.cc
index 9a31807..f467cee 100644
--- a/chrome/browser/first_party_sets/first_party_sets_policy_service_unittest.cc
+++ b/chrome/browser/first_party_sets/first_party_sets_policy_service_unittest.cc
@@ -230,7 +230,7 @@
           {{net::SchemefulSite(GURL("https://example.test")),
             net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
                 net::SchemefulSite(GURL("https://primary.test")),
-                net::SiteType::kAssociated, std::nullopt))}})
+                net::SiteType::kAssociated))}})
           .value());
   service()->InitForTesting();
 
@@ -258,7 +258,7 @@
           {{example_site,
             net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
                 net::SchemefulSite(GURL("https://primary.test")),
-                net::SiteType::kAssociated, std::nullopt))}})
+                net::SiteType::kAssociated))}})
           .value());
   service()->InitForTesting();
   EXPECT_TRUE(service()->IsSiteInManagedSet(example_site));
@@ -323,7 +323,7 @@
           {{example_site,
             net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
                 net::SchemefulSite(GURL("https://primary.test")),
-                net::SiteType::kAssociated, std::nullopt))}})
+                net::SiteType::kAssociated))}})
           .value());
   SetRwsEnabledViaPref(false);
   service()->InitForTesting();
@@ -343,11 +343,9 @@
       kVersion,
       {
           {associate1_site,
-           {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated,
-                                    0)}},
+           {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated)}},
           {primary_site,
-           {net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary,
-                                    std::nullopt)}},
+           {net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary)}},
       },
       {}));
 
@@ -364,10 +362,10 @@
        FindEntry_FpsEnabled_ReturnsEmptyUntilAllSetsReady) {
   net::SchemefulSite primary_site(GURL("https://primary.test"));
   net::SchemefulSite associate1_site(GURL("https://associate1.test"));
-  net::FirstPartySetEntry primary_entry(net::FirstPartySetEntry(
-      primary_site, net::SiteType::kPrimary, std::nullopt));
+  net::FirstPartySetEntry primary_entry(
+      net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary));
   net::FirstPartySetEntry associate1_entry(
-      net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated, 0));
+      net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated));
 
   SetRwsEnabledViaPref(true);
   // Verify that FindEntry returns empty if the global sets and profile sets
@@ -403,10 +401,10 @@
 
   net::SchemefulSite primary_site(GURL("https://primary.test"));
   net::SchemefulSite associate_site(GURL("https://associate.test"));
-  net::FirstPartySetEntry primary_entry(net::FirstPartySetEntry(
-      primary_site, net::SiteType::kPrimary, std::nullopt));
+  net::FirstPartySetEntry primary_entry(
+      net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary));
   net::FirstPartySetEntry associate_entry(
-      net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated, 0));
+      net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated));
 
   SetRwsEnabledViaPref(true);
 
@@ -441,10 +439,10 @@
 
   net::SchemefulSite primary_site(GURL("https://primary.test"));
   net::SchemefulSite associate_site(GURL("https://associate.test"));
-  net::FirstPartySetEntry primary_entry(net::FirstPartySetEntry(
-      primary_site, net::SiteType::kPrimary, std::nullopt));
+  net::FirstPartySetEntry primary_entry(
+      net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary));
   net::FirstPartySetEntry associate_entry(
-      net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated, 0));
+      net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated));
 
   SetRwsEnabledViaPref(true);
 
@@ -480,11 +478,9 @@
       kVersion,
       {
           {primary_site,
-           {net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary,
-                                    std::nullopt)}},
+           {net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary)}},
           {associate_site,
-           {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated,
-                                    0)}},
+           {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated)}},
       },
       {}));
 
@@ -506,10 +502,10 @@
        ForEachEffectiveSetEntry_ReturnsEmptyUntilAllSetsReady) {
   net::SchemefulSite primary_site(GURL("https://primary.test"));
   net::SchemefulSite associate_site(GURL("https://associate.test"));
-  net::FirstPartySetEntry primary_entry(net::FirstPartySetEntry(
-      primary_site, net::SiteType::kPrimary, std::nullopt));
+  net::FirstPartySetEntry primary_entry(
+      net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary));
   net::FirstPartySetEntry associate_entry(
-      net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated, 0));
+      net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated));
 
   SetRwsEnabledViaPref(true);
   // Verify that ForEachEffectiveSetEntry returns false if FPS is not
@@ -558,12 +554,12 @@
   net::SchemefulSite primary_site(GURL("https://primary.test"));
   net::SchemefulSite associate_site(GURL("https://associate.test"));
   net::SchemefulSite service_site(GURL("https://service.test"));
-  net::FirstPartySetEntry primary_entry(net::FirstPartySetEntry(
-      primary_site, net::SiteType::kPrimary, std::nullopt));
+  net::FirstPartySetEntry primary_entry(
+      net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary));
   net::FirstPartySetEntry associate_entry(
-      net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated, 0));
-  net::FirstPartySetEntry override_entry(net::FirstPartySetEntry(
-      primary_site, net::SiteType::kService, std::nullopt));
+      net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated));
+  net::FirstPartySetEntry override_entry(
+      net::FirstPartySetEntry(primary_site, net::SiteType::kService));
 
   // Create the global First-Party Sets with the following set:
   // { primary: "https://primary.test",
@@ -597,8 +593,7 @@
 TEST_F(FirstPartySetsPolicyServicePrefTest,
        OnProfileConfigReady_InitDisabled_NotifiesReadyWithConfig) {
   net::SchemefulSite test_primary(GURL("https://a.test"));
-  net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary,
-                                     std::nullopt);
+  net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary);
   net::FirstPartySetsContextConfig test_config =
       net::FirstPartySetsContextConfig::Create(
           {{test_primary, net::FirstPartySetEntryOverride(test_entry)}})
@@ -659,8 +654,7 @@
 TEST_F(FirstPartySetsPolicyServicePrefTest,
        OnRelatedWebsiteSetsEnabledChanged_Enables_WithConfig) {
   net::SchemefulSite test_primary(GURL("https://a.test"));
-  net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary,
-                                     std::nullopt);
+  net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary);
   net::FirstPartySetsContextConfig test_config =
       net::FirstPartySetsContextConfig::Create(
           {{test_primary, net::FirstPartySetEntryOverride(test_entry)}})
@@ -726,8 +720,7 @@
 
 TEST_F(FirstPartySetsPolicyServiceTest, NotifiesReadyWithConfigAndCacheFilter) {
   net::SchemefulSite test_primary(GURL("https://a.test"));
-  net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary,
-                                     std::nullopt);
+  net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary);
   net::FirstPartySetsContextConfig test_config =
       net::FirstPartySetsContextConfig::Create(
           {{test_primary, net::FirstPartySetEntryOverride(test_entry)}})
@@ -749,8 +742,7 @@
 TEST_F(FirstPartySetsPolicyServiceTest,
        ComputeFirstPartySetMetadata_BeforeInitialization) {
   net::SchemefulSite test_primary(GURL("https://a.test"));
-  net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary,
-                                     std::nullopt);
+  net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary);
   net::FirstPartySetsContextConfig test_config =
       net::FirstPartySetsContextConfig::Create(
           {{test_primary, net::FirstPartySetEntryOverride(test_entry)}})
@@ -771,8 +763,7 @@
 TEST_F(FirstPartySetsPolicyServiceTest,
        ComputeFirstPartySetMetadata_AfterInitialization_StillAsync) {
   net::SchemefulSite test_primary(GURL("https://a.test"));
-  net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary,
-                                     std::nullopt);
+  net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary);
   net::FirstPartySetsContextConfig test_config =
       net::FirstPartySetsContextConfig::Create(
           {{test_primary, net::FirstPartySetEntryOverride(test_entry)}})
@@ -791,8 +782,7 @@
 TEST_F(FirstPartySetsPolicyServiceTest,
        ComputeFirstPartySetMetadata_AfterInitialization_Sync) {
   net::SchemefulSite test_primary(GURL("https://a.test"));
-  net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary,
-                                     std::nullopt);
+  net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary);
   net::FirstPartySetsContextConfig test_config =
       net::FirstPartySetsContextConfig::Create(
           {{test_primary, net::FirstPartySetEntryOverride(test_entry)}})
@@ -812,8 +802,7 @@
 TEST_P(FirstPartySetsPolicyServicePrefTest,
        ComputeFirstPartySetMetadata_PrefDisabled) {
   net::SchemefulSite test_primary(GURL("https://a.test"));
-  net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary,
-                                     std::nullopt);
+  net::FirstPartySetEntry test_entry(test_primary, net::SiteType::kPrimary);
   net::FirstPartySetsContextConfig test_config =
       net::FirstPartySetsContextConfig::Create(
           {{test_primary, net::FirstPartySetEntryOverride(test_entry)}})
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 775db982..cf221ff7 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -3088,17 +3088,17 @@
   {
     "name": "enable-desktop-pwas-tab-strip",
     "owners": [ "pwa-team@google.com" ],
-    "expiry_milestone": 140
+    "expiry_milestone": 150
   },
   {
     "name": "enable-desktop-pwas-tab-strip-customizations",
     "owners": [ "cros-web-apps-core@google.com", "pwa-team@google.com" ],
-    "expiry_milestone": 140
+    "expiry_milestone": 150
   },
   {
     "name": "enable-desktop-pwas-tab-strip-settings",
     "owners": [ "pwa-team@google.com" ],
-    "expiry_milestone": 132
+    "expiry_milestone": 150
   },
   {
     "name": "enable-disco-feed-endpoint",
@@ -3953,6 +3953,11 @@
     "expiry_milestone": 125
   },
   {
+    "name": "enable-pix-account-linking",
+    "owners": [ "vishwasuppoor@google.com", "chrome-payments-team@google.com", "payments-autofill-team@google.com" ],
+    "expiry_milestone": 143
+  },
+  {
     "name": "enable-pix-payments",
     "owners": [ "siashah@google.com", "chrome-payments-team@google.com", "payments-autofill-team@google.com" ],
     "expiry_milestone": 140
@@ -7456,7 +7461,7 @@
   {
     "name": "pdf-xfa-forms",
     "owners": [ "thestig@chromium.org", "//pdf/OWNERS" ],
-    "expiry_milestone": 135
+    "expiry_milestone": 140
   },
   {
     "name": "permission-element",
@@ -7501,6 +7506,14 @@
     "expiry_milestone": 130
   },
   {
+    "name": "pinned-tab-toast-on-close",
+    "owners": [
+      "dljames@chromium.org",
+      "top-chrome-desktop-ui@google.com"
+    ],
+    "expiry_milestone": 150
+  },
+  {
     "name": "platform-keys-changes-wave-1",
     "owners": [
       "fsandrade@chromium.org",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 191efbe8..3dde30f 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -480,11 +480,6 @@
     "If enabled, the full screen signin promo will be forced to show up at "
     "Chrome start-up.";
 
-const char kFontationsFontBackendName[] = "Enable Fontations font backend";
-const char kFontationsFontBackendDescription[] =
-    "If enabled, the Fontations font backend will be used for web fonts where "
-    "otherwise FreeType would have been used.";
-
 const char kFwupdDeveloperModeName[] = "Enable fwupd developer mode";
 const char kFwupdDeveloperModeDescription[] =
     "Allows display and installation in UI of unauthenticated firmware by "
@@ -1373,6 +1368,11 @@
     "system compositor.";
 
 #if BUILDFLAG(IS_ANDROID)
+const char kEnablePixAccountLinkingName[] = "Enable Pix account linking";
+const char kEnablePixAccountLinkingDescription[] =
+    "When enabled, users without linked Pix accounts will be prompted to link "
+    "their Pix accounts to Google Wallet.";
+
 const char kEnablePixPaymentsName[] = "Enable Pix payments";
 const char kEnablePixPaymentsDescription[] =
     "When enabled, users will be offered to pay for Pix transactions using "
@@ -3306,17 +3306,6 @@
     "Allows prerendering pages to execute more lifecycle updates, such as "
     "prepaint, before activation";
 
-const char kWarmUpCompositorName[] = "Warm up compositor";
-const char kWarmUpCompositorDescription[] =
-    "Allows compositor to start warming up on certain signals";
-
-const char kPrerender2WarmUpCompositorName[] =
-    "Warm up compositor on prerendering";
-const char kPrerender2WarmUpCompositorDescription[] =
-    "Enables compositor warming up on particular loading events of prerender "
-    "initial navigation. Requires chrome://flags/#compositor-warm-up to be "
-    "enabled";
-
 const char kPrerender2ForNewTabPageAndroidName[] =
     "Enable prerendering on New Tab Page Android";
 const char kPrerender2ForNewTabPageAndroidDescription[] =
@@ -3985,6 +3974,11 @@
 const char kTopChromeToastRefinementsName[] = "Top Chrome Toast Refinements";
 const char kTopChromeToastRefinementsDescription[] =
     "Enables the use of options to control which toasts appear.";
+
+const char kPinnedTabToastOnCloseName[] = "Pinned Tab Toast On Close";
+const char kPinnedTabToastOnCloseDescription[] =
+    "Enable to show a confirmation toast that displays when a pinned tab is "
+    "closed via the keyboard shortcut.";
 #endif
 
 const char kTopChromeTouchUiName[] = "Touch UI Layout";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index b63113f..1aff2eb8 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -785,6 +785,9 @@
 extern const char kEnableDelegatedCompositingDescription[];
 
 #if BUILDFLAG(IS_ANDROID)
+extern const char kEnablePixAccountLinkingName[];
+extern const char kEnablePixAccountLinkingDescription[];
+
 extern const char kEnablePixPaymentsName[];
 extern const char kEnablePixPaymentsDescription[];
 
@@ -1907,12 +1910,6 @@
 extern const char kPrerender2EarlyDocumentLifecycleUpdateName[];
 extern const char kPrerender2EarlyDocumentLifecycleUpdateDescription[];
 
-extern const char kWarmUpCompositorName[];
-extern const char kWarmUpCompositorDescription[];
-
-extern const char kPrerender2WarmUpCompositorName[];
-extern const char kPrerender2WarmUpCompositorDescription[];
-
 extern const char kPrerender2ForNewTabPageAndroidName[];
 extern const char kPrerender2ForNewTabPageAndroidDescription[];
 
@@ -2301,6 +2298,9 @@
 
 extern const char kTopChromeToastRefinementsName[];
 extern const char kTopChromeToastRefinementsDescription[];
+
+extern const char kPinnedTabToastOnCloseName[];
+extern const char kPinnedTabToastOnCloseDescription[];
 #endif
 
 extern const char kTopChromeTouchUiName[];
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index cbbb38f..c418a0ed 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -179,7 +179,6 @@
     &history_clusters::internal::kOmniboxAction,
     &kAdaptiveButtonInTopToolbarCustomizationV2,
     &kAdaptiveButtonInTopToolbarPageSummary,
-    &kAllowNewIncognitoTabIntents,
     &kAllowTabClosingUponMinimization,
     &kAndroidAppIntegration,
     &kAndroidAppIntegrationV2,
@@ -465,10 +464,6 @@
              "AdaptiveButtonInTopToolbarPageSummary",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kAllowNewIncognitoTabIntents,
-             "AllowNewIncognitoTabIntents",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kAllowTabClosingUponMinimization,
              "AllowTabClosingUponMinimization",
              base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h
index a76bb28..c56769af 100644
--- a/chrome/browser/flags/android/chrome_feature_list.h
+++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -16,7 +16,6 @@
 // Alphabetical:
 BASE_DECLARE_FEATURE(kAdaptiveButtonInTopToolbarCustomizationV2);
 BASE_DECLARE_FEATURE(kAdaptiveButtonInTopToolbarPageSummary);
-BASE_DECLARE_FEATURE(kAllowNewIncognitoTabIntents);
 BASE_DECLARE_FEATURE(kAllowTabClosingUponMinimization);
 BASE_DECLARE_FEATURE(kAndroidAppIntegration);
 BASE_DECLARE_FEATURE(kAndroidAppIntegrationV2);
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 0ee2fc3..ac06dde 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
@@ -158,7 +158,6 @@
             "AdaptiveButtonInTopToolbarCustomizationV2";
     public static final String ADAPTIVE_BUTTON_IN_TOP_TOOLBAR_PAGE_SUMMARY =
             "AdaptiveButtonInTopToolbarPageSummary";
-    public static final String ALLOW_NEW_INCOGNITO_TAB_INTENTS = "AllowNewIncognitoTabIntents";
     public static final String ALLOW_TAB_CLOSING_UPON_MINIMIZATION =
             "AllowTabClosingUponMinimization";
     public static final String ALWAYS_BLOCK_3PCS_INCOGNITO = "AlwaysBlock3pcsIncognito";
diff --git a/chrome/browser/glic/e2e_test/internal_test_placeholder_constants.h b/chrome/browser/glic/e2e_test/internal_test_placeholder_constants.h
index 506e66485..9eee867 100644
--- a/chrome/browser/glic/e2e_test/internal_test_placeholder_constants.h
+++ b/chrome/browser/glic/e2e_test/internal_test_placeholder_constants.h
@@ -13,8 +13,8 @@
 
 namespace glic::test {
 
-const char kAllowedHostAndPathForWpr[] = "";
-const char kTestAccountLabel[] = "";
+inline constexpr char kAllowedHostAndPathForWpr[] = "";
+inline constexpr char kTestAccountLabel[] = "";
 auto kWprArguments = std::vector<std::string>{};
 
 }  // namespace glic::test
diff --git a/chrome/browser/glic/fre/glic_fre_controller.cc b/chrome/browser/glic/fre/glic_fre_controller.cc
index 19a6213..74a8600 100644
--- a/chrome/browser/glic/fre/glic_fre_controller.cc
+++ b/chrome/browser/glic/fre/glic_fre_controller.cc
@@ -19,6 +19,7 @@
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/glic/fre/fre_util.h"
 #include "chrome/browser/glic/fre/glic_fre_dialog_view.h"
+#include "chrome/browser/glic/glic_enabling.h"
 #include "chrome/browser/glic/glic_keyed_service.h"
 #include "chrome/browser/glic/glic_keyed_service_factory.h"
 #include "chrome/browser/glic/glic_pref_names.h"
@@ -30,6 +31,7 @@
 #include "chrome/browser/shell_integration.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/tabs/public/tab_dialog_manager.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/common/channel_info.h"
@@ -79,8 +81,7 @@
 bool GlicFreController::ShouldShowFreDialog() {
   // If the given profile has not previously completed the FRE, then it should
   // be shown.
-  return profile_->GetPrefs()->GetInteger(prefs::kGlicCompletedFre) !=
-         static_cast<int>(prefs::FreStatus::kCompleted);
+  return !GlicEnabling::HasConsentedForProfile(profile_);
 }
 
 bool GlicFreController::CanShowFreDialog(Browser* browser) {
@@ -96,6 +97,17 @@
   return tab && tab->CanShowModalUI();
 }
 
+void GlicFreController::OpenFreDialogInNewTab(BrowserWindowInterface* bwi) {
+  Browser* browser = bwi->GetBrowserForMigrationOnly();
+  if (!ShouldShowFreDialog()) {
+    return;
+  }
+  chrome::AddAndReturnTabAt(browser, GURL(), /*index=*/-1, /*foreground=*/true);
+  if (CanShowFreDialog(browser)) {
+    ShowFreDialog(browser);
+  }
+}
+
 void GlicFreController::ShowFreDialog(Browser* browser) {
   CHECK(CanShowFreDialog(browser));
 
@@ -112,7 +124,6 @@
     // Sign-in required and handled by AuthController. In this case, do not
     // record the FRE load time metric.
     show_start_time_ = base::TimeTicks();
-    return;
   }
 }
 
@@ -133,18 +144,17 @@
 
   CreateView();
 
-  tabs::TabInterface* tab_interface = browser->GetActiveTabInterface();
+  tab_showing_modal_ = browser->GetActiveTabInterface();
   // Note that this call to `CreateShowDialogAndBlockTabInteraction` is
   // necessarily preceded by a call to `CanShowModalUI`. See
   // `GlicFreController::CanShowFreDialog`.
   // TODO(crbug.com/393400004): This returned widget should be configured to
   // use a synchronous close.
-  fre_widget_ =
-      tab_interface->GetTabFeatures()
-          ->tab_dialog_manager()
-          ->CreateShowDialogAndBlockTabInteraction(fre_view_.release());
+  fre_widget_ = tab_showing_modal_->GetTabFeatures()
+                    ->tab_dialog_manager()
+                    ->CreateShowDialogAndBlockTabInteraction(
+                        fre_view_.release(), /*close_on_navigation=*/false);
   GetWebContents()->Focus();
-  tab_showing_modal_ = tab_interface;
   will_detach_subscription_ = tab_showing_modal_->RegisterWillDetach(
       base::BindRepeating(&GlicFreController::OnTabShowingModalWillDetach,
                           base::Unretained(this)));
diff --git a/chrome/browser/glic/fre/glic_fre_controller.h b/chrome/browser/glic/fre/glic_fre_controller.h
index 7b53161..8299613 100644
--- a/chrome/browser/glic/fre/glic_fre_controller.h
+++ b/chrome/browser/glic/fre/glic_fre_controller.h
@@ -66,6 +66,10 @@
   // showing the dialog.
   bool CanShowFreDialog(Browser* browser);
 
+  // Open the new tab page in the browser and show the FRE in that tab if
+  // possible.
+  void OpenFreDialogInNewTab(BrowserWindowInterface* bwi);
+
   // Shows the FRE dialog. This should only be called if `ShouldShowFreDialog`
   // and `CanShowFreDialog` are both satisfied.
   void ShowFreDialog(Browser* browser);
diff --git a/chrome/browser/glic/glic_enabling.cc b/chrome/browser/glic/glic_enabling.cc
index 89db3f6..7805564 100644
--- a/chrome/browser/glic/glic_enabling.cc
+++ b/chrome/browser/glic/glic_enabling.cc
@@ -23,15 +23,6 @@
 
 namespace glic {
 
-namespace {
-
-bool HasConsentedForProfile(Profile* profile) {
-  return profile->GetPrefs()->GetInteger(prefs::kGlicCompletedFre) ==
-         static_cast<int>(prefs::FreStatus::kCompleted);
-}
-
-}  // namespace
-
 GlicEnabling::ProfileEnablement GlicEnabling::EnablementForProfile(
     Profile* profile) {
   ProfileEnablement result;
@@ -119,6 +110,11 @@
   return EnablementForProfile(profile).IsEnabled();
 }
 
+bool GlicEnabling::HasConsentedForProfile(Profile* profile) {
+  return profile->GetPrefs()->GetInteger(prefs::kGlicCompletedFre) ==
+         static_cast<int>(prefs::FreStatus::kCompleted);
+}
+
 bool GlicEnabling::IsEnabledAndConsentForProfile(Profile* profile) {
   return EnablementForProfile(profile).IsEnabledAndConsented();
 }
diff --git a/chrome/browser/glic/glic_enabling.h b/chrome/browser/glic/glic_enabling.h
index 0cfbf05..7c6482bfa 100644
--- a/chrome/browser/glic/glic_enabling.h
+++ b/chrome/browser/glic/glic_enabling.h
@@ -61,6 +61,9 @@
   // Code inside should use instance method IsAllowed() instead.
   static bool IsEnabledForProfile(Profile* profile);
 
+  // Returns true if the profile has completed the FRE.
+  static bool HasConsentedForProfile(Profile* profile);
+
   // Returns true if the given profile has Glic enabled and has completed the
   // FRE. True implies that IsEnabledByFlags(), IsProfileEligible(profile), and
   // IsEnabledForProfile(profile) are also true. This value can change at
diff --git a/chrome/browser/glic/glic_keyed_service.cc b/chrome/browser/glic/glic_keyed_service.cc
index 7f3f6b60..34afb46 100644
--- a/chrome/browser/glic/glic_keyed_service.cc
+++ b/chrome/browser/glic/glic_keyed_service.cc
@@ -154,6 +154,16 @@
   window_controller_->Toggle(bwi, prevent_close, source);
 }
 
+void GlicKeyedService::OpenFreDialogInNewTab(BrowserWindowInterface* bwi) {
+  // Glic may be disabled for certain user profiles (the user is browsing in
+  // incognito or guest mode, policy, etc). In those cases, the entry points to
+  // this method should already have been removed.
+  CHECK(GlicEnabling::IsEnabledForProfile(profile_));
+
+  glic_profile_manager_->SetActiveGlic(this);
+  window_controller_->fre_controller()->OpenFreDialogInNewTab(bwi);
+}
+
 void GlicKeyedService::CloseUI() {
   window_controller_->Shutdown();
   host().Shutdown();
diff --git a/chrome/browser/glic/glic_keyed_service.h b/chrome/browser/glic/glic_keyed_service.h
index 3431c01..7b5125d 100644
--- a/chrome/browser/glic/glic_keyed_service.h
+++ b/chrome/browser/glic/glic_keyed_service.h
@@ -74,6 +74,8 @@
                 bool prevent_close,
                 mojom::InvocationSource source);
 
+  void OpenFreDialogInNewTab(BrowserWindowInterface* bwi);
+
   // Forcibly close the UI. This is similar to Shutdown in that it causes the
   // window controller to shutdown (and clear cached state), but unlike
   // Shutdown, it doesn't unregister as the "active glic" with the profile
diff --git a/chrome/browser/glic/glic_profile_manager.cc b/chrome/browser/glic/glic_profile_manager.cc
index 0eb12b1..bdddeb8 100644
--- a/chrome/browser/glic/glic_profile_manager.cc
+++ b/chrome/browser/glic/glic_profile_manager.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/profiles/profile_picker.h"
+#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_switches.h"
 #include "content/public/browser/network_service_instance.h"
@@ -203,17 +204,23 @@
 }
 
 void GlicProfileManager::DidSelectProfile(Profile* profile) {
-  // TODO(crbug.com/399727295) Remove once the profile picker calls this with
-  // fully initialized profiles.
   if (!GlicEnabling::IsEnabledForProfile(profile)) {
     return;
   }
-  // Toggle glic but prevent close if it is already open for the selected
-  // profile.
+
   GlicKeyedService* service =
       GlicKeyedServiceFactory::GetGlicKeyedService(profile);
-  service->ToggleUI(nullptr, /*prevent_close=*/true,
-                    mojom::InvocationSource::kProfilePicker);
+
+  if (!GlicEnabling::HasConsentedForProfile(profile)) {
+    // Open a browser and show the FRE in a new tab.
+    chrome::ScopedTabbedBrowserDisplayer displayer(profile);
+    service->OpenFreDialogInNewTab(displayer.browser());
+  } else {
+    // Toggle glic but prevent close if it is already open for the selected
+    // profile.
+    service->ToggleUI(nullptr, /*prevent_close=*/true,
+                      mojom::InvocationSource::kProfilePicker);
+  }
 }
 
 void GlicProfileManager::AddObserver(Observer* observer) {
diff --git a/chrome/browser/glic/widget/glic_window_controller.h b/chrome/browser/glic/widget/glic_window_controller.h
index ad9d3a2d..acc5306 100644
--- a/chrome/browser/glic/widget/glic_window_controller.h
+++ b/chrome/browser/glic/widget/glic_window_controller.h
@@ -74,7 +74,7 @@
 
   // Show, summon, or activate the panel if needed, or close it if it's already
   // active and prevent_close is false.
-  virtual void Toggle(BrowserWindowInterface* browser,
+  virtual void Toggle(BrowserWindowInterface* bwi,
                       bool prevent_close,
                       mojom::InvocationSource source) = 0;
 
diff --git a/chrome/browser/google/google_brand_code_map_chromeos.cc b/chrome/browser/google/google_brand_code_map_chromeos.cc
index 7259d9b..de6ff148 100644
--- a/chrome/browser/google/google_brand_code_map_chromeos.cc
+++ b/chrome/browser/google/google_brand_code_map_chromeos.cc
@@ -185,6 +185,7 @@
                      {"FCPG", {"WITB", "FOXJ", "YJQZ"}},
                      {"FCVS", {"HOBX", "YMDN", "GKTP"}},
                      {"FENM", {"NTNB", "RIJA", "WEHG"}},
+                     {"FHOM", {"RFZM", "TFXJ", "ENQL"}},
                      {"FHYR", {"YKUD", "XTKX", "QFMD"}},
                      {"FIGU", {"VMWP", "SBFY", "IYUS"}},
                      {"FNVY", {"DLEJ", "DCNV", "XALG"}},
@@ -463,6 +464,7 @@
                      {"MZVS", {"VUZM", "RIDT", "URTS"}},
                      {"NAMM", {"BFSS", "BKVK", "EBDV"}},
                      {"NBQS", {"KMJF", "MFWA", "UWRX"}},
+                     {"NDQE", {"XNUV", "DTTJ", "RAYN"}},
                      {"NFCO", {"VBYL", "IUIS", "KMVC"}},
                      {"NGVJ", {"GVZG", "GJWP", "CFNU"}},
                      {"NHYA", {"JUYB", "XYFL", "XRQH"}},
@@ -536,7 +538,9 @@
                      {"QLWW", {"LNZB", "JTVW", "XVCX"}},
                      {"QNDA", {"VFMY", "KTBL", "UOJY"}},
                      {"QOAX", {"ITAT", "RSMG", "IFBZ"}},
+                     {"QPEQ", {"EHWX", "ROSZ", "MPXK"}},
                      {"QQFU", {"ZUKV", "QBAU", "SIID"}},
+                     {"QQKF", {"PRKZ", "BFPF", "WSQL"}},
                      {"QRII", {"NHZV", "AUJW", "NSZF"}},
                      {"QRQB", {"IXPL", "NBMV", "KVTR"}},
                      {"QSAX", {"IGTA", "AMBN", "ASDW"}},
@@ -639,6 +643,8 @@
                      {"TZNR", {"MHIP", "YJBK", "VDZV"}},
                      {"TZXN", {"VQDQ", "NKBA", "NNUN"}},
                      {"UBKE", {"CPTX", "EGAC", "MRXT"}},
+                     {"UBRR", {"UBYQ", "SDFZ", "DPKG"}},
+                     {"UCAB", {"LLAZ", "RLYU", "CADQ"}},
                      {"UCLD", {"XHWD", "CBQT", "UUFI"}},
                      {"UDAJ", {"XNWC", "GPQO", "JTJZ"}},
                      {"UERT", {"XSDZ", "GOMR", "THXS"}},
@@ -707,6 +713,7 @@
                      {"WPFB", {"JOSR", "MHKH", "OHJH"}},
                      {"WPKT", {"NAPI", "TQRX", "DBBS"}},
                      {"WTXQ", {"NPCP", "DIOS", "DSTX"}},
+                     {"WUSA", {"BWCF", "RWUY", "BFXG"}},
                      {"WVRW", {"GJGN", "QQFA", "AGVP"}},
                      {"WWTI", {"GZHX", "JHGD", "ZDGL"}},
                      {"WXZG", {"IUGR", "JOEE", "PTHY"}},
diff --git a/chrome/browser/headless/headless_mode_protocol_browsertest.cc b/chrome/browser/headless/headless_mode_protocol_browsertest.cc
index 2ccaec9..65fed52 100644
--- a/chrome/browser/headless/headless_mode_protocol_browsertest.cc
+++ b/chrome/browser/headless/headless_mode_protocol_browsertest.cc
@@ -316,10 +316,13 @@
                             "sanity/fullscreen-restore-window.js")
 #endif  // !BUILDFLAG(IS_MAC)
 
+// This currently fails on Mac, see https://crbug.com/416088625
+#if !BUILDFLAG(IS_MAC)
 HEADLESS_MODE_PROTOCOL_TEST_WITH_COMMAND_LINE_EXTRAS(
     MaximizedWindowSize,
     "sanity/maximized-window-size.js",
     "--screen-info={1600x1200}")
+#endif  // !BUILDFLAG(IS_MAC)
 
 // This currently fails on Mac, see https://crbug.com/1500046
 #if !BUILDFLAG(IS_MAC)
@@ -348,8 +351,6 @@
     "--ozone-override-screen-size=1234,5678")
 #endif
 
-// --screen-info switch is only supported on Linux and Windows at this time.
-#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
 // This currently results in an unexpected screen orientation type,
 // see http://crbug.com/398150465.
 HEADLESS_MODE_PROTOCOL_TEST_WITH_COMMAND_LINE_EXTRAS(
@@ -357,8 +358,15 @@
     "sanity/multiple-screen-details.js",
     "--screen-info={label=#1}{600x800 label='#2'}")
 
+// TODO(crbug.com/40283476): MoveWindowBetweenScreens is failing on Mac
+#if !BUILDFLAG(IS_MAC)
+#define MAYBE_MoveWindowBetweenScreens MoveWindowBetweenScreens
+#else
+#define MAYBE_MoveWindowBetweenScreens DISABLED_MoveWindowBetweenScreens
+#endif
+
 HEADLESS_MODE_PROTOCOL_TEST_WITH_COMMAND_LINE_EXTRAS(
-    MoveWindowBetweenScreens,
+    MAYBE_MoveWindowBetweenScreens,
     "sanity/move-window-between-screens.js",
     "--screen-info={label='#1'}{label='#2'}{0,600 label='#3'}{label='#4'}")
 
@@ -367,11 +375,16 @@
     "sanity/window-open-on-secondary-screen.js",
     "--screen-info={label='#1'}{label='#2'} --disable-popup-blocking")
 
+// TODO(crbug.com/40283476): CreateTargetSecondaryScreen is failing on Mac
+#if !BUILDFLAG(IS_MAC)
+#define MAYBE_CreateTargetSecondaryScreen CreateTargetSecondaryScreen
+#else
+#define MAYBE_CreateTargetSecondaryScreen DISABLED_CreateTargetSecondaryScreen
+#endif
+
 HEADLESS_MODE_PROTOCOL_TEST_WITH_COMMAND_LINE_EXTRAS(
-    CreateTargetSecondaryScreen,
+    MAYBE_CreateTargetSecondaryScreen,
     "sanity/create-target-secondary-screen.js",
     "--screen-info={label='#1'}{label='#2'}")
 
-#endif
-
 }  // namespace headless
diff --git a/chrome/browser/k_anonymity_service/k_anonymity_service_urls.h b/chrome/browser/k_anonymity_service/k_anonymity_service_urls.h
index 87f9062..8f254ca 100644
--- a/chrome/browser/k_anonymity_service/k_anonymity_service_urls.h
+++ b/chrome/browser/k_anonymity_service/k_anonymity_service_urls.h
@@ -5,16 +5,16 @@
 #ifndef CHROME_BROWSER_K_ANONYMITY_SERVICE_K_ANONYMITY_SERVICE_URLS_H_
 #define CHROME_BROWSER_K_ANONYMITY_SERVICE_K_ANONYMITY_SERVICE_URLS_H_
 
-constexpr char kGenNonUniqueUserIdPath[] = "/v1/generateShortIdentifier";
-constexpr char kFetchKeysPathFmt[] =
+inline constexpr char kGenNonUniqueUserIdPath[] = "/v1/generateShortIdentifier";
+inline constexpr char kFetchKeysPathFmt[] =
     "/v1/%d/fetchKeys?key=%s";  // Put the short ID in the path.
-constexpr char kIssueTrustTokenPathFmt[] =
+inline constexpr char kIssueTrustTokenPathFmt[] =
     "/v1/%d/issueTrustToken";  // Put the short ID in the path.
 
-constexpr char kJoinSetPathFmt[] = "/v1/types/%s/sets/%s:join?key=%s";
-constexpr char kJoinSetOhttpPath[] = "/v1/proxy/keys?key=";
+inline constexpr char kJoinSetPathFmt[] = "/v1/types/%s/sets/%s:join?key=%s";
+inline constexpr char kJoinSetOhttpPath[] = "/v1/proxy/keys?key=";
 
-constexpr char kQuerySetsPath[] = "/v1:query?key=";
-constexpr char kQuerySetOhttpPath[] = "/v1/proxy/keys?key=";
+inline constexpr char kQuerySetsPath[] = "/v1:query?key=";
+inline constexpr char kQuerySetOhttpPath[] = "/v1/proxy/keys?key=";
 
 #endif  // CHROME_BROWSER_K_ANONYMITY_SERVICE_K_ANONYMITY_SERVICE_URLS_H_
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_cast_feature.h b/chrome/browser/media/router/discovery/access_code/access_code_cast_feature.h
index bd0de5d..c1311d3 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_cast_feature.h
+++ b/chrome/browser/media/router/discovery/access_code/access_code_cast_feature.h
@@ -21,12 +21,12 @@
 namespace prefs {
 // Pref name that allows the AccessCode/QR code scanning dialog button to be
 // shown.
-constexpr char kAccessCodeCastEnabled[] =
+inline constexpr char kAccessCodeCastEnabled[] =
     "media_router.access_code_cast.enabled";
 
 // Pref name for the pref that determines how long a scanned receiver remains in
 // the receiver list. Duration is measured in seconds.
-constexpr char kAccessCodeCastDeviceDuration[] =
+inline constexpr char kAccessCodeCastDeviceDuration[] =
     "media_router.access_code_cast.device_duration";
 
 // Pref that keeps track of cast devices added on a user's profile. It is
@@ -35,13 +35,13 @@
 // as a base::Value::Dict.
 // Whenever a cast device is discovered via access code, a new entry will be
 // added to this dictionary (or updated if the MediaSink::Id already exists).
-constexpr char kAccessCodeCastDevices[] =
+inline constexpr char kAccessCodeCastDevices[] =
     "media_router.access_code_cast.devices";
 
 // Pref that keeps track of when a cast device is added. It is be registered
 // as a dictionary pref with each key being a MediaSink::Id and value being a
 // base::Time.
-constexpr char kAccessCodeCastDeviceAdditionTime[] =
+inline constexpr char kAccessCodeCastDeviceAdditionTime[] =
     "media_router.access_code_cast.addition_time";
 }  // namespace prefs
 
diff --git a/chrome/browser/media/router/discovery/access_code/access_code_test_util.h b/chrome/browser/media/router/discovery/access_code/access_code_test_util.h
index c2eb2f71..43aca48 100644
--- a/chrome/browser/media/router/discovery/access_code/access_code_test_util.h
+++ b/chrome/browser/media/router/discovery/access_code/access_code_test_util.h
@@ -14,11 +14,12 @@
 
 namespace media_router {
 
-const char kExpectedDisplayName[] = "test_device";
-const char kExpectedSinkId[] = "1234";
-const char kExpectedPort[] = "666";
-const char kExpectedIpV4[] = "192.0.2.146";
-const char kExpectedIpV6[] = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+inline constexpr char kExpectedDisplayName[] = "test_device";
+inline constexpr char kExpectedSinkId[] = "1234";
+inline constexpr char kExpectedPort[] = "666";
+inline constexpr char kExpectedIpV4[] = "192.0.2.146";
+inline constexpr char kExpectedIpV6[] =
+    "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
 
 using DiscoveryDevice = chrome_browser_media::proto::DiscoveryDevice;
 using NetworkInfo = chrome_browser_media::proto::NetworkInfo;
diff --git a/chrome/browser/media/router/providers/cast/cast_internal_message_util.h b/chrome/browser/media/router/providers/cast/cast_internal_message_util.h
index 38020c8f..beeb27a 100644
--- a/chrome/browser/media/router/providers/cast/cast_internal_message_util.h
+++ b/chrome/browser/media/router/providers/cast/cast_internal_message_util.h
@@ -20,12 +20,12 @@
 
 // Values in the "supportedMediaCommands" list in media status messages
 // sent to the Cast sender SDK.
-constexpr char kMediaCommandPause[] = "pause";
-constexpr char kMediaCommandSeek[] = "seek";
-constexpr char kMediaCommandStreamVolume[] = "stream_volume";
-constexpr char kMediaCommandStreamMute[] = "stream_mute";
-constexpr char kMediaCommandQueueNext[] = "queue_next";
-constexpr char kMediaCommandQueuePrev[] = "queue_prev";
+inline constexpr char kMediaCommandPause[] = "pause";
+inline constexpr char kMediaCommandSeek[] = "seek";
+inline constexpr char kMediaCommandStreamVolume[] = "stream_volume";
+inline constexpr char kMediaCommandStreamMute[] = "stream_mute";
+inline constexpr char kMediaCommandQueueNext[] = "queue_next";
+inline constexpr char kMediaCommandQueuePrev[] = "queue_prev";
 
 // Values in the "supportedMediaCommands" bit array in media status messages
 // received from Cast receivers. They are converted to string values by
diff --git a/chrome/browser/media_galleries/media_galleries_preferences.h b/chrome/browser/media_galleries/media_galleries_preferences.h
index 6c5bc5a..e517ca5 100644
--- a/chrome/browser/media_galleries/media_galleries_preferences.h
+++ b/chrome/browser/media_galleries/media_galleries_preferences.h
@@ -35,8 +35,9 @@
 typedef uint64_t MediaGalleryPrefId;
 const MediaGalleryPrefId kInvalidMediaGalleryPrefId = 0;
 
-const char kMediaGalleriesPrefsVersionKey[] = "preferencesVersion";
-const char kMediaGalleriesDefaultGalleryTypeKey[] = "defaultGalleryType";
+inline constexpr char kMediaGalleriesPrefsVersionKey[] = "preferencesVersion";
+inline constexpr char kMediaGalleriesDefaultGalleryTypeKey[] =
+    "defaultGalleryType";
 
 struct MediaGalleryPermission {
   MediaGalleryPrefId pref_id;
diff --git a/chrome/browser/metrics/chrome_metrics_service_accessor.h b/chrome/browser/metrics/chrome_metrics_service_accessor.h
index 3827c4d4..8dc956a2 100644
--- a/chrome/browser/metrics/chrome_metrics_service_accessor.h
+++ b/chrome/browser/metrics/chrome_metrics_service_accessor.h
@@ -78,6 +78,10 @@
 class CrOSPreConsentMetricsManagerTest;
 }  // namespace metrics
 
+namespace optimization_guide {
+class ChromeOnDeviceModelServiceController;
+}  // namespace optimization_guide
+
 namespace safe_browsing {
 class ChromeSafeBrowsingUIManagerDelegate;
 class DownloadUrlSBClient;
@@ -192,6 +196,7 @@
   friend class glic::GlicSyntheticTrialManager;
 #endif
   friend class OptimizationGuideKeyedService;
+  friend class optimization_guide::ChromeOnDeviceModelServiceController;
   friend class WebUITabStripFieldTrial;
   friend class feed::FeedServiceDelegateImpl;
   friend class FirstRunService;
diff --git a/chrome/browser/metrics/process_memory_metrics_emitter.cc b/chrome/browser/metrics/process_memory_metrics_emitter.cc
index 1cfb5f7..c38b64d3 100644
--- a/chrome/browser/metrics/process_memory_metrics_emitter.cc
+++ b/chrome/browser/metrics/process_memory_metrics_emitter.cc
@@ -1740,6 +1740,7 @@
     base::flat_set<const performance_manager::PageNode*> page_nodes =
         performance_manager::GraphOperations::GetAssociatedPageNodes(
             process_node);
+    const base::TimeTicks now = base::TimeTicks::Now();
     for (const performance_manager::PageNode* page_node : page_nodes) {
       if (page_node->GetUkmSourceID() == ukm::kInvalidSourceId)
         continue;
@@ -1759,7 +1760,7 @@
       page_info.hosts_main_frame = HostsMainFrame(process_node, page_node);
       page_info.is_visible = page_node->IsVisible();
       page_info.time_since_last_visibility_change =
-          page_node->GetTimeSinceLastVisibilityChange();
+          now - page_node->GetLastVisibilityChangeTime();
       page_info.time_since_last_navigation =
           page_node->GetTimeSinceLastNavigation();
     }
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc b/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc
index 0bcac26..f84c791d 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor_preconnect_client_browsertest.cc
@@ -112,6 +112,9 @@
         url::Origin::Create(https_server_->base_url())) {
       return;
     }
+    if (observer.is_valid()) {
+      observer_.Bind(std::move(observer));
+    }
     EXPECT_TRUE(success);
     preresolve_done_count_++;
     if (run_loop_)
@@ -129,6 +132,7 @@
  protected:
   int preresolve_done_count_ = 0;
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
+  mojo::Remote<network::mojom::ReconnectEventObserver> observer_;
 
  private:
   base::test::ScopedFeatureList feature_list_;
@@ -336,9 +340,14 @@
 
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
   // Now there should be an onload preconnect as well as a navigation
-  // preconnect.
-  WaitForPreresolveCount(3);
-  EXPECT_EQ(3, preresolve_done_count_);
+  // preconnect. If `SearchEnginePreconnect2` is enabled, we will have one
+  // additional preresolve count because navigating to a URL will start a
+  // preconnect due to a change in web visibility (i.e. the app is foregrounded)
+  // for the first navigation.
+  int preresolve_count =
+      SearchEnginePreconnector::SearchEnginePreconnect2Enabled() ? 4 : 3;
+  WaitForPreresolveCount(preresolve_count);
+  EXPECT_EQ(preresolve_count, preresolve_done_count_);
 }
 
 class NavigationPredictorPreconnectClientLocalURLBrowserTest
diff --git a/chrome/browser/navigation_predictor/search_engine_preconnector_browsertest.cc b/chrome/browser/navigation_predictor/search_engine_preconnector_browsertest.cc
index 30f9345..abee4af 100644
--- a/chrome/browser/navigation_predictor/search_engine_preconnector_browsertest.cc
+++ b/chrome/browser/navigation_predictor/search_engine_preconnector_browsertest.cc
@@ -524,8 +524,10 @@
 
 IN_PROC_BROWSER_TEST_F(SearchEnginePreconnectorDesktopAutoStartBrowserTest,
                        AutoStartDesktop) {
+  int preresolve_count =
+      SearchEnginePreconnector::SearchEnginePreconnect2Enabled() ? 1 : 2;
   // Verifies that the default search is preconnected.
-  WaitForPreresolveCountForURL(GURL(kGoogleSearch), 2);
+  WaitForPreresolveCountForURL(GURL(kGoogleSearch), preresolve_count);
 }
 
 class SearchEnginePreconnectorEnabledOnlyBrowserTest
diff --git a/chrome/browser/nearby_sharing/instantmessaging/constants.h b/chrome/browser/nearby_sharing/instantmessaging/constants.h
index 1261e414..38a4fe1 100644
--- a/chrome/browser/nearby_sharing/instantmessaging/constants.h
+++ b/chrome/browser/nearby_sharing/instantmessaging/constants.h
@@ -5,13 +5,13 @@
 #ifndef CHROME_BROWSER_NEARBY_SHARING_INSTANTMESSAGING_CONSTANTS_H_
 #define CHROME_BROWSER_NEARBY_SHARING_INSTANTMESSAGING_CONSTANTS_H_
 
-const char kInstantMessagingReceiveMessageAPI[] =
+inline constexpr char kInstantMessagingReceiveMessageAPI[] =
     "https://instantmessaging-pa.googleapis.com/v1/messages:receiveExpress";
 
-const char kInstantMessagingSendMessageAPI[] =
+inline constexpr char kInstantMessagingSendMessageAPI[] =
     "https://instantmessaging-pa.googleapis.com/v1/message:sendExpress";
 
 // Template for optional OAuth2 authorization HTTP header.
-const char kAuthorizationHeaderFormat[] = "Authorization: Bearer %s";
+inline constexpr char kAuthorizationHeaderFormat[] = "Authorization: Bearer %s";
 
 #endif  // CHROME_BROWSER_NEARBY_SHARING_INSTANTMESSAGING_CONSTANTS_H_
diff --git a/chrome/browser/nearby_sharing/wifi_network_configuration/fake_wifi_network_configuration_handler.h b/chrome/browser/nearby_sharing/wifi_network_configuration/fake_wifi_network_configuration_handler.h
index 13a2d9a6..8f83f56 100644
--- a/chrome/browser/nearby_sharing/wifi_network_configuration/fake_wifi_network_configuration_handler.h
+++ b/chrome/browser/nearby_sharing/wifi_network_configuration/fake_wifi_network_configuration_handler.h
@@ -11,7 +11,7 @@
 namespace {
 
 const uint64_t kDefaultId = 0;
-const char kDefaultSsid[] = "not_set";
+inline constexpr char kDefaultSsid[] = "not_set";
 const WifiCredentialsAttachment::SecurityType kDefaultSecurityType =
     sharing::mojom::WifiCredentialsMetadata::SecurityType::kWpaPsk;
 
@@ -51,4 +51,4 @@
   std::string error_message_ = "not set";
 };
 
-#endif  //  CHROME_BROWSER_NEARBY_SHARING_WIFI_NETWORK_CONFIGURATION_FAKE_WIFI_NETWORK_CONFIGURATION_HANDLER_H_
+#endif  // CHROME_BROWSER_NEARBY_SHARING_WIFI_NETWORK_CONFIGURATION_FAKE_WIFI_NETWORK_CONFIGURATION_HANDLER_H_
diff --git a/chrome/browser/new_tab_page/modules/new_tab_page_modules_interactive_uitest.cc b/chrome/browser/new_tab_page/modules/new_tab_page_modules_interactive_uitest.cc
index bd2d936..625f4a0 100644
--- a/chrome/browser/new_tab_page/modules/new_tab_page_modules_interactive_uitest.cc
+++ b/chrome/browser/new_tab_page/modules/new_tab_page_modules_interactive_uitest.cc
@@ -26,9 +26,8 @@
 DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kNewTabPageElementId);
 
 using DeepQuery = WebContentsInteractionTestUtil::DeepQuery;
-const DeepQuery kModulesV2Container = {"ntp-app", "ntp-modules-v2",
-                                       "#container"};
-const DeepQuery kModulesV2Wrapper = {"ntp-app", "ntp-modules-v2", "#container",
+const DeepQuery kModulesV2Container = {"ntp-app", "ntp-modules", "#container"};
+const DeepQuery kModulesV2Wrapper = {"ntp-app", "ntp-modules", "#container",
                                      "ntp-module-wrapper"};
 const DeepQuery kMicrosoftAuthIframe = {"ntp-app", "#microsoftAuth"};
 
@@ -53,18 +52,18 @@
     {{ntp_features::kNtpMostRelevantTabResumptionModule,
       {{ntp_features::kNtpMostRelevantTabResumptionModuleDataParam,
         "Fake Data"}}}},
-    {"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+    {"ntp-app", "ntp-modules", "ntp-module-wrapper",
      "ntp-most-relevant-tab-resumption"},
-    {"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+    {"ntp-app", "ntp-modules", "ntp-module-wrapper",
      "ntp-most-relevant-tab-resumption", "ntp-module-header-v2", "#menuButton"},
-    {"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+    {"ntp-app", "ntp-modules", "ntp-module-wrapper",
      "ntp-most-relevant-tab-resumption", "ntp-module-header-v2",
      "cr-action-menu", "dialog"},
-    {"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+    {"ntp-app", "ntp-modules", "ntp-module-wrapper",
      "ntp-most-relevant-tab-resumption", "ntp-module-header-v2", "#dismiss"},
-    {"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+    {"ntp-app", "ntp-modules", "ntp-module-wrapper",
      "ntp-most-relevant-tab-resumption", "ntp-module-header-v2", "#disable"},
-    {{{"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+    {{{"ntp-app", "ntp-modules", "ntp-module-wrapper",
        "ntp-most-relevant-tab-resumption", "#urlVisits", "a"},
       "https://www.google.com"}},
 };
@@ -73,29 +72,29 @@
     ntp_features::kNtpCalendarModule,
     {{ntp_features::kNtpCalendarModule,
       {{ntp_features::kNtpCalendarModuleDataParam, "fake"}}}},
-    {"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+    {"ntp-app", "ntp-modules", "ntp-module-wrapper",
      "ntp-google-calendar-module"},
-    {"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+    {"ntp-app", "ntp-modules", "ntp-module-wrapper",
      "ntp-google-calendar-module", "ntp-module-header-v2", "#menuButton"},
-    {"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+    {"ntp-app", "ntp-modules", "ntp-module-wrapper",
      "ntp-google-calendar-module", "ntp-module-header-v2", "cr-action-menu",
      "dialog"},
-    {"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+    {"ntp-app", "ntp-modules", "ntp-module-wrapper",
      "ntp-google-calendar-module", "ntp-module-header-v2", "#dismiss"},
-    {"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+    {"ntp-app", "ntp-modules", "ntp-module-wrapper",
      "ntp-google-calendar-module", "ntp-module-header-v2", "#disable"},
-    {{{"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+    {{{"ntp-app", "ntp-modules", "ntp-module-wrapper",
        "ntp-google-calendar-module", "ntp-calendar", "ntp-calendar-event",
        "#header"},
       "https://foo.com/0"},
-     {{"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+     {{"ntp-app", "ntp-modules", "ntp-module-wrapper",
        "ntp-google-calendar-module", "ntp-calendar", "#seeMore", "a"},
       "https://calendar.google.com"},
-     {{"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+     {{"ntp-app", "ntp-modules", "ntp-module-wrapper",
        "ntp-google-calendar-module", "ntp-calendar", "ntp-calendar-event",
        "cr-chip"},
       "https://foo.com/attachment0"},
-     {{"ntp-app", "ntp-modules-v2", "ntp-module-wrapper",
+     {{"ntp-app", "ntp-modules", "ntp-module-wrapper",
        "ntp-google-calendar-module", "ntp-calendar", "ntp-calendar-event",
        "cr-button"},
       "https://foo.com/conference0"}},
@@ -237,8 +236,11 @@
                          NewTabPageModulesInteractiveUiTest,
                          ::testing::ValuesIn(kAllModules));
 
+// TODO(crbug.com/416206296): Re-enable once we have a workaround for querying
+// the `module_wrapper.html` slotted element.
+// @see chrome/browser/resources/new_tab_page/modules/module_wrapper.html
 IN_PROC_BROWSER_TEST_P(NewTabPageModulesInteractiveUiTest,
-                       ClickingHideButtonDismissesModule) {
+                       DISABLED_ClickingHideButtonDismissesModule) {
   RunTestSequence(
       // 1. Wait for new tab page to load.
       LoadNewTabPage(),
@@ -269,8 +271,11 @@
       WaitForElementChildElementCount(kModulesV2Container, 0));
 }
 
+// TODO(crbug.com/416206296): Re-enable once we have a workaround for querying
+// the `module_wrapper.html` slotted element.
+// @see chrome/browser/resources/new_tab_page/modules/module_wrapper.html
 IN_PROC_BROWSER_TEST_P(NewTabPageModulesInteractiveUiTest,
-                       ClickingDisableButtonDisablesModule) {
+                       DISABLED_ClickingDisableButtonDisablesModule) {
   const auto& module_details = ModuleDetails();
   RunTestSequence(
       // 1. Wait for new tab page to load.
@@ -334,8 +339,11 @@
                          ::testing::ValuesIn(GetAllModuleLinks(kAllModules)));
 #endif
 
+// TODO(crbug.com/416206296): Re-enable once we have a workaround for querying
+// the `module_wrapper.html` slotted element.
+// @see chrome/browser/resources/new_tab_page/modules/module_wrapper.html
 IN_PROC_BROWSER_TEST_P(NewTabPageModulesInteractiveLinkUiTest,
-                       ClickingEntryNavigatesToCorrectPage) {
+                       DISABLED_ClickingEntryNavigatesToCorrectPage) {
   RunTestSequence(
       // 1. Wait for new tab page to load.
       LoadNewTabPage(),
diff --git a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsInitializerTest.java b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsInitializerTest.java
index 71fe3d8..a40802b 100644
--- a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsInitializerTest.java
+++ b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelsInitializerTest.java
@@ -321,21 +321,6 @@
 
     @Test
     @Feature({"Browser", "Notifications"})
-    public void testEnsureInitialized_priceDropChannel() {
-        mChannelsInitializer.ensureInitialized(ChromeChannelDefinitions.ChannelId.PRICE_DROP);
-
-        assertThat(getChannelsIgnoringDefault(), hasSize(1));
-        NotificationChannel channel = getChannelsIgnoringDefault().get(0);
-        assertThat(channel.getId(), is(ChromeChannelDefinitions.ChannelId.PRICE_DROP));
-        assertThat(
-                channel.getName().toString(),
-                is(mContext.getString(R.string.notification_category_price_drop)));
-        assertThat(channel.getImportance(), is(NotificationManager.IMPORTANCE_DEFAULT));
-        assertThat(channel.getGroup(), is(ChromeChannelDefinitions.ChannelGroupId.GENERAL));
-    }
-
-    @Test
-    @Feature({"Browser", "Notifications"})
     public void testEnsureInitialized_priceDropDefaultChannel() {
         mChannelsInitializer.ensureInitialized(
                 ChromeChannelDefinitions.ChannelId.PRICE_DROP_DEFAULT);
diff --git a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/ChromeChannelDefinitions.java b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/ChromeChannelDefinitions.java
index 20a3ffe..e4fa4608 100644
--- a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/ChromeChannelDefinitions.java
+++ b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/channels/ChromeChannelDefinitions.java
@@ -4,10 +4,7 @@
 
 package org.chromium.chrome.browser.notifications.channels;
 
-import android.app.NotificationChannel;
 import android.app.NotificationManager;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
 import android.text.TextUtils;
 
 import androidx.annotation.StringDef;
@@ -15,8 +12,6 @@
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.chrome.browser.notifications.R;
-import org.chromium.components.browser_ui.notifications.NotificationManagerProxy;
-import org.chromium.components.browser_ui.notifications.NotificationManagerProxyImpl;
 import org.chromium.components.browser_ui.notifications.channels.ChannelDefinitions;
 
 import java.lang.annotation.Retention;
@@ -93,7 +88,7 @@
         ChannelId.WEBAPPS,
         ChannelId.WEBAPPS_QUIET,
         ChannelId.WEBRTC_CAM_AND_MIC,
-        ChannelId.PRICE_DROP,
+        ChannelId.PRICE_DROP, // Deprecated, use PRICE_DROP_DEFAULT.
         ChannelId.PRICE_DROP_DEFAULT,
         ChannelId.SECURITY_KEY,
         ChannelId.BLUETOOTH,
@@ -319,35 +314,11 @@
             // Not added to startup channels because we want this channel to be created on the first
             // use.
             map.put(
-                    ChannelId.PRICE_DROP,
-                    PredefinedChannel.create(
-                            ChannelId.PRICE_DROP,
-                            R.string.notification_category_price_drop,
-                            NotificationManager.IMPORTANCE_DEFAULT,
-                            ChannelGroupId.GENERAL));
-            // TODO(crbug.com/40244973): Make the new channel's behavior consistent with the old
-            // channel's if it's created and modified by the user. Clean this up after one or two
-            // milestones.
-            int priceDropDefaultChannelImportance = NotificationManager.IMPORTANCE_DEFAULT;
-            if (VERSION.SDK_INT >= VERSION_CODES.O) {
-                NotificationManagerProxy notificationManager =
-                        NotificationManagerProxyImpl.getInstance();
-                NotificationChannel priceDropChannel =
-                        notificationManager.getNotificationChannel(ChannelId.PRICE_DROP);
-                if (priceDropChannel != null) {
-                    startup.add(ChannelId.PRICE_DROP_DEFAULT);
-                    if (priceDropChannel.getImportance() != NotificationManager.IMPORTANCE_LOW) {
-                        priceDropDefaultChannelImportance = priceDropChannel.getImportance();
-                    }
-                    notificationManager.deleteNotificationChannel(ChannelId.PRICE_DROP);
-                }
-            }
-            map.put(
                     ChannelId.PRICE_DROP_DEFAULT,
                     PredefinedChannel.create(
                             ChannelId.PRICE_DROP_DEFAULT,
                             R.string.notification_category_price_drop,
-                            priceDropDefaultChannelImportance,
+                            NotificationManager.IMPORTANCE_DEFAULT,
                             ChannelGroupId.GENERAL));
 
             // The security key notification channel will only appear for users
diff --git a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionController.java b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionController.java
index 76d425f9..4e5d8926 100644
--- a/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionController.java
+++ b/chrome/browser/notifications/android/java/src/org/chromium/chrome/browser/notifications/permissions/NotificationPermissionController.java
@@ -11,7 +11,6 @@
 import androidx.annotation.IntDef;
 
 import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.base.BuildInfo;
 import org.chromium.base.Callback;
 import org.chromium.base.TimeUtils;
 import org.chromium.base.UnownedUserData;
@@ -184,7 +183,6 @@
      */
     public boolean requestPermissionIfNeeded(boolean contextual) {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
-                || !BuildInfo.targetsAtLeastT()
                 || ApiCompatibilityUtils.isDemoUser()) {
             return false;
         }
@@ -235,7 +233,7 @@
     int shouldRequestPermission() {
         // Notifications only require permission starting at Android T. And apps targeting < T can't
         // request permission as the OS prompts the user automatically.
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || !BuildInfo.targetsAtLeastT()) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
             return PermissionRequestMode.DO_NOT_REQUEST;
         }
 
diff --git a/chrome/browser/notifications/notification_permission_context.cc b/chrome/browser/notifications/notification_permission_context.cc
index ee6fbf5..216a00e 100644
--- a/chrome/browser/notifications/notification_permission_context.cc
+++ b/chrome/browser/notifications/notification_permission_context.cc
@@ -184,7 +184,8 @@
       ShortcutHelper::DoesOriginContainAnyInstalledTrustedWebActivity(
           request_data->requesting_origin);
   bool contains_installed_webapp = contains_twa || contains_webapk;
-  if (base::android::BuildInfo::GetInstance()->is_at_least_t() &&
+  if (base::android::BuildInfo::GetInstance()->sdk_int() >=
+          base::android::SDK_VERSION_T &&
       contains_installed_webapp) {
     // WebAPKs match URLs using a scope URL which may contain a path. An origin
     // has no path and would not fall within such a scope. So to find a matching
diff --git a/chrome/browser/notifications/scheduler/internal/scheduler_config.h b/chrome/browser/notifications/scheduler/internal/scheduler_config.h
index 4dc38a6..f7b9cb3 100644
--- a/chrome/browser/notifications/scheduler/internal/scheduler_config.h
+++ b/chrome/browser/notifications/scheduler/internal/scheduler_config.h
@@ -12,33 +12,37 @@
 namespace notifications {
 
 // Configure the maxmium number of notifications daily shown for all types.
-constexpr char kMaxDailyShownAllTypeConfig[] = "max_daily_shown_all_type";
+inline constexpr char kMaxDailyShownAllTypeConfig[] =
+    "max_daily_shown_all_type";
 
 // Configure the maxmium number of notifications daily shown per type.
-constexpr char kMaxDailyShownPerTypeConfig[] = "max_daily_shown_per_type";
+inline constexpr char kMaxDailyShownPerTypeConfig[] =
+    "max_daily_shown_per_type";
 
 // Configure the initial number of notifications daily shown per type.
-constexpr char kInitialDailyShownPerTypeConfig[] =
+inline constexpr char kInitialDailyShownPerTypeConfig[] =
     "initial_daily_shown_per_type";
 
 // Configure the expiration duration for notifications.
-constexpr char kNotificationExpirationConfig[] =
+inline constexpr char kNotificationExpirationConfig[] =
     "notification_expiration_in_days";
 
 // Configure the expiration duration for impressions.
-constexpr char kImpressionExpirationConfig[] = "impression_expiration_in_days";
+inline constexpr char kImpressionExpirationConfig[] =
+    "impression_expiration_in_days";
 
 // Configure the expiration duration for suppression.
-constexpr char kSuppressionDurationConfig[] = "suppression_duration_in_days";
+inline constexpr char kSuppressionDurationConfig[] =
+    "suppression_duration_in_days";
 
 // Configure the number of dismiss count.
-constexpr char kDismissCountConfig[] = "dismiss_count";
+inline constexpr char kDismissCountConfig[] = "dismiss_count";
 
 // Configure the duration of a dismiss.
-constexpr char kDismissDurationConfig[] = "dismiss_duration_in_days";
+inline constexpr char kDismissDurationConfig[] = "dismiss_duration_in_days";
 
 // Configure the duration of background task window.
-constexpr char kBackgroundTaskWindowDurationConfig[] =
+inline constexpr char kBackgroundTaskWindowDurationConfig[] =
     "background_task_window_duration_in_hours";
 
 // Configuration of notification scheduler system.
diff --git a/chrome/browser/notifications/scheduler/public/notification_scheduler_constant.h b/chrome/browser/notifications/scheduler/public/notification_scheduler_constant.h
index 7a4f97b..74984ba 100644
--- a/chrome/browser/notifications/scheduler/public/notification_scheduler_constant.h
+++ b/chrome/browser/notifications/scheduler/public/notification_scheduler_constant.h
@@ -7,9 +7,9 @@
 
 namespace notifications {
 
-constexpr char kDefaultHelpfulButtonId[] =
+inline constexpr char kDefaultHelpfulButtonId[] =
     "NOTIFICATION_SCHEDULER_DEFAULT_HELPFUL_BUTTON_ID";
-constexpr char kDefaultUnhelpfulButtonId[] =
+inline constexpr char kDefaultUnhelpfulButtonId[] =
     "NOTIFICATION_SCHEDULER_DEFAULT_UNHELPFUL_BUTTON_ID";
 
 }  // namespace notifications
diff --git a/chrome/browser/on_device_translation/component_manager.cc b/chrome/browser/on_device_translation/component_manager.cc
index 908ef53..83ab626 100644
--- a/chrome/browser/on_device_translation/component_manager.cc
+++ b/chrome/browser/on_device_translation/component_manager.cc
@@ -147,14 +147,14 @@
 
 // static
 std::set<LanguagePackKey> ComponentManager::GetInstalledLanguagePacks() {
-  std::set<LanguagePackKey> insalled_pack_keys;
+  std::set<LanguagePackKey> installed_pack_keys;
   for (const auto& it : kLanguagePackComponentConfigMap) {
     if (!GetFilePathFromGlobalPrefs(GetComponentPathPrefName(*it.second))
              .empty()) {
-      insalled_pack_keys.insert(it.first);
+      installed_pack_keys.insert(it.first);
     }
   }
-  return insalled_pack_keys;
+  return installed_pack_keys;
 }
 
 // static
diff --git a/chrome/browser/optimization_guide/model_execution/chrome_on_device_model_service_controller.cc b/chrome/browser/optimization_guide/model_execution/chrome_on_device_model_service_controller.cc
index d509291..898ee0b 100644
--- a/chrome/browser/optimization_guide/model_execution/chrome_on_device_model_service_controller.cc
+++ b/chrome/browser/optimization_guide/model_execution/chrome_on_device_model_service_controller.cc
@@ -5,7 +5,9 @@
 #include "chrome/browser/optimization_guide/model_execution/chrome_on_device_model_service_controller.h"
 
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
 #include "components/optimization_guide/core/model_execution/on_device_model_access_controller.h"
+#include "components/optimization_guide/core/model_execution/performance_class.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "content/public/browser/service_process_host.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
@@ -53,4 +55,15 @@
   g_instance = nullptr;
 }
 
+void ChromeOnDeviceModelServiceController::
+    RegisterPerformanceClassSyntheticTrial(
+        OnDeviceModelPerformanceClass perf_class) {
+  if (perf_class != OnDeviceModelPerformanceClass::kUnknown) {
+    ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
+        "SyntheticOnDeviceModelPerformanceClass",
+        SyntheticTrialGroupForPerformanceClass(perf_class),
+        variations::SyntheticTrialAnnotationMode::kCurrentLog);
+  }
+}
+
 }  // namespace optimization_guide
diff --git a/chrome/browser/optimization_guide/model_execution/chrome_on_device_model_service_controller.h b/chrome/browser/optimization_guide/model_execution/chrome_on_device_model_service_controller.h
index 211a97b..88969f8 100644
--- a/chrome/browser/optimization_guide/model_execution/chrome_on_device_model_service_controller.h
+++ b/chrome/browser/optimization_guide/model_execution/chrome_on_device_model_service_controller.h
@@ -5,9 +5,9 @@
 #ifndef CHROME_BROWSER_OPTIMIZATION_GUIDE_MODEL_EXECUTION_CHROME_ON_DEVICE_MODEL_SERVICE_CONTROLLER_H_
 #define CHROME_BROWSER_OPTIMIZATION_GUIDE_MODEL_EXECUTION_CHROME_ON_DEVICE_MODEL_SERVICE_CONTROLLER_H_
 
-#include "components/optimization_guide/core/model_execution/on_device_model_service_controller.h"
-
 #include "base/memory/scoped_refptr.h"
+#include "components/optimization_guide/core/model_execution/on_device_model_service_controller.h"
+#include "components/optimization_guide/core/optimization_guide_enums.h"
 
 namespace optimization_guide {
 class OnDeviceModelComponentStateManager;
@@ -30,6 +30,10 @@
   // created yet.
   static ChromeOnDeviceModelServiceController* GetSingleInstanceMayBeNull();
 
+  void RegisterPerformanceClassSyntheticTrial(
+      OnDeviceModelPerformanceClass perf_class) override;
+
+ protected:
  private:
   ~ChromeOnDeviceModelServiceController() override;
 };
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
index 59b73806..12487b2 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.cc
@@ -202,17 +202,6 @@
 }
 
 // static
-void OptimizationGuideKeyedService::RegisterPerformanceClassSyntheticTrial(
-    OnDeviceModelPerformanceClass perf_class) {
-  if (perf_class != OnDeviceModelPerformanceClass::kUnknown) {
-    ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
-        "SyntheticOnDeviceModelPerformanceClass",
-        SyntheticTrialGroupForPerformanceClass(perf_class),
-        variations::SyntheticTrialAnnotationMode::kCurrentLog);
-  }
-}
-
-// static
 void OptimizationGuideKeyedService::SetIsOfficialBuildForTesting(
     bool is_official_build) {
   g_is_official_build_for_testing = is_official_build;
@@ -409,9 +398,11 @@
           optimization_guide::features::GetOnDeviceStartupMetricDelay());
     }
     // If the perf class was previously determined, register that.
-    RegisterPerformanceClassSyntheticTrial(
-        optimization_guide::PerformanceClassFromPref(
-            *g_browser_process->local_state()));
+    GetOnDeviceModelServiceController(
+        on_device_component_manager_->GetWeakPtr())
+        ->RegisterPerformanceClassSyntheticTrial(
+            optimization_guide::PerformanceClassFromPref(
+                *g_browser_process->local_state()));
 
     auto* variations_service = g_browser_process->variations_service();
     auto dogfood_status =
@@ -853,47 +844,8 @@
 
 void OptimizationGuideKeyedService::EnsurePerformanceClassAvailable(
     base::OnceClosure complete) {
-  if (!on_device_component_manager_ ||
-      !on_device_component_manager_->NeedsPerformanceClassUpdate()) {
-    std::move(complete).Run();
-    return;
-  }
-
-  if (performance_class_state_ == PerformanceClassState::kComplete) {
-    std::move(complete).Run();
-    return;
-  }
-
-  // Use unsafe because cancellation isn't needed.
-  performance_class_callbacks_.AddUnsafe(std::move(complete));
-
-  if (performance_class_state_ == PerformanceClassState::kComputing) {
-    return;
-  }
-
-  performance_class_state_ = PerformanceClassState::kComputing;
-  OnDeviceModelServiceController::GetEstimatedPerformanceClass(
-      GetOnDeviceModelServiceController(
-          on_device_component_manager_->GetWeakPtr()),
-      base::BindOnce([](OnDeviceModelPerformanceClass perf_class) {
-        base::UmaHistogramEnumeration(
-            "OptimizationGuide.ModelExecution.OnDeviceModelPerformanceClass",
-            perf_class);
-        RegisterPerformanceClassSyntheticTrial(perf_class);
-        return perf_class;
-      })
-          .Then(base::BindOnce(
-              &OnDeviceModelComponentStateManager::
-                  DevicePerformanceClassChanged,
-              on_device_component_manager_->GetWeakPtr(),
-              base::BindOnce(
-                  &OptimizationGuideKeyedService::PerformanceClassUpdated,
-                  weak_factory_.GetWeakPtr()))));
-}
-
-void OptimizationGuideKeyedService::PerformanceClassUpdated() {
-  performance_class_state_ = PerformanceClassState::kComplete;
-  performance_class_callbacks_.Notify();
+  GetOnDeviceModelServiceController(on_device_component_manager_->GetWeakPtr())
+      ->EnsurePerformanceClassAvailable(std::move(complete));
 }
 
 void OptimizationGuideKeyedService::FinishGetOnDeviceModelEligibility(
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
index ed50b03..24d09d85 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service.h
@@ -9,8 +9,6 @@
 #include <optional>
 #include <vector>
 
-#include "base/callback_list.h"
-#include "base/cancelable_callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/scoped_observation.h"
@@ -282,9 +280,6 @@
   friend class optimization_guide::android::OptimizationGuideBridge;
 #endif  // BUILDFLAG(IS_ANDROID)
 
-  static void RegisterPerformanceClassSyntheticTrial(
-      optimization_guide::OnDeviceModelPerformanceClass perf_class);
-
   // Allows tests to override the value of `version_info::IsOfficialBuild()`.
   static void SetIsOfficialBuildForTesting(bool is_official_build);
 
@@ -361,9 +356,6 @@
   // `complete` runs.
   void EnsurePerformanceClassAvailable(base::OnceClosure complete);
 
-  // Called when performance class has finished updating.
-  void PerformanceClassUpdated();
-
   void FinishGetOnDeviceModelEligibility(
       optimization_guide::ModelBasedCapabilityKey feature,
       base::OnceCallback<
@@ -423,17 +415,6 @@
   // Used to observe profile initialization event.
   base::ScopedObservation<Profile, ProfileObserver> profile_observation_{this};
 
-  enum class PerformanceClassState {
-    kNotSet,
-    kComputing,
-    kComplete,
-  };
-  PerformanceClassState performance_class_state_ =
-      PerformanceClassState::kNotSet;
-
-  // Callbacks waiting for performance class to finish computing.
-  base::OnceClosureList performance_class_callbacks_;
-
   base::WeakPtrFactory<OptimizationGuideKeyedService> weak_factory_{this};
 };
 
diff --git a/chrome/browser/os_crypt/app_bound_encryption_provider_win.cc b/chrome/browser/os_crypt/app_bound_encryption_provider_win.cc
index 1e4a805..8ebeebb 100644
--- a/chrome/browser/os_crypt/app_bound_encryption_provider_win.cc
+++ b/chrome/browser/os_crypt/app_bound_encryption_provider_win.cc
@@ -83,7 +83,7 @@
 
 BASE_FEATURE(kRegenerateKeyForCatastrophicFailures,
              "RegenerateKeyForCatastrophicFailures",
-             base::FEATURE_ENABLED_BY_DEFAULT);
+             base::FEATURE_DISABLED_BY_DEFAULT);
 }  // namespace features
 
 AppBoundEncryptionProviderWin::AppBoundEncryptionProviderWin(
diff --git a/chrome/browser/os_crypt/app_bound_encryption_provider_win.h b/chrome/browser/os_crypt/app_bound_encryption_provider_win.h
index 52c8b1d..548d22a1 100644
--- a/chrome/browser/os_crypt/app_bound_encryption_provider_win.h
+++ b/chrome/browser/os_crypt/app_bound_encryption_provider_win.h
@@ -42,9 +42,8 @@
 // for data encryption by the elevated service.
 BASE_DECLARE_FEATURE(kAppBoundEncryptionKeyV3);
 
-// An emergency kill switch feature to prevent key regeneration for catastrophic
-// failures, in case the numbers in https://crbug.com/382059244#comment2 prove
-// incorrect for some reason.
+// If enabled, will re-generate a new key for catastrophic failures. See
+// `DetermineErrorType` in the cc file for the two current cases.
 BASE_DECLARE_FEATURE(kRegenerateKeyForCatastrophicFailures);
 
 }  // namespace features
diff --git a/chrome/browser/os_crypt/app_bound_encryption_provider_win_unittest.cc b/chrome/browser/os_crypt/app_bound_encryption_provider_win_unittest.cc
index 92dd750..1b62b10 100644
--- a/chrome/browser/os_crypt/app_bound_encryption_provider_win_unittest.cc
+++ b/chrome/browser/os_crypt/app_bound_encryption_provider_win_unittest.cc
@@ -113,6 +113,9 @@
 }
 
 TEST(AppBoundEncryptionProvider, Basic) {
+  base::test::ScopedFeatureList feature(
+      os_crypt_async::features::kRegenerateKeyForCatastrophicFailures);
+
   base::test::TaskEnvironment env;
   ::testing::StrictMock<MockAppBoundEncryptionOverrides> mock_app_bound;
 
diff --git a/chrome/browser/password_manager/password_change/change_form_submission_verifier.cc b/chrome/browser/password_manager/password_change/change_form_submission_verifier.cc
index 31efaafe..b5995527 100644
--- a/chrome/browser/password_manager/password_change/change_form_submission_verifier.cc
+++ b/chrome/browser/password_manager/password_change/change_form_submission_verifier.cc
@@ -11,10 +11,10 @@
 #include "chrome/browser/page_content_annotations/page_content_extraction_service.h"
 #include "chrome/browser/page_content_annotations/page_content_extraction_service_factory.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
+#include "chrome/browser/password_manager/password_change/model_quality_logs_uploader.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/optimization_guide/content/browser/page_content_proto_provider.h"
 #include "components/optimization_guide/core/model_quality/model_execution_logging_wrappers.h"
-#include "components/optimization_guide/core/model_quality/model_quality_log_entry.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/optimization_guide/core/optimization_guide_model_executor.h"
 #include "components/optimization_guide/core/optimization_guide_proto_util.h"
@@ -53,28 +53,6 @@
       .Record(ukm::UkmRecorder::Get());
 }
 
-void AddFinalModelStatusLog(
-    OptimizationGuideKeyedService* optimization_executor,
-    FinalModelStatus final_status) {
-  base::WeakPtr<optimization_guide::ModelQualityLogsUploaderService>
-      logs_uploader_ptr;
-
-  auto* logs_uploader =
-      optimization_executor->GetModelQualityLogsUploaderService();
-  if (logs_uploader) {
-    logs_uploader_ptr = logs_uploader->GetWeakPtr();
-  }
-
-  QualityLogEntry log_entry =
-      std::make_unique<optimization_guide::ModelQualityLogEntry>(
-          logs_uploader_ptr);
-  log_entry->log_ai_data_request()
-      ->mutable_password_change_submission()
-      ->mutable_quality()
-      ->set_final_model_status(final_status);
-  optimization_guide::ModelQualityLogEntry::Upload(std::move(log_entry));
-}
-
 void RecordOutcomeMetrics(
     optimization_guide::proto ::PasswordChangeSubmissionData submission_data,
     ukm::SourceId ukm_id) {
@@ -131,21 +109,26 @@
 
 ChangeFormSubmissionVerifier::ChangeFormSubmissionVerifier(
     content::WebContents* web_contents,
-    FormSubmissionResultCallback callback)
+    FormSubmissionResultCallback callback,
+    ModelQualityLogsUploader* logs_uploader)
     : web_contents_(web_contents->GetWeakPtr()),
       capture_annotated_page_content_(
           base::BindOnce(&optimization_guide::GetAIPageContent,
                          web_contents,
                          optimization_guide::DefaultAIPageContentOptions())),
-      callback_(std::move(callback)) {}
+      callback_(std::move(callback)),
+      logs_uploader_(logs_uploader) {}
 
 ChangeFormSubmissionVerifier::ChangeFormSubmissionVerifier(
     base::PassKey<class ChangeFormSubmissionVerifierTest>,
     content::WebContents* web_contents,
     base::OnceCallback<void(optimization_guide::OnAIPageContentDone)>
         capture_annotated_page_content,
-    FormSubmissionResultCallback callback)
-    : ChangeFormSubmissionVerifier(web_contents, std::move(callback)) {
+    FormSubmissionResultCallback callback,
+    ModelQualityLogsUploader* logs_uploader)
+    : ChangeFormSubmissionVerifier(web_contents,
+                                   std::move(callback),
+                                   std::move(logs_uploader)) {
   capture_annotated_page_content_ = std::move(capture_annotated_page_content);
 }
 
@@ -352,13 +335,9 @@
   ChromePasswordManagerClient* client =
       ChromePasswordManagerClient::FromWebContents(web_contents_.get());
 
-  OptimizationGuideKeyedService* optimization_executor =
-      GetOptimizationService();
   if (!execution_result.response.has_value()) {
     LogSubmissionOutcome(SubmissionOutcome::kNoResponse,
                          client->GetUkmSourceId());
-    AddFinalModelStatusLog(optimization_executor,
-                           FinalModelStatus::FINAL_MODEL_STATUS_UNSPECIFIED);
     std::move(callback_).Run(false);
     return;
   }
@@ -369,12 +348,15 @@
   if (!response) {
     LogSubmissionOutcome(SubmissionOutcome::kCouldNotParse,
                          client->GetUkmSourceId());
-    AddFinalModelStatusLog(optimization_executor,
-                           FinalModelStatus::FINAL_MODEL_STATUS_UNSPECIFIED);
     std::move(callback_).Run(false);
     return;
   }
 
+  if (logging_data) {
+    // There is data to log, meaning this is a complete response.
+    logs_uploader_->MergeData(response.value(), std::move(logging_data));
+  }
+
   RecordOutcomeMetrics(response.value().outcome_data(),
                        client->GetUkmSourceId());
   PasswordChangeOutcome outcome =
@@ -385,12 +367,8 @@
       outcome !=
           PasswordChangeOutcome::
               PasswordChangeSubmissionData_PasswordChangeOutcome_UNKNOWN_OUTCOME) {
-    AddFinalModelStatusLog(optimization_executor,
-                           FinalModelStatus::FINAL_MODEL_STATUS_FAILURE);
     std::move(callback_).Run(false);
     return;
   }
-  AddFinalModelStatusLog(optimization_executor,
-                         FinalModelStatus::FINAL_MODEL_STATUS_SUCCESS);
   std::move(callback_).Run(true);
 }
diff --git a/chrome/browser/password_manager/password_change/change_form_submission_verifier.h b/chrome/browser/password_manager/password_change/change_form_submission_verifier.h
index dfeee84f..c0951c2 100644
--- a/chrome/browser/password_manager/password_change/change_form_submission_verifier.h
+++ b/chrome/browser/password_manager/password_change/change_form_submission_verifier.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include "base/functional/callback_forward.h"
+#include "base/memory/raw_ptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/timer/timer.h"
 #include "components/autofill/core/common/form_data.h"
@@ -19,6 +20,7 @@
 namespace content {
 class WebContents;
 }
+class ModelQualityLogsUploader;
 class OptimizationGuideKeyedService;
 namespace password_manager {
 class PasswordFormManager;
@@ -63,13 +65,15 @@
       "PasswordManager.PasswordChangeVerificationTriggeredAutomatically";
 
   ChangeFormSubmissionVerifier(content::WebContents* web_contents,
-                               FormSubmissionResultCallback callback);
+                               FormSubmissionResultCallback callback,
+                               ModelQualityLogsUploader* logs_uploader);
   ChangeFormSubmissionVerifier(
       base::PassKey<class ChangeFormSubmissionVerifierTest>,
       content::WebContents* web_contents,
       base::OnceCallback<void(optimization_guide::OnAIPageContentDone)>
           capture_annotated_page_content,
-      FormSubmissionResultCallback callback);
+      FormSubmissionResultCallback callback,
+      ModelQualityLogsUploader* logs_uploader);
   ~ChangeFormSubmissionVerifier();
 
   // Starts chain of actions:
@@ -127,6 +131,7 @@
   base::OnceCallback<void(optimization_guide::OnAIPageContentDone)>
       capture_annotated_page_content_;
   FormSubmissionResultCallback callback_;
+  raw_ptr<ModelQualityLogsUploader> logs_uploader_;
   std::unique_ptr<password_manager::PasswordFormManager> form_manager_;
   // TODO(crbug.com/409946698): Delete this when removing support for AX tree
   // prompts.
diff --git a/chrome/browser/password_manager/password_change/change_form_submission_verifier_unittest.cc b/chrome/browser/password_manager/password_change/change_form_submission_verifier_unittest.cc
index e8f4d5c..a7751fb9 100644
--- a/chrome/browser/password_manager/password_change/change_form_submission_verifier_unittest.cc
+++ b/chrome/browser/password_manager/password_change/change_form_submission_verifier_unittest.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/password_manager/chrome_webauthn_credentials_delegate_factory.h"
+#include "chrome/browser/password_manager/password_change/model_quality_logs_uploader.h"
 #include "chrome/browser/password_manager/password_manager_settings_service_factory.h"
 #include "chrome/browser/password_manager/profile_password_store_factory.h"
 #include "chrome/browser/sync/sync_service_factory.h"
@@ -141,7 +142,8 @@
  public:
   ChangeFormSubmissionVerifierTest()
       : ChromeRenderViewHostTestHarness(
-            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+            base::test::TaskEnvironment::TimeSource::MOCK_TIME),
+        logs_uploader_(profile()) {}
   ~ChangeFormSubmissionVerifierTest() override = default;
 
   void SetUp() override {
@@ -157,7 +159,6 @@
         base::BindRepeating(&password_manager::BuildPasswordStoreInterface<
                             content::BrowserContext,
                             password_manager::MockPasswordStoreInterface>));
-
     // `ChromePasswordManagerClient` observes `AutofillManager`s, so
     // `ChromeAutofillClient` needs to be set up, too.
     autofill::ChromeAutofillClient::CreateForWebContents(web_contents());
@@ -190,7 +191,8 @@
       base::OnceCallback<void(bool)> result_callback) {
     auto verifier = std::make_unique<ChangeFormSubmissionVerifier>(
         base::PassKey<class ChangeFormSubmissionVerifierTest>(), web_contents(),
-        std::move(capture_annotated_page_content), std::move(result_callback));
+        std::move(capture_annotated_page_content), std::move(result_callback),
+        &logs_uploader_);
     verifier->FillChangePasswordForm(manager, kOldPassword, kNewPassword);
     return verifier;
   }
@@ -211,6 +213,7 @@
   autofill::test::AutofillUnitTestEnvironment autofill_environment_{
       {.disable_server_communication = true}};
   password_manager::FakeFormFetcher form_fetcher_;
+  ModelQualityLogsUploader logs_uploader_;
   MockStubPasswordManagerDriver driver_;
 };
 
diff --git a/chrome/browser/password_manager/password_change/model_quality_logs_uploader.cc b/chrome/browser/password_manager/password_change/model_quality_logs_uploader.cc
new file mode 100644
index 0000000..f04f61316
--- /dev/null
+++ b/chrome/browser/password_manager/password_change/model_quality_logs_uploader.cc
@@ -0,0 +1,72 @@
+// 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 "chrome/browser/password_manager/password_change/model_quality_logs_uploader.h"
+
+#include "base/logging.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 "components/optimization_guide/core/model_quality/model_quality_log_entry.h"
+#include "content/public/browser/web_contents.h"
+
+using FinalModelStatus = optimization_guide::proto::FinalModelStatus;
+using PasswordChangeOutcome = optimization_guide::proto ::
+    PasswordChangeSubmissionData_PasswordChangeOutcome;
+
+ModelQualityLogsUploader::ModelQualityLogsUploader(Profile* profile)
+    : profile_(profile) {}
+ModelQualityLogsUploader::~ModelQualityLogsUploader() = default;
+
+void ModelQualityLogsUploader::AddFinalModelStatusLog(
+    FinalModelStatus final_model_status,
+    std::unique_ptr<
+        optimization_guide::proto::PasswordChangeSubmissionLoggingData>
+        logging_data) {
+  optimization_guide::proto::LogAiDataRequest request;
+  request.mutable_password_change_submission()->MergeFrom(*logging_data);
+  // Set final model status
+  request.mutable_password_change_submission()
+      ->mutable_quality()
+      ->set_final_model_status(final_model_status);
+  // Store the new log request
+  log_entries_requests_.push_back(std::move(request));
+}
+
+void ModelQualityLogsUploader::UploadFinalLog() {
+  auto* logs_uploader =
+      OptimizationGuideKeyedServiceFactory::GetForProfile(profile_)
+          ->GetModelQualityLogsUploaderService();
+  if (!logs_uploader) {
+    return;
+  }
+  auto new_log_entry =
+      std::make_unique<optimization_guide::ModelQualityLogEntry>(
+          logs_uploader->GetWeakPtr());
+  for (const auto& entry : log_entries_requests_) {
+    new_log_entry->log_ai_data_request()->MergeFrom(entry);
+  }
+  optimization_guide::ModelQualityLogEntry::Upload(std::move(new_log_entry));
+}
+
+void ModelQualityLogsUploader::MergeData(
+    const optimization_guide::proto::PasswordChangeResponse& response,
+    std::unique_ptr<
+        optimization_guide::proto::PasswordChangeSubmissionLoggingData>
+        logging_data) {
+  // TODO(407503334): Split this per step, now assuming it is just verify
+  // submission.
+  PasswordChangeOutcome outcome = response.outcome_data().submission_outcome();
+  if (outcome !=
+          PasswordChangeOutcome::
+              PasswordChangeSubmissionData_PasswordChangeOutcome_SUCCESSFUL_OUTCOME &&
+      outcome !=
+          PasswordChangeOutcome::
+              PasswordChangeSubmissionData_PasswordChangeOutcome_UNKNOWN_OUTCOME) {
+    AddFinalModelStatusLog(FinalModelStatus::FINAL_MODEL_STATUS_FAILURE,
+                           std::move(logging_data));
+  } else {
+    AddFinalModelStatusLog(FinalModelStatus::FINAL_MODEL_STATUS_SUCCESS,
+                           std::move(logging_data));
+  }
+}
diff --git a/chrome/browser/password_manager/password_change/model_quality_logs_uploader.h b/chrome/browser/password_manager/password_change/model_quality_logs_uploader.h
new file mode 100644
index 0000000..4f1f4a4e
--- /dev/null
+++ b/chrome/browser/password_manager/password_change/model_quality_logs_uploader.h
@@ -0,0 +1,56 @@
+// 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 CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_CHANGE_MODEL_QUALITY_LOGS_UPLOADER_H_
+#define CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_CHANGE_MODEL_QUALITY_LOGS_UPLOADER_H_
+
+#include "components/optimization_guide/core/model_quality/model_quality_log_entry.h"
+#include "components/optimization_guide/core/model_quality/model_quality_logs_uploader_service.h"
+
+class Profile;
+
+// Helper class which handles Model Logging Quality logic and uploads the
+// logs to the Server.
+class ModelQualityLogsUploader {
+ public:
+  explicit ModelQualityLogsUploader(Profile* profile);
+  ~ModelQualityLogsUploader();
+  ModelQualityLogsUploader(const ModelQualityLogsUploader&) = delete;
+  ModelQualityLogsUploader& operator=(const ModelQualityLogsUploader&) = delete;
+  // As we only want to record one log per flow, this is to be called just
+  // once. It will merge all existing LogAiDataRequest and upload a single
+  // log entry to the model quality logging service.
+  void UploadFinalLog();
+  // Merges the logging data with the response given by
+  // optimization service call.
+  void MergeData(
+      const optimization_guide::proto::PasswordChangeResponse& response,
+      std::unique_ptr<
+          optimization_guide::proto::PasswordChangeSubmissionLoggingData>
+          logging_data);
+#if defined(UNIT_TEST)
+  // Used for testing only.
+  const std::vector<optimization_guide::proto::LogAiDataRequest>&
+  GetLogEntryRequestsForTesting() const {
+    return log_entries_requests_;
+  }
+#endif
+
+ private:
+  std::unique_ptr<optimization_guide::ModelQualityLogEntry> CreateNewLogEntry();
+  void AddFinalModelStatusLog(
+      optimization_guide::proto::FinalModelStatus final_model_status,
+      std::unique_ptr<
+          optimization_guide::proto::PasswordChangeSubmissionLoggingData>
+          logging_data);
+
+  // Holds all feature's logging data request to at the end of the
+  // flow merge them in a single log entry.
+  std::vector<optimization_guide::proto::LogAiDataRequest>
+      log_entries_requests_;
+  const raw_ptr<Profile> profile_;
+  base::WeakPtrFactory<ModelQualityLogsUploader> weak_ptr_factory_{this};
+};
+
+#endif  // CHROME_BROWSER_PASSWORD_MANAGER_PASSWORD_CHANGE_MODEL_QUALITY_LOGS_UPLOADER_H_
diff --git a/chrome/browser/password_manager/password_change/model_quality_logs_uploader_unittest.cc b/chrome/browser/password_manager/password_change/model_quality_logs_uploader_unittest.cc
new file mode 100644
index 0000000..1bbd3d3
--- /dev/null
+++ b/chrome/browser/password_manager/password_change/model_quality_logs_uploader_unittest.cc
@@ -0,0 +1,44 @@
+// 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 "chrome/browser/password_manager/password_change/model_quality_logs_uploader.h"
+
+#include <memory>
+#include <vector>
+
+#include "base/test/test_future.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/optimization_guide/core/model_quality/model_quality_log_entry.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using FinalModelStatus = optimization_guide::proto::FinalModelStatus;
+using PasswordChangeSubmissionLoggingData =
+    optimization_guide::proto::PasswordChangeSubmissionLoggingData;
+using PasswordChangeOutcome = ::optimization_guide::proto::
+    PasswordChangeSubmissionData_PasswordChangeOutcome;
+
+class ModelQualityLogsUploaderTest : public ChromeRenderViewHostTestHarness {
+ public:
+  ModelQualityLogsUploaderTest() = default;
+  ~ModelQualityLogsUploaderTest() override = default;
+};
+
+TEST_F(ModelQualityLogsUploaderTest, AddFinalModelStatusLog) {
+  ModelQualityLogsUploader logs_uploader(profile());
+  auto logging_data = std::make_unique<PasswordChangeSubmissionLoggingData>();
+  optimization_guide::proto::PasswordChangeResponse response;
+  response.mutable_outcome_data()->set_submission_outcome(
+      PasswordChangeOutcome::
+          PasswordChangeSubmissionData_PasswordChangeOutcome_SUCCESSFUL_OUTCOME);
+  logs_uploader.MergeData(response, std::move(logging_data));
+  auto logs = logs_uploader.GetLogEntryRequestsForTesting();
+  ASSERT_EQ(logs.size(), 1u);
+  ASSERT_EQ(logs[0]
+                .mutable_password_change_submission()
+                ->mutable_quality()
+                ->final_model_status(),
+            FinalModelStatus::FINAL_MODEL_STATUS_SUCCESS);
+}
diff --git a/chrome/browser/password_manager/password_change_delegate_impl.cc b/chrome/browser/password_manager/password_change_delegate_impl.cc
index 161fb39..30ff47a 100644
--- a/chrome/browser/password_manager/password_change_delegate_impl.cc
+++ b/chrome/browser/password_manager/password_change_delegate_impl.cc
@@ -12,6 +12,8 @@
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/password_manager/password_change/change_form_submission_verifier.h"
 #include "chrome/browser/password_manager/password_change/change_password_form_finder.h"
+#include "chrome/browser/password_manager/password_change/change_password_form_waiter.h"
+#include "chrome/browser/password_manager/password_change/model_quality_logs_uploader.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
 #include "components/autofill/core/browser/logging/log_manager.h"
@@ -154,7 +156,9 @@
     : change_password_url_(std::move(change_password_url)),
       username_(std::move(username)),
       original_password_(std::move(password)),
-      originator_(originator->GetWeakPtr()) {
+      originator_(originator->GetWeakPtr()),
+      logs_uploader_(std::make_unique<ModelQualityLogsUploader>(
+          Profile::FromBrowserContext(originator->GetBrowserContext()))) {
   if (auto logger = GetLoggerIfAvailable(originator_)) {
     logger->LogMessage(
         BrowserSavePasswordProgressLogger::STRING_PASSWORD_CHANGE_STARTED);
@@ -227,7 +231,8 @@
       executor_.get(),
       base::BindOnce(
           &PasswordChangeDelegateImpl::OnChangeFormSubmissionVerified,
-          weak_ptr_factory_.GetWeakPtr()));
+          weak_ptr_factory_.GetWeakPtr()),
+      logs_uploader_.get());
   submission_verifier_->FillChangePasswordForm(form_manager, original_password_,
                                                generated_password_);
   UpdateState(PasswordChangeDelegate::State::kChangingPassword);
@@ -393,7 +398,7 @@
     submission_verifier_->SavePassword(username_);
     UpdateState(State::kPasswordSuccessfullyChanged);
   }
-
+  logs_uploader_->UploadFinalLog();
   submission_verifier_.reset();
 }
 bool PasswordChangeDelegateImpl::IsPrivacyNoticeAcknowledged() const {
diff --git a/chrome/browser/password_manager/password_change_delegate_impl.h b/chrome/browser/password_manager/password_change_delegate_impl.h
index 87c22ea..6a8f195 100644
--- a/chrome/browser/password_manager/password_change_delegate_impl.h
+++ b/chrome/browser/password_manager/password_change_delegate_impl.h
@@ -28,6 +28,8 @@
 
 class ChangeFormSubmissionVerifier;
 class ChangePasswordFormFinder;
+class ChangePasswordFormWaiter;
+class ModelQualityLogsUploader;
 
 // This class controls password change process including acceptance of privacy
 // notice, opening of a new tab, navigation to the change password url, password
@@ -115,6 +117,11 @@
 
   // Helper class which looks for a change password form.
   std::unique_ptr<ChangePasswordFormFinder> form_finder_;
+  // Helper class which uploads model quality logs.
+  std::unique_ptr<ModelQualityLogsUploader> logs_uploader_;
+
+  // Class which awaits for change password form to appear.
+  std::unique_ptr<ChangePasswordFormWaiter> form_waiter_;
 
   // Helper class which submits a form and verifies submission.
   std::unique_ptr<ChangeFormSubmissionVerifier> submission_verifier_;
diff --git a/chrome/browser/pdf/pdf_extension_js_test.cc b/chrome/browser/pdf/pdf_extension_js_test.cc
index b17e27cc..1c168c0 100644
--- a/chrome/browser/pdf/pdf_extension_js_test.cc
+++ b/chrome/browser/pdf/pdf_extension_js_test.cc
@@ -624,7 +624,14 @@
   RunTestsInJsModule("ink2_size_selector_test.js", "test.pdf");
 }
 
-IN_PROC_BROWSER_TEST_P(PDFExtensionJSInk2Test, Ink2ViewerToolbar) {
+// TODO(crbug.com/416359681): Ink2ViewerToolbar is flaky on Mac12 and Mac13
+// tests.
+#if !BUILDFLAG(IS_MAC)
+#define MAYBE_Ink2ViewerToolbar Ink2ViewerToolbar
+#else
+#define MAYBE_Ink2ViewerToolbar DISABLED_Ink2ViewerToolbar
+#endif
+IN_PROC_BROWSER_TEST_P(PDFExtensionJSInk2Test, MAYBE_Ink2ViewerToolbar) {
   RunTestsInJsModule("ink2_viewer_toolbar_test.js", "test.pdf");
 }
 
diff --git a/chrome/browser/performance_manager/policies/background_tab_loading_policy.cc b/chrome/browser/performance_manager/policies/background_tab_loading_policy.cc
index 8780830..4a93348 100644
--- a/chrome/browser/performance_manager/policies/background_tab_loading_policy.cc
+++ b/chrome/browser/performance_manager/policies/background_tab_loading_policy.cc
@@ -387,8 +387,10 @@
     return false;
 
   // Enforce a max time since last use.
-  if (page_node_data.page_node->GetTimeSinceLastVisibilityChange() >
-      kMaxTimeSinceLastUseToLoad) {
+  const base::TimeDelta time_since_last_visibility_change =
+      base::TimeTicks::Now() -
+      page_node_data.page_node->GetLastVisibilityChangeTime();
+  if (time_since_last_visibility_change > kMaxTimeSinceLastUseToLoad) {
     return false;
   }
 
@@ -492,9 +494,10 @@
 
   // Refine the score using the age of the tab. More recently used tabs have
   // higher scores.
-  score += CalculateAgeScore(
-      page_node_to_load_data->page_node->GetTimeSinceLastVisibilityChange()
-          .InSecondsF());
+  const base::TimeDelta time_since_last_visibility_change =
+      base::TimeTicks::Now() -
+      page_node_to_load_data->page_node->GetLastVisibilityChangeTime();
+  score += CalculateAgeScore(time_since_last_visibility_change.InSecondsF());
 
   ++tabs_scored_;
   page_node_to_load_data->score = score;
diff --git a/chrome/browser/performance_manager/policies/discard_eligibility_policy.cc b/chrome/browser/performance_manager/policies/discard_eligibility_policy.cc
index dc0490b..8ddcc06 100644
--- a/chrome/browser/performance_manager/policies/discard_eligibility_policy.cc
+++ b/chrome/browser/performance_manager/policies/discard_eligibility_policy.cc
@@ -36,16 +36,17 @@
 
 }  // namespace
 
-PageNodeSortProxy::PageNodeSortProxy(base::WeakPtr<const PageNode> page_node,
-                                     CanDiscardResult can_discard_result,
-                                     bool is_visible,
-                                     bool is_focused,
-                                     base::TimeDelta last_visible)
+PageNodeSortProxy::PageNodeSortProxy(
+    base::WeakPtr<const PageNode> page_node,
+    CanDiscardResult can_discard_result,
+    bool is_visible,
+    bool is_focused,
+    base::TimeTicks last_visibility_change_time)
     : page_node_(std::move(page_node)),
       can_discard_result_(can_discard_result),
       is_visible_(is_visible),
       is_focused_(is_focused),
-      last_visible_(last_visible) {}
+      last_visibility_change_time_(last_visibility_change_time) {}
 
 PageNodeSortProxy::PageNodeSortProxy(PageNodeSortProxy&&) = default;
 PageNodeSortProxy& PageNodeSortProxy::operator=(PageNodeSortProxy&&) = default;
@@ -179,7 +180,8 @@
   if (page_node->IsVisible()) {
     add_reason_and_update_result(CannotDiscardReason::kVisible,
                                  CanDiscardResult::kProtected);
-  } else if (page_node->GetTimeSinceLastVisibilityChange() <
+  } else if ((base::TimeTicks::Now() -
+              page_node->GetLastVisibilityChangeTime()) <
              minimum_time_in_background) {
     add_reason_and_update_result(CannotDiscardReason::kRecentlyVisible,
                                  CanDiscardResult::kProtected);
diff --git a/chrome/browser/performance_manager/policies/discard_eligibility_policy.h b/chrome/browser/performance_manager/policies/discard_eligibility_policy.h
index 6488603..dbdcf4b 100644
--- a/chrome/browser/performance_manager/policies/discard_eligibility_policy.h
+++ b/chrome/browser/performance_manager/policies/discard_eligibility_policy.h
@@ -58,7 +58,7 @@
                     CanDiscardResult can_discard_result,
                     bool is_visible,
                     bool is_focused,
-                    base::TimeDelta last_visible);
+                    base::TimeTicks last_visibility_change_time);
   PageNodeSortProxy(PageNodeSortProxy&&);
   PageNodeSortProxy& operator=(PageNodeSortProxy&&);
   ~PageNodeSortProxy();
@@ -72,7 +72,9 @@
   }
   bool is_visible() const { return is_visible_; }
   bool is_focused() const { return is_focused_; }
-  base::TimeDelta last_visible() const { return last_visible_; }
+  base::TimeTicks last_visibility_change_time() const {
+    return last_visibility_change_time_;
+  }
 
   // Returns true if the rhs is more important.
   bool operator<(const PageNodeSortProxy& rhs) const {
@@ -88,7 +90,7 @@
     if (is_protected() != rhs.is_protected()) {
       return rhs.is_protected();
     }
-    return last_visible_ > rhs.last_visible_;
+    return last_visibility_change_time_ < rhs.last_visibility_change_time_;
   }
 
  private:
@@ -96,8 +98,7 @@
   CanDiscardResult can_discard_result_;
   bool is_visible_;
   bool is_focused_;
-  // Delta between current time and last visibility change time.
-  base::TimeDelta last_visible_;
+  base::TimeTicks last_visibility_change_time_;
 };
 
 // DiscardEligibilityPolicy decides which PageNode is eligigle for tab
diff --git a/chrome/browser/performance_manager/policies/discard_eligibility_policy_unittest.cc b/chrome/browser/performance_manager/policies/discard_eligibility_policy_unittest.cc
index 2f84370..cd13c64d 100644
--- a/chrome/browser/performance_manager/policies/discard_eligibility_policy_unittest.cc
+++ b/chrome/browser/performance_manager/policies/discard_eligibility_policy_unittest.cc
@@ -30,41 +30,45 @@
 using ::testing::Return;
 
 TEST(PageNodeSortProxyTest, Order) {
+  auto absolute_time = [](int seconds) {
+    return base::TimeTicks() + base::Seconds(seconds);
+  };
+
   // Disabled tab is never discarded over focused tabs.
   EXPECT_TRUE(
-      PageNodeSortProxy(nullptr, kProtected, true, true, base::Seconds(1)) <
-      PageNodeSortProxy(nullptr, kDisallowed, false, false, base::Seconds(10)));
+      PageNodeSortProxy(nullptr, kProtected, true, true, absolute_time(10)) <
+      PageNodeSortProxy(nullptr, kDisallowed, false, false, absolute_time(1)));
   // Focused tab is more important than visible & non-focused tab.
   EXPECT_TRUE(
-      PageNodeSortProxy(nullptr, kProtected, true, false, base::Seconds(1)) <
-      PageNodeSortProxy(nullptr, kProtected, true, true, base::Seconds(10)));
+      PageNodeSortProxy(nullptr, kProtected, true, false, absolute_time(10)) <
+      PageNodeSortProxy(nullptr, kProtected, true, true, absolute_time(1)));
   // Visible tab is more important than protected & non-visible tab.
   EXPECT_TRUE(
-      PageNodeSortProxy(nullptr, kProtected, false, false, base::Seconds(1)) <
-      PageNodeSortProxy(nullptr, kProtected, true, false, base::Seconds(10)));
+      PageNodeSortProxy(nullptr, kProtected, false, false, absolute_time(10)) <
+      PageNodeSortProxy(nullptr, kProtected, true, false, absolute_time(1)));
   // Protected tab is more important than non-protected tab.
   EXPECT_TRUE(
-      PageNodeSortProxy(nullptr, kEligible, false, false, base::Seconds(1)) <
-      PageNodeSortProxy(nullptr, kProtected, false, false, base::Seconds(10)));
+      PageNodeSortProxy(nullptr, kEligible, false, false, absolute_time(10)) <
+      PageNodeSortProxy(nullptr, kProtected, false, false, absolute_time(1)));
 
   // Compare disabled tabs.
   EXPECT_TRUE(
-      PageNodeSortProxy(nullptr, kDisallowed, false, false, base::Seconds(10)) <
-      PageNodeSortProxy(nullptr, kDisallowed, false, false, base::Seconds(1)));
-  // Sort visible tabs based on last_visible_.
+      PageNodeSortProxy(nullptr, kDisallowed, false, false, absolute_time(1)) <
+      PageNodeSortProxy(nullptr, kDisallowed, false, false, absolute_time(10)));
+  // Sort visible tabs based on `last_visibility_change_time_`.
   // TODO(crbug.com/391243672): use focus status change instead of
   // last_visible_.
   EXPECT_TRUE(
-      PageNodeSortProxy(nullptr, kProtected, true, false, base::Seconds(10)) <
-      PageNodeSortProxy(nullptr, kProtected, true, false, base::Seconds(1)));
-  // Sort protected tabs based on last_visible_.
+      PageNodeSortProxy(nullptr, kProtected, true, false, absolute_time(1)) <
+      PageNodeSortProxy(nullptr, kProtected, true, false, absolute_time(10)));
+  // Sort protected tabs based on `last_visibility_change_time_`.
   EXPECT_TRUE(
-      PageNodeSortProxy(nullptr, kProtected, false, false, base::Seconds(10)) <
-      PageNodeSortProxy(nullptr, kProtected, false, false, base::Seconds(1)));
-  // Sort non-protected tabs based on last_visible_.
+      PageNodeSortProxy(nullptr, kProtected, false, false, absolute_time(1)) <
+      PageNodeSortProxy(nullptr, kProtected, false, false, absolute_time(10)));
+  // Sort non-protected tabs based on `last_visibility_change_time_`.
   EXPECT_TRUE(
-      PageNodeSortProxy(nullptr, kEligible, false, false, base::Seconds(10)) <
-      PageNodeSortProxy(nullptr, kEligible, false, false, base::Seconds(1)));
+      PageNodeSortProxy(nullptr, kEligible, false, false, absolute_time(1)) <
+      PageNodeSortProxy(nullptr, kEligible, false, false, absolute_time(10)));
 }
 
 class DiscardEligibilityPolicyTest
diff --git a/chrome/browser/performance_manager/policies/oom_score_policy_chromeos.cc b/chrome/browser/performance_manager/policies/oom_score_policy_chromeos.cc
index e40fd03..a502f89 100644
--- a/chrome/browser/performance_manager/policies/oom_score_policy_chromeos.cc
+++ b/chrome/browser/performance_manager/policies/oom_score_policy_chromeos.cc
@@ -83,7 +83,7 @@
     bool is_focused = page_node->IsFocused();
     candidates.emplace_back(page_node->GetWeakPtr(), can_discard_result,
                             is_visible, is_focused,
-                            page_node->GetTimeSinceLastVisibilityChange());
+                            page_node->GetLastVisibilityChangeTime());
   }
 
   // Sorts with descending importance.
diff --git a/chrome/browser/performance_manager/policies/page_discarding_helper.cc b/chrome/browser/performance_manager/policies/page_discarding_helper.cc
index b5912c9..dafe483 100644
--- a/chrome/browser/performance_manager/policies/page_discarding_helper.cc
+++ b/chrome/browser/performance_manager/policies/page_discarding_helper.cc
@@ -155,7 +155,7 @@
     }
     candidates.emplace_back(page_node->GetWeakPtr(), can_discard_result,
                             page_node->IsVisible(), page_node->IsFocused(),
-                            page_node->GetTimeSinceLastVisibilityChange());
+                            page_node->GetLastVisibilityChangeTime());
   }
 
   // Sorts with descending importance.
diff --git a/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc b/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc
index a988a7b..7bd87807 100644
--- a/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc
+++ b/chrome/browser/performance_manager/policies/page_discarding_helper_unittest.cc
@@ -313,8 +313,8 @@
   page_node2->SetIsVisible(false);
   AdvanceClock(base::Minutes(30));
   EXPECT_EQ(kEligible, CanDiscard(page_node2.get(), DiscardReason::URGENT));
-  EXPECT_GT(page_node()->GetTimeSinceLastVisibilityChange(),
-            page_node2->GetTimeSinceLastVisibilityChange());
+  EXPECT_LT(page_node()->GetLastVisibilityChangeTime(),
+            page_node2->GetLastVisibilityChangeTime());
 
   process_node()->set_resident_set_kb(1024);
   process_node2->set_resident_set_kb(2048);
@@ -394,8 +394,8 @@
   page_node()->SetIsVisible(false);
   AdvanceClock(base::Minutes(30));
   EXPECT_EQ(kEligible, CanDiscard(page_node(), DiscardReason::URGENT));
-  EXPECT_GT(page_node2->GetTimeSinceLastVisibilityChange(),
-            page_node()->GetTimeSinceLastVisibilityChange());
+  EXPECT_LT(page_node2->GetLastVisibilityChangeTime(),
+            page_node()->GetLastVisibilityChangeTime());
 
   // |page_node2| should be discarded as there's no RSS data for any of the
   // pages and it's the least recently visible page.
@@ -423,8 +423,8 @@
   page_node()->SetIsVisible(false);
   AdvanceClock(base::Minutes(30));
   EXPECT_EQ(kEligible, CanDiscard(page_node(), DiscardReason::URGENT));
-  EXPECT_GT(page_node2->GetTimeSinceLastVisibilityChange(),
-            page_node()->GetTimeSinceLastVisibilityChange());
+  EXPECT_LT(page_node2->GetLastVisibilityChangeTime(),
+            page_node()->GetLastVisibilityChangeTime());
 
   // |page_node2| should be discarded as there's no RSS data for any of the
   // pages and it's the least recently visible page.
diff --git a/chrome/browser/performance_manager/policies/policy_features.cc b/chrome/browser/performance_manager/policies/policy_features.cc
index 7b3478b..b3b7339 100644
--- a/chrome/browser/performance_manager/policies/policy_features.cc
+++ b/chrome/browser/performance_manager/policies/policy_features.cc
@@ -98,7 +98,7 @@
 //
 // * To mitigate load pressure on system because the system is busy just after
 //   resuming for a while.
-// * GetTimeSinceLastVisibilityChange() of each node become meaningless because
+// * GetLastVisibilityChangeTime() of each node become meaningless because
 //   the monotonic clock keeps proceeding during dark resume. Waiting for
 //   kNodeInvisibleTimeSec after resuming ensures that enough time has elapsed
 //   so that inappropriately added time from dark resume can no longer affect
diff --git a/chrome/browser/performance_manager/policies/report_page_processes_policy.cc b/chrome/browser/performance_manager/policies/report_page_processes_policy.cc
index d0c49b6..3b87519 100644
--- a/chrome/browser/performance_manager/policies/report_page_processes_policy.cc
+++ b/chrome/browser/performance_manager/policies/report_page_processes_policy.cc
@@ -143,7 +143,7 @@
     bool is_focused = page_node->IsFocused();
     candidates.emplace_back(page_node->GetWeakPtr(), can_discard_result,
                             is_visible, is_focused,
-                            page_node->GetTimeSinceLastVisibilityChange());
+                            page_node->GetLastVisibilityChangeTime());
   }
 
   // Sorts with descending importance.
@@ -159,8 +159,6 @@
     const std::vector<PageNodeSortProxy>& candidates) {
   base::flat_map<base::ProcessId, PageState> current_pages;
 
-  base::TimeTicks report_time = base::TimeTicks::Now();
-
   for (auto& candidate : candidates) {
     // Only list candidates that could be discarded.
     if (candidate.is_disallowed()) {
@@ -182,7 +180,7 @@
           std::piecewise_construct, std::forward_as_tuple(pid),
           std::forward_as_tuple(candidate.is_protected(),
                                 candidate.is_visible(), candidate.is_focused(),
-                                report_time - candidate.last_visible()));
+                                candidate.last_visibility_change_time()));
     }
   }
 
diff --git a/chrome/browser/performance_manager/policies/userspace_swap_policy_chromeos.cc b/chrome/browser/performance_manager/policies/userspace_swap_policy_chromeos.cc
index a5eb2b2..27f4a89 100644
--- a/chrome/browser/performance_manager/policies/userspace_swap_policy_chromeos.cc
+++ b/chrome/browser/performance_manager/policies/userspace_swap_policy_chromeos.cc
@@ -190,7 +190,7 @@
     if (process_node && process_node->GetProcess().IsValid()) {
       bool is_visible = page_node->IsVisible();
       auto last_visibility_change =
-          page_node->GetTimeSinceLastVisibilityChange();
+          now_ticks - page_node->GetLastVisibilityChangeTime();
       auto url = main_frame_node->GetURL();
 
       uint64_t memory_reclaimed = GetProcessNodeReclaimedBytes(process_node);
@@ -254,9 +254,9 @@
   return page_node->IsVisible();
 }
 
-base::TimeDelta UserspaceSwapPolicy::GetTimeSinceLastVisibilityChange(
+base::TimeTicks UserspaceSwapPolicy::GetLastVisibilityChangeTime(
     const PageNode* page_node) {
-  return page_node->GetTimeSinceLastVisibilityChange();
+  return page_node->GetLastVisibilityChangeTime();
 }
 
 bool UserspaceSwapPolicy::IsEligibleToSwap(const ProcessNode* process_node,
@@ -296,7 +296,7 @@
 
     // Next the page node must have been invisible for longer than the
     // configured time.
-    if (GetTimeSinceLastVisibilityChange(page_node) <
+    if ((now_ticks - GetLastVisibilityChangeTime(page_node)) <
         config_->invisible_time_before_swap) {
       return false;
     }
diff --git a/chrome/browser/performance_manager/policies/userspace_swap_policy_chromeos.h b/chrome/browser/performance_manager/policies/userspace_swap_policy_chromeos.h
index cbb02a6b..0f00794 100644
--- a/chrome/browser/performance_manager/policies/userspace_swap_policy_chromeos.h
+++ b/chrome/browser/performance_manager/policies/userspace_swap_policy_chromeos.h
@@ -73,7 +73,7 @@
   virtual bool IsPageNodeVisible(const PageNode* page_node);
   virtual bool IsPageNodeAudible(const PageNode* page_node);
   virtual bool IsPageNodeLoadingOrBusy(const PageNode* page_node);
-  virtual base::TimeDelta GetTimeSinceLastVisibilityChange(
+  virtual base::TimeTicks GetLastVisibilityChangeTime(
       const PageNode* page_node);
 
   // IsEligibleToSwap will return true if the |page_node| belonging to the
diff --git a/chrome/browser/performance_manager/policies/userspace_swap_policy_chromeos_unittest.cc b/chrome/browser/performance_manager/policies/userspace_swap_policy_chromeos_unittest.cc
index b28d7ede..db695d56 100644
--- a/chrome/browser/performance_manager/policies/userspace_swap_policy_chromeos_unittest.cc
+++ b/chrome/browser/performance_manager/policies/userspace_swap_policy_chromeos_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/compiler_specific.h"
 #include "base/memory/raw_ptr.h"
 #include "base/system/sys_info.h"
+#include "base/time/time.h"
 #include "chrome/browser/performance_manager/policies/policy_features.h"
 #include "chromeos/ash/components/memory/userspace_swap/userspace_swap.h"
 #include "components/performance_manager/graph/graph_impl.h"
@@ -53,8 +54,7 @@
   MOCK_METHOD1(IsPageNodeAudible, bool(const PageNode*));
   MOCK_METHOD1(IsPageNodeVisible, bool(const PageNode*));
   MOCK_METHOD1(IsPageNodeLoading, bool(const PageNode*));
-  MOCK_METHOD1(GetTimeSinceLastVisibilityChange,
-               base::TimeDelta(const PageNode*));
+  MOCK_METHOD1(GetLastVisibilityChangeTime, base::TimeTicks(const PageNode*));
 
   // Allow our mock to dispatch to default implementations.
   bool DefaultIsEligibleToSwap(const ProcessNode* process_node,
@@ -378,8 +378,8 @@
       .WillRepeatedly(Return(false));
   EXPECT_CALL(*policy(), IsPageNodeVisible(page_node().get()))
       .WillRepeatedly(Return(false));
-  EXPECT_CALL(*policy(), GetTimeSinceLastVisibilityChange(page_node().get()))
-      .WillRepeatedly(Return(base::TimeDelta::Max()));
+  EXPECT_CALL(*policy(), GetLastVisibilityChangeTime(page_node().get()))
+      .WillRepeatedly(Return(base::TimeTicks::Max()));
 
   EXPECT_CALL(*policy(), SwapNodesOnGraph())
       .WillRepeatedly(
diff --git a/chrome/browser/performance_manager/policies/working_set_trimmer_policy_chromeos.cc b/chrome/browser/performance_manager/policies/working_set_trimmer_policy_chromeos.cc
index 1f26047..6ed5029 100644
--- a/chrome/browser/performance_manager/policies/working_set_trimmer_policy_chromeos.cc
+++ b/chrome/browser/performance_manager/policies/working_set_trimmer_policy_chromeos.cc
@@ -164,7 +164,7 @@
   const base::TimeTicks now_ticks = base::TimeTicks::Now();
   for (const PageNode* page_node : GetOwningGraph()->GetAllPageNodes()) {
     if (!page_node->IsVisible() &&
-        page_node->GetTimeSinceLastVisibilityChange() >
+        (now_ticks - page_node->GetLastVisibilityChangeTime()) >
             params_.node_invisible_time) {
       // Get the process node and if it has not been
       // trimmed within the backoff period, we will do that
diff --git a/chrome/browser/persisted_state_db/session_proto_db_factory.h b/chrome/browser/persisted_state_db/session_proto_db_factory.h
index e97bc3a..138e163 100644
--- a/chrome/browser/persisted_state_db/session_proto_db_factory.h
+++ b/chrome/browser/persisted_state_db/session_proto_db_factory.h
@@ -25,13 +25,14 @@
 #endif
 
 namespace {
-const char kPersistedStateDBFolder[] = "persisted_state_db";
-const char kChromeCartDBFolder[] = "chrome_cart_db";
-const char kMerchantTrustSignalDBFolder[] = "merchant_signal_db";
-const char kCommerceSubscriptionDBFolder[] = "commerce_subscription_db";
-const char kCouponDBFolder[] = "coupon_db";
-const char kDiscountsDBFolder[] = "discounts_db";
-const char kParcelTrackingDBFolder[] = "parcel_tracking_db";
+inline constexpr char kPersistedStateDBFolder[] = "persisted_state_db";
+inline constexpr char kChromeCartDBFolder[] = "chrome_cart_db";
+inline constexpr char kMerchantTrustSignalDBFolder[] = "merchant_signal_db";
+inline constexpr char kCommerceSubscriptionDBFolder[] =
+    "commerce_subscription_db";
+inline constexpr char kCouponDBFolder[] = "coupon_db";
+inline constexpr char kDiscountsDBFolder[] = "discounts_db";
+inline constexpr char kParcelTrackingDBFolder[] = "parcel_tracking_db";
 }  // namespace
 
 SessionProtoDBFactory<persisted_state_db::PersistedStateContentProto>*
diff --git a/chrome/browser/policy/messaging_layer/util/reporting_server_connector_test_util.h b/chrome/browser/policy/messaging_layer/util/reporting_server_connector_test_util.h
index 14deda7..1b26eca 100644
--- a/chrome/browser/policy/messaging_layer/util/reporting_server_connector_test_util.h
+++ b/chrome/browser/policy/messaging_layer/util/reporting_server_connector_test_util.h
@@ -27,7 +27,7 @@
 
 namespace reporting {
 
-constexpr char kFakeDmToken[] = "FAKE_DM_TOKEN";
+inline constexpr char kFakeDmToken[] = "FAKE_DM_TOKEN";
 
 class EncryptedReportingClient;
 
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc
index 1bd7e64..de3d81a 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.cc
@@ -780,6 +780,8 @@
   }
   CHECK(!search_terms.empty());
 
+  RecordPotentialDuplicateSearchTermsAheadOfNavigationalPrefetch(search_terms);
+
   // Search history suggestions (those that are not also server suggestions)
   // don't have search term args. If search history suggestions are enabled,
   // generate search term args to get a prefetch URL.
@@ -1292,3 +1294,19 @@
   }
   search_terms_cache_.Put(search_terms, base::Time::Now());
 }
+
+void SearchPrefetchService::
+    RecordPotentialDuplicateSearchTermsAheadOfNavigationalPrefetch(
+        const std::u16string& search_terms) {
+  // Do not affect the order.
+  const auto& iter = search_terms_cache_.Peek(search_terms);
+  if (iter != search_terms_cache_.end()) {
+    // For now we just want to track the very recent duplicate terms which might
+    // be a bug.
+    base::UmaHistogramCustomTimes(
+        "Omnibox.SearchPrefetch."
+        "DuplicateSearchTermsAgeAheadOfNavigationalPrefetch",
+        base::Time::Now() - iter->second, base::Milliseconds(1),
+        base::Minutes(2), 50);
+  }
+}
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.h b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.h
index 6f2ca1aa..9d4d7dd 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.h
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service.h
@@ -274,6 +274,8 @@
 
   void RecordInterceptionMetrics(const std::u16string& search_terms,
                                  SearchPrefetchServingReason serving_status);
+  void RecordPotentialDuplicateSearchTermsAheadOfNavigationalPrefetch(
+      const std::u16string& search_terms);
 
   // Prefetches that are started are stored using search terms as a key. Only
   // one prefetch should be started for a given search term until the old
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_browsertest.cc b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_browsertest.cc
index 23cef01..ff58cd7 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_browsertest.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_browsertest.cc
@@ -3477,6 +3477,54 @@
   std::unique_ptr<ukm::TestAutoSetUkmRecorder> test_ukm_recorder_;
 };
 
+// Tests DuplicateSearchTermsAgeAheadOfNavigationalPrefetch is recorded as
+// expected.
+IN_PROC_BROWSER_TEST_F(
+    SearchPrefetchServiceNavigationPrefetchBrowserTest,
+    RecordDuplicateSearchTermsAgeAheadOfNavigationalPrefetch) {
+  SetDSEWithURL(
+      GetSearchServerQueryURL(
+          "{searchTerms}&{google:assistedQueryStats}{google:prefetchSource}"),
+      true);
+  base::HistogramTester histogram_tester;
+
+  auto* search_prefetch_service =
+      SearchPrefetchServiceFactory::GetForProfile(browser()->profile());
+  std::string search_terms = "terms of service";
+  std::string user_input = "terms";
+
+  auto [prefetch_url, search_url] =
+      GetSearchPrefetchAndNonPrefetch(search_terms);
+  ASSERT_TRUE(content::NavigateToURL(GetWebContents(), search_url));
+
+  AutocompleteMatch autocomplete_match =
+      CreateSearchSuggestionMatch(search_terms, search_terms, false);
+  SearchPrefetchServiceFactory::GetForProfile(browser()->profile())
+      ->OnNavigationLikely(1, autocomplete_match,
+                           NavigationPredictor::kMouseDown, GetWebContents());
+
+  WaitUntilStatusChangesTo(
+      GetCanonicalSearchURL(autocomplete_match.destination_url),
+      SearchPrefetchStatus::kComplete);
+  auto prefetch_status =
+      search_prefetch_service->GetSearchPrefetchStatusForTesting(
+          GetCanonicalSearchURL(autocomplete_match.destination_url));
+  ASSERT_TRUE(prefetch_status.has_value());
+  EXPECT_EQ(SearchPrefetchStatus::kComplete, prefetch_status.value());
+
+  GURL canonical_search_url = GetCanonicalSearchURL(prefetch_url);
+  // Navigate.
+  ASSERT_TRUE(content::NavigateToURL(GetWebContents(), search_url));
+
+  auto inner_html = GetDocumentInnerHTML();
+  EXPECT_FALSE(base::Contains(inner_html, "regular"));
+  EXPECT_TRUE(base::Contains(inner_html, "prefetch"));
+  histogram_tester.ExpectTotalCount(
+      "Omnibox.SearchPrefetch."
+      "DuplicateSearchTermsAgeAheadOfNavigationalPrefetch",
+      1);
+}
+
 IN_PROC_BROWSER_TEST_F(SearchPrefetchServiceNavigationPrefetchBrowserTest,
                        NavigationPrefetchIsServedMouseDown) {
   SetDSEWithURL(
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.cc b/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.cc
index a582336f..79f7b74 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.cc
+++ b/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.cc
@@ -18,6 +18,7 @@
 #include "base/task/sequenced_task_runner.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/time/time.h"
+#include "base/trace_event/named_trigger.h"
 #include "base/trace_event/trace_event.h"
 #include "chrome/browser/preloading/prefetch/search_prefetch/field_trial_settings.h"
 #include "chrome/browser/preloading/prerender/prerender_utils.h"
@@ -351,6 +352,7 @@
     scoped_refptr<StreamingSearchPrefetchURLLoader> loader) {
   DCHECK(!loader->streaming_prefetch_request_);
   DCHECK(!loader->forwarding_client_);
+  loader->should_be_serving_to_activation_navigation_ = true;
   loader->RecordInterceptionTime();
   return base::BindOnce(
       &StreamingSearchPrefetchURLLoader::SetUpForwardingClient,
@@ -371,6 +373,7 @@
     mojo::PendingReceiver<network::mojom::URLLoader> receiver,
     mojo::PendingRemote<network::mojom::URLLoaderClient> forwarding_client) {
   CHECK(!streaming_prefetch_request_);
+  CHECK(should_be_serving_to_activation_navigation_);
   // Bind to the content/ navigation code.
   CHECK(!receiver_.is_bound());
 
@@ -837,6 +840,11 @@
             : "Omnibox.SearchPreload.ForwardingResult.NotServedToPrerender",
         forwarding_result_);
   }
+  if (should_be_serving_to_activation_navigation_ &&
+      forwarding_result_ == ForwardingResult::kNotServed) {
+    base::trace_event::EmitNamedTrigger(
+        "search-prefetch-destroyed-unexpectedly");
+  }
 
   // To avoid UAF bugs, post a separate task to delete this object.
   base::SequencedTaskRunner::GetCurrentDefault()->ReleaseSoon(
diff --git a/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.h b/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.h
index ee59e30..0e352c6 100644
--- a/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.h
+++ b/chrome/browser/preloading/prefetch/search_prefetch/streaming_search_prefetch_url_loader.h
@@ -437,7 +437,12 @@
   // Whether this loader is created specifically for a navigation prefetch.
   bool navigation_prefetch_;
 
-  // Whether this url loader was activated via the navigation stack.
+  // Whether the SearchPrefetchService selected to use this loader to serve a
+  // real navigation.
+  bool should_be_serving_to_activation_navigation_ = false;
+
+  // Whether this url loader was activated via the navigation stack. Set after
+  // the call to `SetUpForwardingClient`.
   bool is_activated_ = false;
 
   base::OnceClosure on_destruction_callback_for_testing_;
diff --git a/chrome/browser/printing/web_api/web_printing_browsertest.cc b/chrome/browser/printing/web_api/web_printing_browsertest.cc
index 358e2d0..56df0a4 100644
--- a/chrome/browser/printing/web_api/web_printing_browsertest.cc
+++ b/chrome/browser/printing/web_api/web_printing_browsertest.cc
@@ -140,6 +140,8 @@
                                       Pointee(Eq("tray-1")))))),
       // printColorMode:
       Property(&PrintSettings::color, Eq(mojom::ColorModel::kColorModeColor)),
+      // printQuality:
+      Property(&PrintSettings::quality, Eq(mojom::Quality::kUnknownQuality)),
       Property(&PrintSettings::title, Eq(u"Title")),
       // multipleDocumentHandling:
       Property(&PrintSettings::collate, Eq(true)),
@@ -335,6 +337,8 @@
     }],
     "printColorModeDefault": "monochrome",
     "printColorModeSupported": [ "monochrome", "color" ],
+    "printQualityDefault": "normal",
+    "printQualitySupported": [ "draft", "normal", "high" ],
     "printerName": "name",
     "printerState": "idle",
     "printerStateMessage": "Ready to Print!",
diff --git a/chrome/browser/printing/web_api/web_printing_mojom_traits.cc b/chrome/browser/printing/web_api/web_printing_mojom_traits.cc
index 9f9d4449..ae0f01c 100644
--- a/chrome/browser/printing/web_api/web_printing_mojom_traits.cc
+++ b/chrome/browser/printing/web_api/web_printing_mojom_traits.cc
@@ -28,6 +28,10 @@
 // orientation-requested:
 using OrientationRequested = blink::mojom::WebPrintingOrientationRequested;
 
+// print-quality:
+using WebPrintQuality = blink::mojom::WebPrintQuality;
+using printing::mojom::Quality;
+
 // print-color-mode:
 using PrintColorMode = blink::mojom::WebPrintColorMode;
 using printing::mojom::ColorModel;
@@ -118,6 +122,39 @@
 }
 
 // static
+blink::mojom::WebPrintQuality EnumTraits<WebPrintQuality, Quality>::ToMojom(
+    Quality input) {
+  switch (input) {
+    case Quality::kDraft:
+      return WebPrintQuality::kDraft;
+    case Quality::kNormal:
+      return WebPrintQuality::kNormal;
+    case Quality::kHigh:
+      return WebPrintQuality::kHigh;
+    case Quality::kUnknownQuality:
+      return WebPrintQuality::kNormal;
+  }
+  NOTREACHED();
+}
+
+// static
+bool EnumTraits<WebPrintQuality, Quality>::FromMojom(WebPrintQuality input,
+                                                     Quality* output) {
+  switch (input) {
+    case WebPrintQuality::kDraft:
+      *output = Quality::kDraft;
+      return true;
+    case WebPrintQuality::kNormal:
+      *output = Quality::kNormal;
+      return true;
+    case WebPrintQuality::kHigh:
+      *output = Quality::kHigh;
+      return true;
+  }
+  NOTREACHED();
+}
+
+// static
 blink::mojom::WebPrinterState
 EnumTraits<blink::mojom::WebPrinterState, ipp_pstate_t>::ToMojom(
     ipp_pstate_t printer_state) {
@@ -271,6 +308,15 @@
   if (auto print_color_mode = data.print_color_mode()) {
     settings->set_color(PrintColorModeToColorModel(*print_color_mode));
   }
+  {
+    std::optional<Quality> quality;
+    if (!data.ReadPrintQuality(&quality)) {
+      return false;
+    }
+    if (quality) {
+      settings->set_quality(*quality);
+    }
+  }
 
   *out = std::move(settings);
   return true;
diff --git a/chrome/browser/printing/web_api/web_printing_mojom_traits.h b/chrome/browser/printing/web_api/web_printing_mojom_traits.h
index edac760f..a5affce6 100644
--- a/chrome/browser/printing/web_api/web_printing_mojom_traits.h
+++ b/chrome/browser/printing/web_api/web_printing_mojom_traits.h
@@ -29,6 +29,13 @@
 };
 
 template <>
+struct EnumTraits<blink::mojom::WebPrintQuality, printing::mojom::Quality> {
+  static blink::mojom::WebPrintQuality ToMojom(printing::mojom::Quality input);
+  static bool FromMojom(blink::mojom::WebPrintQuality input,
+                        printing::mojom::Quality* output);
+};
+
+template <>
 struct EnumTraits<blink::mojom::WebPrinterState, ipp_pstate_t> {
   static blink::mojom::WebPrinterState ToMojom(ipp_pstate_t input);
   static bool FromMojom(blink::mojom::WebPrinterState input,
@@ -93,6 +100,10 @@
       const std::unique_ptr<printing::PrintSettings>& ptr) {
     NOTREACHED();
   }
+  static const std::optional<blink::mojom::WebPrintQuality>& print_quality(
+      const std::unique_ptr<printing::PrintSettings>& ptr) {
+    NOTREACHED();
+  }
   static const std::optional<blink::mojom::WebPrintingSides>& sides(
       const std::unique_ptr<printing::PrintSettings>& ptr) {
     NOTREACHED();
diff --git a/chrome/browser/printing/web_api/web_printing_type_converters.cc b/chrome/browser/printing/web_api/web_printing_type_converters.cc
index 6842a74..20be313 100644
--- a/chrome/browser/printing/web_api/web_printing_type_converters.cc
+++ b/chrome/browser/printing/web_api/web_printing_type_converters.cc
@@ -163,6 +163,14 @@
   }
 }
 
+void ProcessPrintQuality(const PrinterSemanticCapsAndDefaults& caps,
+                         blink::mojom::WebPrinterAttributes* attributes) {
+  attributes->print_quality_default = blink::mojom::WebPrintQuality::kNormal;
+  attributes->print_quality_supported = {blink::mojom::WebPrintQuality::kDraft,
+                                         blink::mojom::WebPrintQuality::kNormal,
+                                         blink::mojom::WebPrintQuality::kHigh};
+}
+
 void ProcessSides(const PrinterSemanticCapsAndDefaults& caps,
                   blink::mojom::WebPrinterAttributes* attributes) {
   if (caps.duplex_default != mojom::DuplexMode::kUnknownDuplexMode) {
@@ -201,6 +209,7 @@
   printing::ProcessOrientationRequested(capabilities, attributes.get());
   printing::ProcessPrinterResolution(capabilities, attributes.get());
   printing::ProcessPrintColorMode(capabilities, attributes.get());
+  printing::ProcessPrintQuality(capabilities, attributes.get());
   printing::ProcessSides(capabilities, attributes.get());
 
   return attributes;
diff --git a/chrome/browser/privacy_sandbox/BUILD.gn b/chrome/browser/privacy_sandbox/BUILD.gn
index 21ddfda5f..fe578739 100644
--- a/chrome/browser/privacy_sandbox/BUILD.gn
+++ b/chrome/browser/privacy_sandbox/BUILD.gn
@@ -153,6 +153,26 @@
       "//net:test_support",
     ]
   }
+} else {
+  source_set("attestations_component_installer_android_browsertest") {
+    testonly = true
+    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+    sources = [ "privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_android_browsertest.cc" ]
+    deps = [
+      "//base/test:test_support",
+      "//chrome/browser:browser_process",
+      "//chrome/test:test_support",
+      "//chrome/test:test_support_ui_android",
+      "//components/component_updater:component_updater_paths",
+      "//components/privacy_sandbox:features",
+      "//components/privacy_sandbox:test_support",
+      "//components/privacy_sandbox/privacy_sandbox_attestations",
+      "//components/privacy_sandbox/privacy_sandbox_attestations:metrics",
+      "//components/privacy_sandbox/privacy_sandbox_attestations/preload",
+      "//components/privacy_sandbox/privacy_sandbox_attestations/proto",
+      "//content/test:test_support",
+    ]
+  }
 }
 
 group("browser_tests") {
@@ -169,6 +189,8 @@
       ":settings_browser_test",
       ":survey_desktop_controller_browser_test",
     ]
+  } else {
+    deps += [ ":attestations_component_installer_android_browsertest" ]
   }
 }
 
diff --git a/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc b/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc
index f2ee1381..8072afbe 100644
--- a/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc
+++ b/chrome/browser/privacy_sandbox/privacy_sandbox_service_impl_unittest.cc
@@ -1791,11 +1791,9 @@
       kRelatedWebsiteSetsVersion,
       {
           {primary_site,
-           {net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary,
-                                    std::nullopt)}},
+           {net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary)}},
           {associate1_site,
-           {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated,
-                                    0)}},
+           {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated)}},
       },
       {});
 
@@ -1831,11 +1829,9 @@
       kRelatedWebsiteSetsVersion,
       {
           {primary_site,
-           {net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary,
-                                    std::nullopt)}},
+           {net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary)}},
           {associate1_site,
-           {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated,
-                                    0)}},
+           {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated)}},
       },
       {});
 
@@ -1872,11 +1868,9 @@
       kRelatedWebsiteSetsVersion,
       {
           {primary_site,
-           {net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary,
-                                    std::nullopt)}},
+           {net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary)}},
           {associate1_site,
-           {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated,
-                                    0)}},
+           {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated)}},
       },
       {});
 
@@ -1955,14 +1949,11 @@
       kRelatedWebsiteSetsVersion,
       {
           {primary_site,
-           {net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary,
-                                    std::nullopt)}},
+           {net::FirstPartySetEntry(primary_site, net::SiteType::kPrimary)}},
           {associate1_site,
-           {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated,
-                                    0)}},
+           {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated)}},
           {associate2_site,
-           {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated,
-                                    1)}},
+           {net::FirstPartySetEntry(primary_site, net::SiteType::kAssociated)}},
       },
       {}));
 
@@ -2069,10 +2060,10 @@
       {
           {youtube_primary_site,
            {net::FirstPartySetEntry(youtube_primary_site,
-                                    net::SiteType::kPrimary, std::nullopt)}},
+                                    net::SiteType::kPrimary)}},
           {youtube_site,
            {net::FirstPartySetEntry(youtube_primary_site,
-                                    net::SiteType::kAssociated, 0)}},
+                                    net::SiteType::kAssociated)}},
       },
       {}));
 
@@ -2082,7 +2073,7 @@
           {{net::SchemefulSite(GURL("https://google.de")),
             net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
                 net::SchemefulSite(GURL("https://new-primary.test")),
-                net::SiteType::kAssociated, 0))}})
+                net::SiteType::kAssociated))}})
           .value());
 
   first_party_sets_policy_service()->InitForTesting();
diff --git a/chrome/browser/private_network_access/private_network_device_permission_context.h b/chrome/browser/private_network_access/private_network_device_permission_context.h
index f64dc8f..1dd88d879 100644
--- a/chrome/browser/private_network_access/private_network_device_permission_context.h
+++ b/chrome/browser/private_network_access/private_network_device_permission_context.h
@@ -17,9 +17,9 @@
 
 class Profile;
 
-const char kPrivateNetworkDeviceValidityHistogramName[] =
+inline constexpr char kPrivateNetworkDeviceValidityHistogramName[] =
     "Security.PrivateNetworkAccess.PermissionDeviceValidity";
-const char kUserAcceptedPrivateNetworkDeviceHistogramName[] =
+inline constexpr char kUserAcceptedPrivateNetworkDeviceHistogramName[] =
     "Security.PrivateNetworkAccess.PermissionNewAcceptedDeviceType";
 
 // These values are logged to UMA. Entries should not be renumbered and numeric
diff --git a/chrome/browser/resources/ash/settings/os_about_page/about_page_browser_proxy.ts b/chrome/browser/resources/ash/settings/os_about_page/about_page_browser_proxy.ts
index dc5b6832..715c9f7 100644
--- a/chrome/browser/resources/ash/settings/os_about_page/about_page_browser_proxy.ts
+++ b/chrome/browser/resources/ash/settings/os_about_page/about_page_browser_proxy.ts
@@ -200,6 +200,8 @@
    */
   getChannelInfo(): Promise<ChannelInfo>;
 
+  canChangeFirmware(): Promise<boolean>;
+
   canChangeChannel(): Promise<boolean>;
 
   getVersionInfo(): Promise<VersionInfo>;
@@ -324,6 +326,10 @@
     return sendWithPromise('getChannelInfo');
   }
 
+  canChangeFirmware(): Promise<boolean> {
+    return sendWithPromise('canChangeFirmware');
+  }
+
   canChangeChannel(): Promise<boolean> {
     return sendWithPromise('canChangeChannel');
   }
diff --git a/chrome/browser/resources/ash/settings/os_about_page/os_about_page.html b/chrome/browser/resources/ash/settings/os_about_page/os_about_page.html
index fae2123..165e992 100644
--- a/chrome/browser/resources/ash/settings/os_about_page/os_about_page.html
+++ b/chrome/browser/resources/ash/settings/os_about_page/os_about_page.html
@@ -253,6 +253,7 @@
             external
             deep-link-focus-id$="[[Setting.kDiagnostics]]">
         </cr-link-row>
+        <template is="dom-if" if="[[canChangeFirmware_]]">
         <cr-link-row class="hr" id="firmwareUpdates"
             start-icon="os-settings:about-firmware-updates"
             on-click="onFirmwareUpdatesClick_"
@@ -272,6 +273,32 @@
                 firmwareUpdateCount_)]]">
           </div>
         </cr-link-row>
+        </template>
+        <template is="dom-if" if="[[!canChangeFirmware_]]">
+        <cr-link-row class="hr" id="firmwareUpdates"
+            start-icon="os-settings:about-firmware-updates"
+            label="$i18n{aboutFirmwareUpdates}"
+            sub-label="[[getFirmwareSublabel_(firmwareUpdateCount_)]]"
+            external
+            using-slotted-label
+            disabled=true
+            deep-link-focus-id$="[[Setting.kFirmwareUpdates]]">
+          <iron-icon id="firmwareUpdateBadge"
+              icon$="[[getFirmwareUpdatesIcon_(firmwareUpdateCount_)]]"
+              hidden$="[[!shouldShowFirmwareUpdatesBadge_(
+                firmwareUpdateCount_)]]">
+          </iron-icon>
+          <div id="firmwareUpdateBadgeSeparator"
+              class="separator separator-firmware-updates-badge"
+              hidden$="[[!shouldShowFirmwareUpdatesBadge_(
+                firmwareUpdateCount_)]]">
+          </div>
+          <cr-policy-indicator
+            id="changeChannelPolicyIndicator"
+            indicator-type="[[getFirmwareDisabledIndicatorType_()]]">
+           </cr-policy-indicator>
+        </cr-link-row>
+        </template>
         <cr-link-row class="hr" id="detailedBuildInfoTrigger"
             start-icon="os-settings:about-additional-details"
             on-click="onDetailedBuildInfoClick_"
diff --git a/chrome/browser/resources/ash/settings/os_about_page/os_about_page.ts b/chrome/browser/resources/ash/settings/os_about_page/os_about_page.ts
index 9aefa3583..23dcea3 100644
--- a/chrome/browser/resources/ash/settings/os_about_page/os_about_page.ts
+++ b/chrome/browser/resources/ash/settings/os_about_page/os_about_page.ts
@@ -23,10 +23,12 @@
 import './eol_offer_section.js';
 import './update_warning_dialog.js';
 import '../crostini_page/crostini_settings_card.js';
+import 'chrome://resources/ash/common/cr_elements/policy/cr_policy_indicator.js';
 
 import {LifetimeBrowserProxyImpl} from '/shared/settings/lifetime_browser_proxy.js';
 import type {CrButtonElement} from 'chrome://resources/ash/common/cr_elements/cr_button/cr_button.js';
 import {I18nMixin} from 'chrome://resources/ash/common/cr_elements/i18n_mixin.js';
+import {CrPolicyIndicatorType} from 'chrome://resources/ash/common/cr_elements/policy/cr_policy_indicator_mixin.js';
 import {WebUiListenerMixin} from 'chrome://resources/ash/common/cr_elements/web_ui_listener_mixin.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
@@ -92,6 +94,11 @@
         value: false,
       },
 
+      /**
+       * Whether users may initiate firmware updates
+       */
+      canChangeFirmware_: Boolean,
+
       currentUpdateStatusEvent_: {
         type: Object,
         value: {
@@ -288,6 +295,7 @@
   ]);
 
   private isDarkModeActive_: boolean;
+  private canChangeFirmware_: boolean;
   private currentUpdateStatusEvent_: UpdateStatusChangedEvent;
   private isManaged_: boolean;
   private deviceManager_: string;
@@ -415,6 +423,12 @@
         this.onExtendedUpdatesSettingChanged_.bind(this));
   }
 
+  private updateFirmwareInfo_(): void {
+    this.aboutBrowserProxy_.canChangeFirmware().then(canChangeFirmware => {
+      this.canChangeFirmware_ = canChangeFirmware;
+    });
+  }
+
   private onUpdateStatusChanged_(event: UpdateStatusChangedEvent): void {
     if (event.status === UpdateStatus.CHECKING) {
       this.hasCheckedForUpdates_ = true;
@@ -489,6 +503,10 @@
         this.currentUpdateStatusEvent_.status !== UpdateStatus.DISABLED;
   }
 
+  private getFirmwareDisabledIndicatorType_(): string {
+    return CrPolicyIndicatorType.DEVICE_POLICY;
+  }
+
   /**
    * Hide the button container if all buttons are hidden, otherwise the
    * container displays an unwanted border (see separator class).
@@ -755,6 +773,7 @@
   private onTpmFirmwareUpdateStatusChanged_(
       event: TpmFirmwareUpdateStatusChangedEvent): void {
     this.showTPMFirmwareUpdateLineItem_ = event.updateAvailable;
+    this.updateFirmwareInfo_();
   }
 
   private onTpmFirmwareUpdateClick_(): void {
@@ -787,9 +806,13 @@
   // </if>
 
   private getFirmwareSublabel_(): string|null {
-    return this.firmwareUpdateCount_ > 0 ?
-        this.i18n('aboutFirmwareUpdateAvailableDescription') :
-        this.i18n('aboutFirmwareUpToDateDescription');
+    if (!this.canChangeFirmware_) {
+      return this.i18n('aboutFirmwareUpdatesDisabledDescription');
+    }
+    if (this.firmwareUpdateCount_ > 0) {
+      return this.i18n('aboutFirmwareUpdateAvailableDescription');
+    }
+    return this.i18n('aboutFirmwareUpToDateDescription');
   }
 
   private computeShowExtendedUpdatesOption_(): boolean {
diff --git a/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_clear_ime_data_dialog.html b/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_clear_ime_data_dialog.html
index ea267ae..cc18f46 100644
--- a/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_clear_ime_data_dialog.html
+++ b/chrome/browser/resources/ash/settings/os_languages_page/os_japanese_clear_ime_data_dialog.html
@@ -25,23 +25,23 @@
   }
 </style>
 <cr-dialog id="dialog" close-text="$i18n{close}" show-on-attach>
-  <div slot="title">Clear personalization data</div>
+  <div slot="title">$i18n{japaneseClearPersonalizationData}</div>
   <div id="dialogBody" slot="body">
-    <div class="subheading">Delete the following items:</div>
+    <div class="subheading">$i18n{japaneseDeleteItems}</div>
     <div>
       <cr-checkbox class="list-item no-outline is-width-of-dialog"
         checked="{{clearConversionHistory_}}">
-        Conversion History
+        $i18n{japaneseConversationHistory}
       </cr-checkbox>
       <cr-checkbox class="list-item no-outline is-width-of-dialog"
         checked="{{clearSuggestionHistory_}}">
-        Suggestion History
+        $i18n{japaneseSuggestionHistory}
       </cr-checkbox>
     </div>
   </div>
   <div slot="button-container">
     <cr-button class="action-button" on-click="onClearButtonClick_">
-      Clear Personalized data
+      $i18n{japaneseClearPersonalizationData}
     </cr-button>
     <cr-button class="cancel-button" on-click="onCancelButtonClick_">
       $i18n{cancel}
diff --git a/chrome/browser/resources/bookmarks/bookmark_manager_api_proxy.ts b/chrome/browser/resources/bookmarks/bookmark_manager_api_proxy.ts
index 9ed38991..8b61f59 100644
--- a/chrome/browser/resources/bookmarks/bookmark_manager_api_proxy.ts
+++ b/chrome/browser/resources/bookmarks/bookmark_manager_api_proxy.ts
@@ -3,11 +3,6 @@
 // found in the LICENSE file.
 import type {ChromeEvent} from '/tools/typescript/definitions/chrome_event.js';
 
-export interface OpenInNewTabParams {
-  active?: boolean;
-  split?: boolean;
-}
-
 export interface BookmarkManagerApiProxy {
   onDragEnter:
       ChromeEvent<(p1: chrome.bookmarkManagerPrivate.DragData) => void>;
@@ -19,7 +14,7 @@
   removeTrees(idList: string[]): Promise<void>;
   canPaste(parentId: string): Promise<boolean>;
   openInNewWindow(idList: string[], incognito: boolean): void;
-  openInNewTab(id: string, params?: OpenInNewTabParams): void;
+  openInNewTab(id: string, active: boolean): void;
   openInNewTabGroup(idList: string[]): void;
   cut(idList: string[]): Promise<void>;
   paste(parentId: string, selectedIdList?: string[]): Promise<void>;
@@ -49,12 +44,11 @@
   }
 
   openInNewWindow(idList: string[], incognito: boolean) {
-    chrome.bookmarkManagerPrivate.openInNewWindow(idList, incognito);
+    return chrome.bookmarkManagerPrivate.openInNewWindow(idList, incognito);
   }
 
-  openInNewTab(id: string, params: OpenInNewTabParams) {
-    chrome.bookmarkManagerPrivate.openInNewTab(
-        id, {active: params.active, split: params.split});
+  openInNewTab(id: string, active: boolean) {
+    return chrome.bookmarkManagerPrivate.openInNewTab(id, active);
   }
 
   openInNewTabGroup(idList: string[]) {
diff --git a/chrome/browser/resources/bookmarks/bookmarks.ts b/chrome/browser/resources/bookmarks/bookmarks.ts
index 0f98b33..87e30696 100644
--- a/chrome/browser/resources/bookmarks/bookmarks.ts
+++ b/chrome/browser/resources/bookmarks/bookmarks.ts
@@ -8,7 +8,7 @@
 export {changeFolderOpen, clearSearch, createBookmark, deselectItems, editBookmark, moveBookmark, removeBookmark, reorderChildren, selectFolder, SelectFolderAction, selectItem, SelectItemsAction, setSearchResults, setSearchTerm, StartSearchAction, updateAnchor} from './actions.js';
 export {setDebouncerForTesting} from './api_listener.js';
 export {BookmarksAppElement, HIDE_FOCUS_RING_ATTRIBUTE} from './app.js';
-export {BookmarkManagerApiProxy, BookmarkManagerApiProxyImpl, OpenInNewTabParams} from './bookmark_manager_api_proxy.js';
+export {BookmarkManagerApiProxy, BookmarkManagerApiProxyImpl} from './bookmark_manager_api_proxy.js';
 export {BookmarksApiProxy, BookmarksApiProxyImpl, Query} from './bookmarks_api_proxy.js';
 export {BrowserProxy, BrowserProxyImpl} from './browser_proxy.js';
 export {BookmarksCommandManagerElement} from './command_manager.js';
diff --git a/chrome/browser/resources/bookmarks/command_manager.ts b/chrome/browser/resources/bookmarks/command_manager.ts
index 70bf956..7f5e9b3 100644
--- a/chrome/browser/resources/bookmarks/command_manager.ts
+++ b/chrome/browser/resources/bookmarks/command_manager.ts
@@ -257,7 +257,6 @@
       case Command.OPEN_NEW_GROUP:
       case Command.OPEN_NEW_TAB:
       case Command.OPEN_NEW_WINDOW:
-      case Command.OPEN_SPLIT_VIEW:
         return itemIds.size > 0;
       case Command.ADD_BOOKMARK:
       case Command.ADD_FOLDER:
@@ -287,8 +286,6 @@
         return this.expandIds_(itemIds).length > 0 &&
             state.prefs.incognitoAvailability !==
             IncognitoAvailability.DISABLED;
-      case Command.OPEN_SPLIT_VIEW:
-        return this.expandIds_(itemIds).length === 1;
       case Command.SORT:
         return this.canChangeList_() &&
             state.nodes[state.selectedFolder]!.children!.length > 1;
@@ -403,7 +400,6 @@
       case Command.OPEN_NEW_GROUP:
       case Command.OPEN_NEW_TAB:
       case Command.OPEN_NEW_WINDOW:
-      case Command.OPEN_SPLIT_VIEW:
         this.openBookmarkIds_(this.expandIds_(itemIds), command);
         break;
       case Command.OPEN:
@@ -524,34 +520,27 @@
         command === Command.OPEN || command === Command.OPEN_NEW_TAB ||
         command === Command.OPEN_NEW_WINDOW ||
         command === Command.OPEN_INCOGNITO ||
-        command === Command.OPEN_SPLIT_VIEW ||
         command === Command.OPEN_NEW_GROUP);
 
     if (ids.length === 0) {
       return;
     }
 
-    if (command === Command.OPEN_SPLIT_VIEW) {
-      assert(ids.length === 1);
-    }
-
     const openBookmarkIdsCallback = function() {
       const incognito = command === Command.OPEN_INCOGNITO;
       if (command === Command.OPEN_NEW_WINDOW || incognito) {
         BookmarkManagerApiProxyImpl.getInstance().openInNewWindow(
             ids, incognito);
-      } else if (command === Command.OPEN_SPLIT_VIEW) {
-        BookmarkManagerApiProxyImpl.getInstance().openInNewTab(
-            ids.shift()!, {active: false, split: true});
       } else if (command === Command.OPEN_NEW_GROUP) {
         BookmarkManagerApiProxyImpl.getInstance().openInNewTabGroup(ids);
       } else {
         if (command === Command.OPEN) {
-          BookmarkManagerApiProxyImpl.getInstance().openInNewTab(ids.shift()!);
+          BookmarkManagerApiProxyImpl.getInstance().openInNewTab(
+              ids.shift()!, /*active=*/ true);
         }
         ids.forEach(function(id) {
           BookmarkManagerApiProxyImpl.getInstance().openInNewTab(
-              id, {active: false});
+              id, /*active=*/ false);
         });
       }
     };
@@ -663,9 +652,6 @@
       case Command.HELP_CENTER:
         label = 'menuHelpCenter';
         break;
-      case Command.OPEN_SPLIT_VIEW:
-        label = 'menuOpenSplitView';
-        break;
     }
     if (label !== null) {
       return loadTimeData.getString(label);
@@ -715,7 +701,7 @@
     switch (this.menuSource_) {
       case MenuSource.ITEM:
       case MenuSource.TREE:
-        const commands = [
+        return [
           Command.EDIT,
           Command.SHOW_IN_FOLDER,
           Command.DELETE,
@@ -729,10 +715,6 @@
           Command.OPEN_NEW_TAB,
           Command.OPEN_NEW_WINDOW,
         ];
-        if (loadTimeData.getBoolean('splitViewEnabled')) {
-          commands.push(Command.OPEN_SPLIT_VIEW);
-        }
-        return commands;
       case MenuSource.TOOLBAR:
         return [
           Command.SORT,
diff --git a/chrome/browser/resources/bookmarks/constants.ts b/chrome/browser/resources/bookmarks/constants.ts
index 7eaef0c..b38e604 100644
--- a/chrome/browser/resources/bookmarks/constants.ts
+++ b/chrome/browser/resources/bookmarks/constants.ts
@@ -46,12 +46,10 @@
   OPEN_BOOKMARK = 21,
   OPEN_FOLDER = 22,
 
-  OPEN_SPLIT_VIEW = 23,
-
-  OPEN_NEW_GROUP = 24,
+  OPEN_NEW_GROUP = 23,
 
   // Append new values to the end of the enum.
-  MAX_VALUE = 25,
+  MAX_VALUE = 24,
 }
 
 /**
diff --git a/chrome/browser/resources/new_tab_footer/app.css b/chrome/browser/resources/new_tab_footer/app.css
index 7b2b17a..279ea37 100644
--- a/chrome/browser/resources/new_tab_footer/app.css
+++ b/chrome/browser/resources/new_tab_footer/app.css
@@ -19,7 +19,8 @@
 }
 
 #centerContainer > div > p,
-#centerContainer > div > a {
+#centerContainer > div > a,
+#managementNoticeText > p {
   color: var(--color-new-tab-footer-text);
   display: inline-block;
   font-size: 13px;
diff --git a/chrome/browser/resources/new_tab_footer/app.html.ts b/chrome/browser/resources/new_tab_footer/app.html.ts
index 1c81add..f1810a6 100644
--- a/chrome/browser/resources/new_tab_footer/app.html.ts
+++ b/chrome/browser/resources/new_tab_footer/app.html.ts
@@ -14,10 +14,8 @@
 separated from each other by a divider.
 -->
 <div id="centerContainer">
-  ${!this.extensionAttribution_ ?
-      html`<!-- TODO(crbug.com/409056431): Remove #example-div once actual
-      elements added. This is used as a placeholder. -->
-      <div id="example-div"><p>${this.message_}</p></div>` : ''}
+  ${this.managementNotice_ ?
+      html`<div id="managementNoticeText"><p>${this.managementNotice_.text}</p></div>` : ''}
   ${this.extensionAttribution_ ?
       html`<div id="extensionAttribution">
         <a href="${this.extensionAttribution_.url}">
diff --git a/chrome/browser/resources/new_tab_footer/app.ts b/chrome/browser/resources/new_tab_footer/app.ts
index b2a0ea47..cc87468 100644
--- a/chrome/browser/resources/new_tab_footer/app.ts
+++ b/chrome/browser/resources/new_tab_footer/app.ts
@@ -4,14 +4,14 @@
 
 import '/strings.m.js';
 
+import {assert} from '//resources/js/assert.js';
 import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js';
 import {ColorChangeUpdater} from 'chrome://resources/cr_components/color_change_listener/colors_css_updater.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 
 import {getCss} from './app.css.js';
 import {getHtml} from './app.html.js';
 import {NewTabFooterDocumentProxy} from './browser_proxy.js';
-import type {ExtensionAttribution} from './new_tab_footer.mojom-webui.js';
+import type {ExtensionAttribution, ManagementNotice, NewTabFooterDocumentCallbackRouter} from './new_tab_footer.mojom-webui.js';
 
 export class NewTabFooterAppElement extends CrLitElement {
   static get is() {
@@ -29,17 +29,20 @@
   static override get properties() {
     return {
       extensionAttribution_: {type: Object},
-      message_: {type: String},
+      managementNotice_: {type: Object},
     };
   }
 
+
   protected accessor extensionAttribution_: ExtensionAttribution|null = null;
-  // TODO(crbug.com/409056431): Remove `message_` once relevant
-  // variables/properties are added. This is used as a placeholder.
-  protected accessor message_: string = loadTimeData.getString('message');
+  protected accessor managementNotice_: ManagementNotice|null = null;
+  private callbackRouter_: NewTabFooterDocumentCallbackRouter;
+  private setManagementNoticeListener_: number|null = null;
 
   constructor() {
     super();
+    this.callbackRouter_ =
+        NewTabFooterDocumentProxy.getInstance().callbackRouter;
     this.getNtpExtensionAttribution_();
   }
 
@@ -47,6 +50,24 @@
     ColorChangeUpdater.forDocument().start();
   }
 
+  override connectedCallback() {
+    super.connectedCallback();
+    this.setManagementNoticeListener_ =
+        this.callbackRouter_.setManagementNotice.addListener(
+            (notice: ManagementNotice) => {
+              if (notice) {
+                this.managementNotice_ = notice;
+              }
+            });
+    NewTabFooterDocumentProxy.getInstance().handler.updateManagementNotice();
+  }
+
+  override disconnectedCallback() {
+    super.disconnectedCallback();
+    assert(this.setManagementNoticeListener_);
+    this.callbackRouter_.removeListener(this.setManagementNoticeListener_);
+  }
+
   private async getNtpExtensionAttribution_() {
     this.extensionAttribution_ = (await NewTabFooterDocumentProxy.getInstance()
                                       .handler.getNtpExtensionAttribution())
diff --git a/chrome/browser/resources/new_tab_page/app.css b/chrome/browser/resources/new_tab_page/app.css
index 39980d7..2b3c866 100644
--- a/chrome/browser/resources/new_tab_page/app.css
+++ b/chrome/browser/resources/new_tab_page/app.css
@@ -120,11 +120,6 @@
   position: relative;
 }
 
-ntp-modules {
-  flex-shrink: 0;
-  width: var(--ntp-module-layout-width);
-}
-
 #modules:not([hidden]) {
   /* We use animation instead of transition to allow a fade-in out of
      display: none. */
diff --git a/chrome/browser/resources/new_tab_page/app.html b/chrome/browser/resources/new_tab_page/app.html
index f7b589a..1218fff 100644
--- a/chrome/browser/resources/new_tab_page/app.html
+++ b/chrome/browser/resources/new_tab_page/app.html
@@ -55,13 +55,13 @@
     ` : ''}
     ${this.modulesEnabled_ ? html`
       ${html`
-        <ntp-modules-v2 id="modules"
+        <ntp-modules id="modules"
             ?modules-shown-to-user="${this.modulesShownToUser}"
             @modules-shown-to-user-changed="${this.onModulesShownToUserChanged_}"
             @customize-module="${this.onCustomizeModule_}"
             @modules-loaded="${this.onModulesLoaded_}"
             ?hidden="${!this.promoAndModulesLoaded_}">
-        </ntp-modules-v2>
+        </ntp-modules>
       `}
     ` : ''}
     <a id="backgroundImageAttribution"
diff --git a/chrome/browser/resources/new_tab_page/lazy_load.ts b/chrome/browser/resources/new_tab_page/lazy_load.ts
index a4baca4..fb9470a 100644
--- a/chrome/browser/resources/new_tab_page/lazy_load.ts
+++ b/chrome/browser/resources/new_tab_page/lazy_load.ts
@@ -25,7 +25,7 @@
 export {InitializeModuleCallback, Module, ModuleDescriptor} from './modules/module_descriptor.js';
 export {counterfactualLoad} from './modules/module_descriptors.js';
 export {ModuleRegistry} from './modules/module_registry.js';
-export {ModuleWrapperElement} from './modules/module_wrapper.js';
+export {ModuleInstance, ModuleWrapperElement} from './modules/module_wrapper.js';
 export {microsoftAuthModuleDescriptor, MicrosoftAuthModuleElement} from './modules/v2/authentication/microsoft_auth_module.js';
 export {MicrosoftAuthProxyImpl} from './modules/v2/authentication/microsoft_auth_module_proxy.js';
 export {CalendarElement} from './modules/v2/calendar/calendar.js';
@@ -45,7 +45,7 @@
 export {microsoftFilesModuleDescriptor, MicrosoftFilesModuleElement} from './modules/v2/file_suggestion/microsoft_files_module.js';
 export {MicrosoftFilesProxyImpl} from './modules/v2/file_suggestion/microsoft_files_proxy.js';
 export {ModuleHeaderElement as ModuleHeaderElementV2} from './modules/v2/module_header.js';
-export {DisableModuleEvent, DismissModuleElementEvent, DismissModuleInstanceEvent, ModulesV2Element, NamedWidth, SUPPORTED_MODULE_WIDTHS} from './modules/v2/modules.js';
+export {DisableModuleEvent, DismissModuleElementEvent, DismissModuleInstanceEvent, ModulesElement, NamedWidth, SUPPORTED_MODULE_WIDTHS} from './modules/v2/modules.js';
 export {ModuleElement as MostRelevantTabResumptionModuleElement, mostRelevantTabResumptionDescriptor} from './modules/v2/most_relevant_tab_resumption/module.js';
 export {MostRelevantTabResumptionProxyImpl} from './modules/v2/most_relevant_tab_resumption/most_relevant_tab_resumption_proxy.js';
 export {VoiceSearchOverlayElement} from './voice_search_overlay.js';
diff --git a/chrome/browser/resources/new_tab_page/modules/module_wrapper.css b/chrome/browser/resources/new_tab_page/modules/module_wrapper.css
new file mode 100644
index 0000000..bf81088
--- /dev/null
+++ b/chrome/browser/resources/new_tab_page/modules/module_wrapper.css
@@ -0,0 +1,35 @@
+/* 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. */
+
+/* #css_wrapper_metadata_start
+ * #type=style-lit
+ * #scheme=relative
+ * #css_wrapper_metadata_end */
+
+:host {
+  background-color: var(--color-new-tab-page-module-background);
+  border: none;
+  border-radius: var(--ntp-module-border-radius);
+  box-sizing: border-box;
+  display: block;
+  height: fit-content;
+  overflow: visible;
+  position: relative;
+}
+
+#impressionProbe {
+  height: 27px;
+  pointer-events: none;
+  position: absolute;
+  width: 100%;
+}
+
+#moduleElement {
+  align-items: center;
+  background: var(--color-new-tab-page-module-background);
+  border-radius: var(--ntp-module-border-radius);
+  display: flex;
+  height: 100%;
+  justify-content: center;
+}
diff --git a/chrome/browser/resources/new_tab_page/modules/module_wrapper.html b/chrome/browser/resources/new_tab_page/modules/module_wrapper.html
index 284624fc..9392dd5 100644
--- a/chrome/browser/resources/new_tab_page/modules/module_wrapper.html
+++ b/chrome/browser/resources/new_tab_page/modules/module_wrapper.html
@@ -1,30 +1,4 @@
-<style>
-  :host {
-    background-color: var(--color-new-tab-page-module-background);
-    border: none;
-    border-radius: var(--ntp-module-border-radius);
-    box-sizing: border-box;
-    display: block;
-    height: fit-content;
-    overflow: visible;
-    position: relative;
-  }
-
-  #impressionProbe {
-    height: 27px;
-    pointer-events: none;
-    position: absolute;
-    width: 100%;
-  }
-
-  #moduleElement {
-    align-items: center;
-    background: var(--color-new-tab-page-module-background);
-    border-radius: var(--ntp-module-border-radius);
-    display: flex;
-    height: 100%;
-    justify-content: center;
-  }
-</style>
 <div id="impressionProbe"></div>
-<div id="moduleElement"></div>
+<div id="moduleElement">
+  <slot id="slot"></slot>
+</div>
diff --git a/chrome/browser/resources/new_tab_page/modules/module_wrapper.ts b/chrome/browser/resources/new_tab_page/modules/module_wrapper.ts
index 0499a85..af31ccc 100644
--- a/chrome/browser/resources/new_tab_page/modules/module_wrapper.ts
+++ b/chrome/browser/resources/new_tab_page/modules/module_wrapper.ts
@@ -2,21 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {assert} from 'chrome://resources/js/assert.js';
-import {microTask, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {CrLitElement, render} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
 import {recordLoadDuration, recordOccurence, recordPerdecage} from '../metrics_utils.js';
 import {NewTabPageProxy} from '../new_tab_page_proxy.js';
 import {WindowProxy} from '../window_proxy.js';
 
 import type {ModuleDescriptor} from './module_descriptor.js';
-import {getTemplate} from './module_wrapper.html.js';
+import {getCss} from './module_wrapper.css.js';
+import {getHtml} from './module_wrapper.html.js';
 
 /** @fileoverview Element that implements the common module UI. */
 
 export interface ModuleInstance {
   element: HTMLElement;
   descriptor: ModuleDescriptor;
+  initialized: boolean;
+  impressed: boolean;
 }
 
 export interface ModuleWrapperElement {
@@ -26,34 +28,65 @@
   };
 }
 
-export class ModuleWrapperElement extends PolymerElement {
+export class ModuleWrapperElement extends CrLitElement {
   static get is() {
     return 'ntp-module-wrapper';
   }
 
-  static get template() {
-    return getTemplate();
+  static override get styles() {
+    return getCss();
   }
 
-  static get properties() {
+  static override get properties() {
     return {
       module: {
-        observer: 'onModuleChange_',
         type: Object,
       },
     };
   }
 
-  declare module: ModuleInstance;
+  accessor module: ModuleInstance;
 
-  private onModuleChange_(
-      _newValue: ModuleInstance, oldValue?: ModuleInstance) {
-    assert(!oldValue);
-    this.$.moduleElement.appendChild(this.module.element);
+  override render() {
+    // Update the light DOM element(s) and allow Lit to handle the shadow DOM
+    // with a slotted module's UI element.
+    if (this.module) {
+      render(this.module.element, this, {host: this});
+    }
+    return getHtml.bind(this)();
+  }
 
+  override firstUpdated() {
+    if (!this.module.initialized) {
+      this.module.initialized = true;
+      this.initModuleInstance_();
+    }
+
+    if (!this.module.impressed) {
+      // Install observer to log module header impression.
+      const headerObserver =
+          new IntersectionObserver(([{intersectionRatio}]) => {
+            if (intersectionRatio >= 1.0) {
+              headerObserver.disconnect();
+
+              const time = WindowProxy.getInstance().now();
+              recordLoadDuration('NewTabPage.Modules.Impression', time);
+              recordLoadDuration(
+                  `NewTabPage.Modules.Impression.${this.module.descriptor.id}`,
+                  time);
+              this.module.impressed = true;
+              this.dispatchEvent(new Event('detect-impression'));
+              this.module.element.dispatchEvent(new Event('detect-impression'));
+            }
+          }, {threshold: 1.0});
+      headerObserver.observe(this.$.impressionProbe);
+    }
+  }
+
+  private initModuleInstance_() {
     // Log at most one usage per module per NTP page load. This is possible,
     // if a user opens a link in a new tab.
-    this.$.moduleElement.addEventListener('usage', (e: Event) => {
+    this.module.element.addEventListener('usage', (e: Event) => {
       e.stopPropagation();
       NewTabPageProxy.getInstance().handler.onModuleUsed(
           this.module.descriptor.id);
@@ -64,7 +97,7 @@
 
     // Dispatch at most one interaction event for a module's `More Actions` menu
     // button clicks.
-    this.$.moduleElement.addEventListener('menu-button-click', (e: Event) => {
+    this.module.element.addEventListener('menu-button-click', (e: Event) => {
       e.stopPropagation();
       NewTabPageProxy.getInstance().handler.onModuleUsed(
           this.module.descriptor.id);
@@ -76,21 +109,17 @@
           'NewTabPage.Modules.InfoButtonClicked', this.module.descriptor.id);
     }, {once: true});
 
-    // Install observer to log module header impression.
-    const headerObserver = new IntersectionObserver(([{intersectionRatio}]) => {
-      if (intersectionRatio >= 1.0) {
-        headerObserver.disconnect();
-        const time = WindowProxy.getInstance().now();
-        recordLoadDuration('NewTabPage.Modules.Impression', time);
-        recordLoadDuration(
-            `NewTabPage.Modules.Impression.${this.module.descriptor.id}`, time);
-        this.dispatchEvent(new Event('detect-impression'));
-        this.module.element.dispatchEvent(new Event('detect-impression'));
-      }
-    }, {threshold: 1.0});
+    // Track whether the user hovered on the module.
+    this.module.element.addEventListener('mouseover', () => {
+      chrome.metricsPrivate.recordSparseValueWithPersistentHash(
+          'NewTabPage.Modules.Hover', this.module.descriptor.id);
+    }, {
+      capture: true,  // So that modules cannot swallow event.
+      once: true,     // Only one log per NTP load.
+    });
 
-    // Install observer to track max perdecage (x/10th) of the module visible on
-    // the page.
+    // Install observer to track max perdecage (x/10th) of the module visible
+    // on the page.
     let intersectionPerdecage = 0;
     const moduleObserver = new IntersectionObserver(([{intersectionRatio}]) => {
       intersectionPerdecage =
@@ -106,24 +135,7 @@
           `NewTabPage.Modules.ImpressionRatio.${this.module.descriptor.id}`,
           intersectionPerdecage);
     });
-
-    // Calling observe will immediately invoke the callback. If the module is
-    // fully shown when the page loads, the first callback invocation will
-    // happen before the elements have dimensions. For this reason, we start
-    // observing after the elements have had a chance to be rendered.
-    microTask.run(() => {
-      headerObserver.observe(this.$.impressionProbe);
-      moduleObserver.observe(this);
-    });
-
-    // Track whether the user hovered on the module.
-    this.addEventListener('mouseover', () => {
-      chrome.metricsPrivate.recordSparseValueWithPersistentHash(
-          'NewTabPage.Modules.Hover', this.module.descriptor.id);
-    }, {
-      capture: true,  // So that modules cannot swallow event.
-      once: true,     // Only one log per NTP load.
-    });
+    moduleObserver.observe(this);
   }
 }
 
diff --git a/chrome/browser/resources/new_tab_page/modules/modules.gni b/chrome/browser/resources/new_tab_page/modules/modules.gni
index db176477..31a14ac 100644
--- a/chrome/browser/resources/new_tab_page/modules/modules.gni
+++ b/chrome/browser/resources/new_tab_page/modules/modules.gni
@@ -39,7 +39,9 @@
 modules_css_files =
     [
       "modules/info_dialog.css",
+      "modules/module_wrapper.css",
       "modules/v2/module_header.css",
+      "modules/v2/modules.css",
     ] + authentication_css_files + calendar_css_files +
     file_suggestion_v2_css_files + most_relevant_tab_resumption_v2_css_files
 
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/authentication/microsoft_auth_module.ts b/chrome/browser/resources/new_tab_page/modules/v2/authentication/microsoft_auth_module.ts
index 3fc06ea..795e35011 100644
--- a/chrome/browser/resources/new_tab_page/modules/v2/authentication/microsoft_auth_module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/v2/authentication/microsoft_auth_module.ts
@@ -78,6 +78,7 @@
 
   protected onDisableButtonClick_() {
     const disableEvent = new CustomEvent('disable-module', {
+      bubbles: true,
       composed: true,
       detail: {
         message: loadTimeData.getStringF(
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/calendar/outlook_calendar_module.ts b/chrome/browser/resources/new_tab_page/modules/v2/calendar/outlook_calendar_module.ts
index d047ea54..be4b0e61 100644
--- a/chrome/browser/resources/new_tab_page/modules/v2/calendar/outlook_calendar_module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/v2/calendar/outlook_calendar_module.ts
@@ -96,6 +96,7 @@
 
   protected onDisableButtonClick_() {
     const disableEvent = new CustomEvent('disable-module', {
+      bubbles: true,
       composed: true,
       detail: {
         message: loadTimeData.getStringF(
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/file_suggestion/drive_module.ts b/chrome/browser/resources/new_tab_page/modules/v2/file_suggestion/drive_module.ts
index 649ddf0..d91d572 100644
--- a/chrome/browser/resources/new_tab_page/modules/v2/file_suggestion/drive_module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/v2/file_suggestion/drive_module.ts
@@ -82,6 +82,7 @@
 
   protected onDisableButtonClick_() {
     const disableEvent = new CustomEvent('disable-module', {
+      bubbles: true,
       composed: true,
       detail: {
         message: loadTimeData.getStringF(
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/file_suggestion/microsoft_files_module.ts b/chrome/browser/resources/new_tab_page/modules/v2/file_suggestion/microsoft_files_module.ts
index 104e436..3f0fb0d 100644
--- a/chrome/browser/resources/new_tab_page/modules/v2/file_suggestion/microsoft_files_module.ts
+++ b/chrome/browser/resources/new_tab_page/modules/v2/file_suggestion/microsoft_files_module.ts
@@ -100,6 +100,7 @@
 
   protected onDisableButtonClick_() {
     const disableEvent = new CustomEvent('disable-module', {
+      bubbles: true,
       composed: true,
       detail: {
         message: loadTimeData.getStringF(
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/modules.css b/chrome/browser/resources/new_tab_page/modules/v2/modules.css
new file mode 100644
index 0000000..283f425
--- /dev/null
+++ b/chrome/browser/resources/new_tab_page/modules/v2/modules.css
@@ -0,0 +1,23 @@
+/* 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. */
+
+/* #css_wrapper_metadata_start
+ * #type=style-lit
+ * #import=//resources/cr_elements/cr_hidden_style_lit.css.js
+ * #scheme=relative
+ * #include=cr-hidden-style-lit
+ * #css_wrapper_metadata_end */
+
+#container {
+  display: flex;
+  flex-flow: row wrap;
+  gap: var(--container-gap);
+  justify-content: center;
+  margin-top: 8px;
+  max-width: var(--container-max-width, inherit);
+}
+
+#undoToastMessage {
+  flex-grow: 1;
+}
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/modules.html b/chrome/browser/resources/new_tab_page/modules/v2/modules.html
index 59856469..08d0489 100644
--- a/chrome/browser/resources/new_tab_page/modules/v2/modules.html
+++ b/chrome/browser/resources/new_tab_page/modules/v2/modules.html
@@ -1,34 +1,20 @@
-<style include="cr-hidden-style">
-  #container {
-    display: flex;
-    flex-flow: row wrap;
-    gap: var(--container-gap);
-    justify-content: center;
-    margin-top: 8px;
-    max-width: var(--container-max-width, inherit);
-  }
-
-  #undoToastMessage {
-    flex-grow: 1;
-  }
-</style>
 <div id="container">
+  ${this.moduleInstances_.map(item => html`
+    <ntp-module-wrapper .module="${item}"
+        ?hidden="${this.moduleDisabled_(item)}"
+        @disable-module="${this.onDisableModule_}"
+        @dismiss-module-element="${this.onDismissModuleElement_}"
+        @dismiss-module-instance="${this.onDismissModuleInstance_}">
+    </ntp-module-wrapper>
+  `)}
 </div>
-<template>
-  <ntp-module-wrapper module="[[item]]"
-      hidden="[[moduleDisabled_(disabledModules_, item)]]"
-      on-disable-module="onDisableModule_"
-      on-dismiss-module-element="onDismissModuleElement_"
-      on-dismiss-module-instance="onDismissModuleInstance_">
-  </ntp-module-wrapper>
-</template>
 <cr-toast id="undoToast" duration="10000">
-  <div id="undoToastMessage">[[undoData_.message]]</div>
-  <template is="dom-if" if="[[undoData_.undo]]">
+  <div id="undoToastMessage">${this.undoData_?.message || ''}</div>
+  ${this.undoData_?.undo ? html `
     <cr-button id="undoButton"
         aria-label="$i18n{undoDescription}"
-        on-click="onUndoButtonClick_">
+        @click="${this.onUndoButtonClick_}">
       $i18n{undo}
     </cr-button>
-  </template>
+  ` : ''}
 </cr-toast>
diff --git a/chrome/browser/resources/new_tab_page/modules/v2/modules.ts b/chrome/browser/resources/new_tab_page/modules/v2/modules.ts
index 1987678..63f693c 100644
--- a/chrome/browser/resources/new_tab_page/modules/v2/modules.ts
+++ b/chrome/browser/resources/new_tab_page/modules/v2/modules.ts
@@ -2,27 +2,26 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
 import 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
 import 'chrome://resources/cr_elements/cr_button/cr_button.js';
 
 import type {CrToastElement} from 'chrome://resources/cr_elements/cr_toast/cr_toast.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.js';
-import type {TemplateInstanceBase} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {PolymerElement, templatize} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js';
+import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
 import {loadTimeData} from '../../i18n_setup.js';
 import {recordOccurence as recordOccurrence} from '../../metrics_utils.js';
-import type {PageCallbackRouter, PageHandlerRemote} from '../../new_tab_page.mojom-webui.js';
-import type {ModuleIdName} from '../../new_tab_page.mojom-webui.js';
+import type {ModuleIdName, PageCallbackRouter, PageHandlerRemote} from '../../new_tab_page.mojom-webui.js';
 import {NewTabPageProxy} from '../../new_tab_page_proxy.js';
 import {WindowProxy} from '../../window_proxy.js';
-import type {Module, ModuleDescriptor} from '../module_descriptor.js';
+import type {Module} from '../module_descriptor.js';
 import {ModuleRegistry} from '../module_registry.js';
 import type {ModuleInstance, ModuleWrapperElement} from '../module_wrapper.js';
 
-import {getTemplate} from './modules.html.js';
+import {getCss} from './modules.css.js';
+import {getHtml} from './modules.html.js';
 
 export interface NamedWidth {
   name: string;
@@ -52,13 +51,6 @@
 export type DismissModuleInstanceEvent = UndoActionEvent;
 export type DisableModuleEvent = UndoActionEvent;
 
-type ModuleWrapperConstructor = new (_: Object) =>
-    TemplateInstanceBase&HTMLElement;
-
-interface ItemTemplateInstance extends TemplateInstanceBase {
-  item: {descriptor: ModuleDescriptor};
-}
-
 declare global {
   interface HTMLElementEventMap {
     'disable-module': DisableModuleEvent;
@@ -67,7 +59,7 @@
   }
 }
 
-export interface ModulesV2Element {
+export interface ModulesElement {
   $: {
     container: HTMLElement,
     undoToast: CrToastElement,
@@ -75,115 +67,74 @@
   };
 }
 
-/**
- * Creates template instances for a list of modules.
- *
- * @param modules The modules for which to create template instances.
- * @param moduleWrapperConstructor The constructor used to create the template
- *     instances.
- * @returns An array of `TemplateInstanceBase` objects.
- */
-function createTemplateInstances(
-    modules: Module[], moduleWrapperConstructor: ModuleWrapperConstructor):
-    TemplateInstanceBase[] {
-  return modules.flatMap(module => module.elements.map(element => {
-    const instanceData = {
+function createModuleInstances(module: Module): ModuleInstance[] {
+  return module.elements.map(element => {
+    return {
       element,
       descriptor: module.descriptor,
+      initialized: false,
+      impressed: false,
     };
-    return new moduleWrapperConstructor({item: instanceData});
-  }));
+  });
 }
 
 /** Container for the NTP modules. */
-export class ModulesV2Element extends PolymerElement {
+export class ModulesElement extends CrLitElement {
   static get is() {
-    return 'ntp-modules-v2';
+    return 'ntp-modules';
   }
 
-  static get template() {
-    return getTemplate();
+  static override get styles() {
+    return getCss();
   }
 
-  static get properties() {
+  static override get properties() {
     return {
-      disabledModules_: {
-        type: Object,
-        observer: 'onDisabledModulesChange_',
-        value: () => ({all: true, ids: []}),
-      },
-
       modulesShownToUser: {
         type: Boolean,
         notify: true,
       },
-
+      moduleInstances_: {type: Array},
+      disabledModules_: {type: Object},
       /** Data about the most recent un-doable action. */
-      undoData_: {
-        type: Object,
-        value: null,
-      },
+      undoData_: {type: Object},
     };
   }
 
-  declare modulesShownToUser: boolean;
+  accessor modulesShownToUser: boolean = false;
   private waitToLoadModules_: boolean =
       loadTimeData.getBoolean('waitToLoadModules');
-  private maxColumnCount_: number;
+  accessor moduleInstances_: ModuleInstance[] = [];
+  accessor disabledModules_:
+      {all: boolean, ids: string[]} = {all: true, ids: []};
+  protected accessor undoData_: {message: string, undo?: () => void}|null =
+      null;
+
+  private maxColumnCount_: number =
+      loadTimeData.getInteger('modulesMaxColumnCount');
+  private availableWidth_: number = 0;
   private containerMaxWidth_: number;
-  declare private disabledModules_: {all: boolean, ids: string[]};
   private eventTracker_: EventTracker = new EventTracker();
-  declare private undoData_: {message: string, undo?: () => void}|null;
   private setDisabledModulesListenerId_: number|null = null;
   private setModulesLoadableListenerId_: number|null = null;
-  private containerObserver_: MutationObserver|null = null;
-  private templateInstances_: TemplateInstanceBase[] = [];
-  private availableModulesIds_: string[]|null = null;
-  private modulesLoadInitiated_: boolean = false;
+
+  private availableModulesIds_: Set<string>|null = null;
   private moduleLoadPromise_: Promise<void>|null = null;
   // TODO(crbug.com/385174675): Remove |modulesReloadable_| flag when safe.
-  // Otherwise, when Microsoft modules are enabled ToT, the current behavior
-  // gated by |modulesReloadable_| should become the default module loading
-  // behavior.
+  // Otherwise, when Microsoft modules are enabled ToT, the current
+  // behavior gated by |modulesReloadable_| should become the default module
+  // loading behavior.
   private modulesReloadable_: boolean =
       loadTimeData.getBoolean('modulesReloadable');
-  private moduleWrapperConstructor_: ModuleWrapperConstructor|null = null;
-  private needsReload_: boolean = false;
-  private newlyEnabledModuleIds_: string[] = [];
+  private modulesLoadInitiated_: boolean = false;
 
-  private callbackRouter_: PageCallbackRouter;
-  private handler_: PageHandlerRemote;
-  private moduleRegistry_: ModuleRegistry;
-
-  constructor() {
-    super();
-    this.callbackRouter_ = NewTabPageProxy.getInstance().callbackRouter;
-    this.handler_ = NewTabPageProxy.getInstance().handler;
-    this.moduleRegistry_ = ModuleRegistry.getInstance();
+  override render() {
+    return getHtml.bind(this)();
   }
 
   override connectedCallback() {
     super.connectedCallback();
 
-    this.setDisabledModulesListenerId_ =
-        this.callbackRouter_.setDisabledModules.addListener(
-            (all: boolean, ids: string[]) => {
-              if (this.modulesReloadable_ && this.modulesLoadInitiated_) {
-                this.handleModuleEnablement_(this.disabledModules_.ids, ids);
-              }
-
-              this.disabledModules_ = {all, ids};
-            });
-    this.handler_.updateDisabledModules();
-
-    this.setModulesLoadableListenerId_ =
-        this.callbackRouter_.setModulesLoadable.addListener(() => {
-          if (this.waitToLoadModules_) {
-            this.waitToLoadModules_ = false;
-            this.moduleLoadPromise_ = this.loadModules_();
-          }
-        });
-
     const widths: Set<number> = new Set();
     for (let i = 0; i < SUPPORTED_MODULE_WIDTHS.length; i++) {
       const namedWidth = SUPPORTED_MODULE_WIDTHS[i];
@@ -231,10 +182,27 @@
 
     this.eventTracker_.add(window, 'keydown', this.onWindowKeydown_.bind(this));
 
-    this.containerObserver_ = new MutationObserver(() => {
-      this.updateContainerAndChildrenStyles_();
-    });
-    this.containerObserver_.observe(this.$.container, {childList: true});
+    this.setDisabledModulesListenerId_ =
+        this.callbackRouter_.setDisabledModules.addListener(
+            (all: boolean, ids: string[]) => {
+              this.disabledModules_ = {all, ids};
+            });
+    this.pageHandler_.updateDisabledModules();
+
+    this.setModulesLoadableListenerId_ =
+        this.callbackRouter_.setModulesLoadable.addListener(() => {
+          if (this.waitToLoadModules_) {
+            this.waitToLoadModules_ = false;
+            this.moduleLoadPromise_ = this.loadModules_();
+          } else if (this.modulesReloadable_) {
+            this.handleModuleEnablement_(this.disabledModules_);
+          }
+        });
+    if (this.waitToLoadModules_) {
+      this.pageHandler_.updateModulesLoadable();
+    } else {
+      this.moduleLoadPromise_ = this.loadModules_();
+    }
   }
 
   override disconnectedCallback() {
@@ -247,158 +215,68 @@
 
     this.eventTracker_.removeAll();
 
-    this.containerObserver_!.disconnect();
   }
 
-  override ready() {
-    super.ready();
+  override firstUpdated() {
+    this.style.setProperty('--container-gap', `${CONTAINER_GAP_WIDTH}px`);
 
-    this.updateStyles({
-      '--container-gap': `${CONTAINER_GAP_WIDTH}px`,
-    });
-
-    this.maxColumnCount_ = loadTimeData.getInteger('modulesMaxColumnCount');
     this.containerMaxWidth_ =
         this.maxColumnCount_ * SUPPORTED_MODULE_WIDTHS[0].value +
         (this.maxColumnCount_ - 1) * CONTAINER_GAP_WIDTH;
+  }
 
-    if (this.waitToLoadModules_) {
-      this.handler_.updateModulesLoadable();
-    } else {
-      this.moduleLoadPromise_ = this.loadModules_();
+  override updated(changedProperties: PropertyValues<this>) {
+    super.updated(changedProperties);
+    this.availableWidth_ = Math.min(
+        document.body.clientWidth - 2 * MARGIN_WIDTH, this.containerMaxWidth_);
+  }
+
+  override willUpdate(changedProperties: PropertyValues<this>) {
+    super.willUpdate(changedProperties);
+    if (changedProperties.has('moduleInstances_') ||
+        changedProperties.has('disabledModules_')) {
+      this.updateContainerAndChildrenStyles_(this.availableWidth_);
     }
   }
 
-  private moduleDisabled_(
-      disabledModules: {all: true, ids: string[]},
-      instance: ModuleInstance): boolean {
-    return disabledModules.all ||
-        disabledModules.ids.includes(instance.descriptor.id);
+  get pageHandler_(): PageHandlerRemote {
+    return NewTabPageProxy.getInstance().handler;
   }
 
-  /**
-   * Manages the reloading of modules within the container based on
-   * updates to the disabled modules list.
-   *
-   * Subsequent calls handle potential reloads. Newly enabled modules are
-   * queued and loaded individually. The user does not see these modules until
-   * the entire container is reloaded via |reloadModules_()| after all
-   * queued modules have been loaded. Loading continues as long as
-   * |this.newlyEnabledModuleIds_| is not empty, even across multiple calls to
-   * |maybeLoadModules_()|.
-   *
-   * @param prevDisabledIds - Previous list of disabled module IDs.
-   * @param newDisabledIds - Latest list of disabled module IDs.
-   */
-  private async handleModuleEnablement_(
-      prevDisabledIds: string[], newDisabledIds: string[]): Promise<void> {
-    if (this.moduleLoadPromise_) {
-      await this.moduleLoadPromise_;
-    }
+  get callbackRouter_(): PageCallbackRouter {
+    return NewTabPageProxy.getInstance().callbackRouter;
+  }
 
-    if (!this.availableModulesIds_) {
-      const modulesIdNames = (await this.handler_.getModulesIdNames()).data;
-      // TODO(crbug.com/385174675): Set |this.availableModulesIds_| in
-      // |this.loadModules_()| when Microsoft modules are enabled ToT, as this
-      // experimental behavior is currently gated to the Microsoft modules.
-      this.availableModulesIds_ = modulesIdNames.map((m: ModuleIdName) => m.id);
-    }
-
-    const filteredNewlyEnabledModuleIds =
-        prevDisabledIds.filter(id => !newDisabledIds.includes(id))
-            .filter(id => this.availableModulesIds_!.includes(id));
-    this.newlyEnabledModuleIds_ =
-        this.newlyEnabledModuleIds_.concat(filteredNewlyEnabledModuleIds);
-
-    if (filteredNewlyEnabledModuleIds.length === 0) {
-      return;
-    }
-
-    // Load modules one by one until the queue is empty.
-    while (this.newlyEnabledModuleIds_.length > 0) {
-      const id = this.newlyEnabledModuleIds_.shift() as string;
-      const hasExistingInstance = this.templateInstances_.some(
-          (templateInstance) =>
-              (templateInstance as unknown as ItemTemplateInstance)
-                  .item.descriptor.id === id);
-      if (!hasExistingInstance) {
-        await this.addTemplateInstance_(id);
-        this.needsReload_ = true;
-      }
-      if (this.newlyEnabledModuleIds_.length === 0 && this.needsReload_) {
-        await this.reloadModules_();
-        this.needsReload_ = false;
-      } else {
-        // More modules to load; the next call to this function will continue
-        // the process.
-        return;
-      }
-    }
+  protected moduleDisabled_(instance: ModuleInstance): boolean {
+    return this.disabledModules_.all ||
+        this.disabledModules_.ids.includes(instance.descriptor.id);
   }
 
   /**
    * Initializes the module container by loading all currently enabled modules.
-   * This method uses |this.moduleRegistry_| to determine which modules to load
+   * This method uses the `ModuleRegistry` to determine which modules to load
    * and is called only when the container is empty.
-   *
    */
   private async loadModules_(): Promise<void> {
-    if (this.waitToLoadModules_) {
-      return;
-    }
-
     this.modulesLoadInitiated_ = true;
-    const modulesIdNames = (await this.handler_.getModulesIdNames()).data;
-    const modules = await this.moduleRegistry_.initializeModulesHavingIds(
-        modulesIdNames.map((m: ModuleIdName) => m.id),
-        loadTimeData.getInteger('modulesLoadTimeout'));
+    const modulesIdNames = (await this.pageHandler_.getModulesIdNames()).data;
+    const modules =
+        await ModuleRegistry.getInstance().initializeModulesHavingIds(
+            modulesIdNames.map((m: ModuleIdName) => m.id),
+            loadTimeData.getInteger('modulesLoadTimeout'));
     if (modules) {
-      this.handler_.onModulesLoadedWithData(
+      this.pageHandler_.onModulesLoadedWithData(
           modules.map(module => module.descriptor.id));
 
-      // TODO(crbug.com/392889804): Remove this logic, since no modules populate
-      // more than once anymore.
-      if (modules.length > 1) {
-        const maxModuleInstanceCount =
-            (modules.length >= this.maxColumnCount_) ?
-            1 :
-            loadTimeData.getInteger(
-                'multipleLoadedModulesMaxModuleInstanceCount');
-        if (maxModuleInstanceCount > 0) {
-          modules.forEach(module => {
-            module.elements.splice(
-                maxModuleInstanceCount,
-                module.elements.length - maxModuleInstanceCount);
-          });
-        }
-      }
-
-      if (modules.length > 0) {
-        if (!this.moduleWrapperConstructor_) {
-          this.initModuleWrapperConstructor_();
-        }
-
-        this.templateInstances_ =
-            createTemplateInstances(modules, this.moduleWrapperConstructor_!);
-        this.$.container.replaceChildren(
-            ...this.templateInstances_.map(t => t.children[0] as HTMLElement));
-      }
+      this.moduleInstances_ = modules
+                                  .map(module => {
+                                    return createModuleInstances(module);
+                                  })
+                                  .flat();
 
       this.recordInitialLoadMetrics_(modules, modulesIdNames);
       this.dispatchEvent(new Event('modules-loaded'));
     }
-
-    this.moduleLoadPromise_ = null;
-  }
-
-  private initModuleWrapperConstructor_() {
-    const template = this.shadowRoot!.querySelector('template')!;
-    this.moduleWrapperConstructor_ = templatize(template, this, {
-                                       parentModel: true,
-                                       forwardHostProp: this.forwardHostProp_,
-                                       instanceProps: {item: true},
-                                     }) as new (item: Object) =>
-                                         TemplateInstanceBase & HTMLElement;
   }
 
   private recordInitialLoadMetrics_(
@@ -412,18 +290,15 @@
               !this.disabledModules_.ids.includes(id));
     });
     chrome.metricsPrivate.recordSmallCount(
-        'NewTabPage.Modules.InstanceCount', this.templateInstances_.length);
+        'NewTabPage.Modules.InstanceCount', this.moduleInstances_.length);
     chrome.metricsPrivate.recordBoolean(
         'NewTabPage.Modules.VisibleOnNTPLoad', !this.disabledModules_.all);
     this.recordModuleLoadedWithModules_(/*onNtpLoad=*/ true);
-
-    this.dispatchEvent(new Event('modules-loaded'));
   }
 
   private recordModuleLoadedWithModules_(onNtpLoad: boolean) {
-    const moduleDescriptorIds = [...new Set(this.templateInstances_.map(
-        instance =>
-            (instance as unknown as ItemTemplateInstance).item.descriptor.id))];
+    const moduleDescriptorIds = [...new Set(
+        this.moduleInstances_.map(instance => instance.descriptor.id))];
 
     const histogramBase = onNtpLoad ? 'NewTabPage.Modules.LoadedWith' :
                                       'NewTabPage.Modules.ReloadedWith';
@@ -439,79 +314,85 @@
   }
 
   /**
-   * Creates a template instance for a module then appends it to
-   * |this.templateInstances_| based on the provided module id.
+   * Manages the reloading of modules within the container based on
+   * updates to the disabled modules list.
    *
-   * @param moduleId A module id to be leveraged when determining the
-   *     module to be initialized.
+   * Subsequent calls handle potential reloads. Newly enabled modules are
+   * queued and loaded individually. The user does not see these modules until
+   * the entire container is reloaded after all queued modules have been loaded.
+   *
+   * @param disabledModules - An object containing the current list of disabled
+   * module ids.
    */
-  private async addTemplateInstance_(moduleId: string): Promise<void> {
-    const module = await this.moduleRegistry_.initializeModuleById(
-        moduleId, loadTimeData.getInteger('modulesLoadTimeout'));
+  private async handleModuleEnablement_(
+      disabledModules: {all: boolean, ids: string[]}): Promise<void> {
+    if (this.moduleLoadPromise_) {
+      await this.moduleLoadPromise_;
+    }
 
-    if (!module) {
+    if (!this.availableModulesIds_) {
+      const modulesIdNames = (await this.pageHandler_.getModulesIdNames()).data;
+      // TODO(crbug.com/385174675): Set |this.availableModulesIds_| in
+      // |this.loadModules_()| when Microsoft modules are enabled ToT, as this
+      // experimental behavior is currently gated to the Microsoft modules.
+      this.availableModulesIds_ = new Set(modulesIdNames.map((m) => m.id));
+    }
+
+    const disabledModuleIds = disabledModules.ids;
+    const newlyEnabledModuleIds = [...this.availableModulesIds_.difference(
+        new Set(disabledModuleIds.concat(
+            this.moduleInstances_.map((m) => m.descriptor.id))))];
+    if (newlyEnabledModuleIds.length === 0) {
+      return;
+    }
+    // Load modules one by one until the queue is empty.
+    const newModuleInstances: ModuleInstance[] = [];
+    while (newlyEnabledModuleIds.length > 0) {
+      const moduleId = newlyEnabledModuleIds.shift()!;
+      const module = await ModuleRegistry.getInstance().initializeModuleById(
+          moduleId, loadTimeData.getInteger('modulesLoadTimeout'));
+      if (module) {
+        newModuleInstances.push(...createModuleInstances(module));
+      }
+    }
+
+    if (newModuleInstances.length > 0) {
+      newModuleInstances.push(...this.moduleInstances_);
+      const orderedIds = (await this.pageHandler_.getModulesOrder()).moduleIds;
+      if (orderedIds && orderedIds.length > 0) {
+        newModuleInstances.sort((a, b) => {
+          const aId = a.descriptor.id;
+          const bId = b.descriptor.id;
+          const aHasOrder = orderedIds.includes(aId);
+          const bHasOrder = orderedIds.includes(bId);
+          if (aHasOrder && bHasOrder) {
+            return orderedIds.indexOf(aId) - orderedIds.indexOf(bId);
+          }
+          return +bHasOrder - +aHasOrder;
+        });
+      }
+
+      this.moduleInstances_ = newModuleInstances;
+      chrome.metricsPrivate.recordSmallCount(
+          'NewTabPage.Modules.ReloadedModulesCount',
+          this.moduleInstances_.length);
+      this.recordModuleLoadedWithModules_(/*onNtpLoad=*/ false);
+    }
+  }
+
+  private updateContainerAndChildrenStyles_(availableWidth: number) {
+    const visibleModuleInstances = this.disabledModules_.all ?
+        [] :
+        this.moduleInstances_.filter(
+            instance =>
+                !this.disabledModules_.ids.includes(instance.descriptor.id));
+
+    this.modulesShownToUser = visibleModuleInstances.length !== 0;
+    if (visibleModuleInstances.length === 0) {
       return;
     }
 
-    if (!this.moduleWrapperConstructor_) {
-      this.initModuleWrapperConstructor_();
-    }
-
-    this.templateInstances_ = this.templateInstances_.concat(
-        ...createTemplateInstances([module], this.moduleWrapperConstructor_!));
-  }
-
-  /**
-   * Reloads the modules container by sorting template instances based on the
-   * order provided by |this.handler_| and then updating the container's DOM.
-   */
-  private async reloadModules_(): Promise<void> {
-    const orderedIds = (await this.handler_.getModulesOrder()).moduleIds;
-    if (orderedIds && orderedIds.length > 0) {
-      this.templateInstances_ = this.templateInstances_.sort((a, b) => {
-        const aId = (a as unknown as ItemTemplateInstance).item.descriptor.id;
-        const bId = (b as unknown as ItemTemplateInstance).item.descriptor.id;
-        const aHasOrder = orderedIds.includes(aId);
-        const bHasOrder = orderedIds.includes(bId);
-        if (aHasOrder && bHasOrder) {
-          // Apply order.
-          return orderedIds.indexOf(aId) - orderedIds.indexOf(bId);
-        }
-        return +bHasOrder - +aHasOrder;
-      });
-    }
-
-    this.$.container.replaceChildren(
-        ...this.templateInstances_.map(t => t.children[0] as HTMLElement));
-
-    chrome.metricsPrivate.recordSmallCount(
-        'NewTabPage.Modules.ReloadedModulesCount',
-        this.templateInstances_.length);
-    this.recordModuleLoadedWithModules_(/*onNtpLoad=*/ false);
-  }
-
-  private forwardHostProp_(property: string, value: any) {
-    this.templateInstances_.forEach(instance => {
-      instance.forwardHostProp(property, value);
-    });
-  }
-
-  private updateContainerAndChildrenStyles_(availableWidth?: number) {
-    if (typeof availableWidth === 'undefined') {
-      availableWidth = Math.min(
-          document.body.clientWidth - 2 * MARGIN_WIDTH,
-          this.containerMaxWidth_);
-    }
-
-    const moduleWrappers =
-        Array.from(this.shadowRoot!.querySelectorAll<ModuleWrapperElement>(
-            'ntp-module-wrapper:not([hidden])'));
-    this.modulesShownToUser = moduleWrappers.length !== 0;
-    if (moduleWrappers.length === 0) {
-      return;
-    }
-
-    this.updateStyles({'--container-max-width': `${availableWidth}px`});
+    this.style.setProperty('--container-max-width', `${availableWidth}px`);
 
     const clamp = (min: number, val: number, max: number) =>
         Math.max(min, Math.min(val, max));
@@ -523,9 +404,9 @@
         this.maxColumnCount_);
 
     let index = 0;
-    while (index < moduleWrappers.length) {
-      const instances = moduleWrappers.slice(index, index + rowMaxInstanceCount)
-                            .map(w => w.module);
+    while (index < visibleModuleInstances.length) {
+      const instances =
+          visibleModuleInstances.slice(index, index + rowMaxInstanceCount);
       let namedWidth = SUPPORTED_MODULE_WIDTHS[0];
       for (let i = 1; i < SUPPORTED_MODULE_WIDTHS.length; i++) {
         if (Math.floor(
@@ -548,8 +429,9 @@
     }
   }
 
-  private onDisableModule_(e: DisableModuleEvent) {
-    const id = (e.target! as ModuleWrapperElement).module.descriptor.id;
+  protected onDisableModule_(e: DisableModuleEvent) {
+    const id = ((e.target! as HTMLElement).parentNode as ModuleWrapperElement)
+                   .module.descriptor.id;
     const restoreCallback = e.detail.restoreCallback;
     this.undoData_ = {
       message: e.detail.message,
@@ -557,7 +439,7 @@
         if (restoreCallback) {
           restoreCallback();
         }
-        this.handler_.setModuleDisabled(id, false);
+        this.pageHandler_.setModuleDisabled(id, false);
         chrome.metricsPrivate.recordSparseValueWithPersistentHash(
             'NewTabPage.Modules.Enabled', id);
         chrome.metricsPrivate.recordSparseValueWithPersistentHash(
@@ -565,7 +447,7 @@
       },
     };
 
-    this.handler_.setModuleDisabled(id, true);
+    this.pageHandler_.setModuleDisabled(id, true);
     this.$.undoToast.show();
     chrome.metricsPrivate.recordSparseValueWithPersistentHash(
         METRIC_NAME_MODULE_DISABLED, id);
@@ -573,31 +455,29 @@
         `${METRIC_NAME_MODULE_DISABLED}.ModuleRequest`, id);
   }
 
-  private onDisabledModulesChange_() {
-    this.updateContainerAndChildrenStyles_();
-  }
-
   /**
    * @param e Event notifying a module instance was dismissed. Contains the
    *     message to show in the toast.
    */
-  private onDismissModuleInstance_(e: DismissModuleInstanceEvent) {
-    const wrapper = (e.target! as ModuleWrapperElement);
+  protected onDismissModuleInstance_(e: DismissModuleInstanceEvent) {
+    const wrapper =
+        ((e.target! as HTMLElement).parentNode as ModuleWrapperElement);
     const index = Array.from(wrapper.parentNode!.children).indexOf(wrapper);
-    wrapper.remove();
+    const module = this.moduleInstances_[index];
+    this.moduleInstances_ = this.moduleInstances_.toSpliced(index, 1);
 
     const restoreCallback = e.detail.restoreCallback;
     this.undoData_ = {
       message: e.detail.message,
       undo: restoreCallback ?
           () => {
-            this.$.container.insertBefore(
-                wrapper, this.$.container.childNodes[index]);
+            this.moduleInstances_ =
+                this.moduleInstances_.toSpliced(index, 0, module);
             restoreCallback();
 
             recordOccurrence('NewTabPage.Modules.Restored');
             recordOccurrence(
-                `NewTabPage.Modules.Restored.${wrapper.module.descriptor.id}`);
+                `NewTabPage.Modules.Restored.${module.descriptor.id}`);
           } :
           undefined,
     };
@@ -605,10 +485,10 @@
     // Notify the user.
     this.$.undoToast.show();
 
-    this.handler_.onDismissModule(wrapper.module.descriptor.id);
+    this.pageHandler_.onDismissModule(module.descriptor.id);
   }
 
-  private onDismissModuleElement_(e: DismissModuleElementEvent) {
+  protected onDismissModuleElement_(e: DismissModuleElementEvent) {
     const restoreCallback = e.detail.restoreCallback;
     this.undoData_ = {
       message: e.detail.message,
@@ -623,7 +503,7 @@
     this.$.undoToast.show();
   }
 
-  private onUndoButtonClick_() {
+  protected onUndoButtonClick_() {
     if (!this.undoData_) {
       return;
     }
@@ -646,4 +526,4 @@
   }
 }
 
-customElements.define(ModulesV2Element.is, ModulesV2Element);
+customElements.define(ModulesElement.is, ModulesElement);
diff --git a/chrome/browser/resources/pdf/constants.ts b/chrome/browser/resources/pdf/constants.ts
index 7e9556c..d50ea68 100644
--- a/chrome/browser/resources/pdf/constants.ts
+++ b/chrome/browser/resources/pdf/constants.ts
@@ -58,12 +58,18 @@
   ITALIC = 'italic',
 }
 
+export enum TextTypeface {
+  SANS_SERIF = 'sans-serif',
+  SERIF = 'serif',
+  MONOSPACE = 'monospace',
+}
+
 export type TextStyles = {
   [key in TextStyle]: boolean
 };
 
 export interface TextAttributes {
-  typeface: string;
+  typeface: TextTypeface;
   size: number;
   color: Color;
   alignment: TextAlignment;
diff --git a/chrome/browser/resources/pdf/controller.ts b/chrome/browser/resources/pdf/controller.ts
index c9be83854..70dfb5e 100644
--- a/chrome/browser/resources/pdf/controller.ts
+++ b/chrome/browser/resources/pdf/controller.ts
@@ -64,11 +64,6 @@
   type: 'finishTextAnnotation';
   data: TextAnnotation;
 }
-
-interface AnnotationFontsMessage {
-  type: 'getTextAnnotFontNames';
-  data: string[];
-}
 // </if>
 
 /**
@@ -231,23 +226,6 @@
     });
   }
 
-  getTextAnnotFontNames(): Promise<AnnotationFontsMessage> {
-    // TODO(crbug.com/402546154): Use backend reply instead of dropping it
-    // on the ground and returning dummy data once the backend is in place.
-    this.postMessageWithReply_({
-      type: 'getTextAnnotFontNames',
-    });
-    return Promise.resolve({
-      type: 'getTextAnnotFontNames',
-      data: [
-        'Roboto',
-        'Serif',
-        'Sans',
-        'Monospace',
-      ],
-    });
-  }
-
   setAnnotationBrush(brush: AnnotationBrush) {
     const message: AnnotationBrushMessage = {
       type: 'setAnnotationBrush',
diff --git a/chrome/browser/resources/pdf/elements/ink_annotation_text_mixin.ts b/chrome/browser/resources/pdf/elements/ink_annotation_text_mixin.ts
index 4c02afc..3ed649c 100644
--- a/chrome/browser/resources/pdf/elements/ink_annotation_text_mixin.ts
+++ b/chrome/browser/resources/pdf/elements/ink_annotation_text_mixin.ts
@@ -5,6 +5,7 @@
 import type {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
 import type {Color, TextAttributes} from '../constants.js';
+import {TextTypeface} from '../constants.js';
 import {Ink2Manager} from '../ink2_manager.js';
 import {hexToColor} from '../pdf_viewer_utils.js';
 
@@ -63,23 +64,30 @@
         }
 
         accessor currentColor: Color = hexToColor(TEXT_COLORS[0]!.color);
-        accessor currentSize: number = TEXT_SIZES[0]!;
-        accessor currentTypeface: string = '';
+        accessor currentSize: number = TEXT_SIZES[3]!;
+        accessor currentTypeface: TextTypeface = TextTypeface.SANS_SERIF;
         accessor colors: ColorOption[] = TEXT_COLORS;
-        accessor fontNames: string[] = [];
+        accessor fontNames: TextTypeface[] = [
+          TextTypeface.SANS_SERIF,
+          TextTypeface.SERIF,
+          TextTypeface.MONOSPACE,
+        ];
         accessor sizes: number[] = TEXT_SIZES;
 
-        override firstUpdated() {
-          Ink2Manager.getInstance().getTextAnnotationFontNames().then(
-              fontNames => {
-                // Set the fontNames and then update the text.
-                this.fontNames = fontNames;
-                this.onTextAttributesChanged(
-                    Ink2Manager.getInstance().getCurrentTextAttributes());
-              });
+        getLabelForTypeface(typeface: TextTypeface): string {
+          // TODO(crbug.com/402547554): These strings should be retrieved from
+          // loadTimeData so they are internationalized once they are final.
+          switch (typeface) {
+            case TextTypeface.SANS_SERIF:
+              return 'Sans-serif';
+            case TextTypeface.SERIF:
+              return 'Serif';
+            case TextTypeface.MONOSPACE:
+              return 'Fixed-width';
+          }
         }
 
-        isSelectedTypeface(typeface: string): boolean {
+        isSelectedTypeface(typeface: TextTypeface): boolean {
           return typeface === this.currentTypeface;
         }
 
@@ -89,7 +97,7 @@
 
         onTypefaceSelected(e: Event) {
           const newValue = (e.target as HTMLSelectElement).value;
-          Ink2Manager.getInstance().setTextTypeface(newValue);
+          Ink2Manager.getInstance().setTextTypeface(newValue as TextTypeface);
         }
 
         onSizeSelected(e: Event) {
@@ -121,8 +129,9 @@
   currentColor: Color;
   currentSize: number;
   currentTypeface: string;
-  fontNames: string[];
+  fontNames: TextTypeface[];
   sizes: number[];
+  getLabelForTypeface(typeface: TextTypeface): string;
   isSelectedTypeface(typeface: string): boolean;
   isSelectedSize(size: number): boolean;
   onTypefaceSelected(e: Event): void;
diff --git a/chrome/browser/resources/pdf/elements/viewer_text_bottom_toolbar.html.ts b/chrome/browser/resources/pdf/elements/viewer_text_bottom_toolbar.html.ts
index 93a4464..bc8f8d26 100644
--- a/chrome/browser/resources/pdf/elements/viewer_text_bottom_toolbar.html.ts
+++ b/chrome/browser/resources/pdf/elements/viewer_text_bottom_toolbar.html.ts
@@ -20,7 +20,7 @@
         ${this.fontNames.map(typeface => html`
           <option value="${typeface}"
               ?selected="${this.isSelectedTypeface(typeface)}">
-            ${typeface}
+            ${this.getLabelForTypeface(typeface)}
           </option>`)}
       </select>
       <select class="md-select size-select" @change="${this.onSizeSelected}">
diff --git a/chrome/browser/resources/pdf/elements/viewer_text_side_panel.html.ts b/chrome/browser/resources/pdf/elements/viewer_text_side_panel.html.ts
index 0abe923..53194f3a 100644
--- a/chrome/browser/resources/pdf/elements/viewer_text_side_panel.html.ts
+++ b/chrome/browser/resources/pdf/elements/viewer_text_side_panel.html.ts
@@ -19,7 +19,7 @@
         ${this.fontNames.map(typeface => html`
           <option value="${typeface}"
               ?selected="${this.isSelectedTypeface(typeface)}">
-            ${typeface}
+            ${this.getLabelForTypeface(typeface)}
           </option>`)}
       </select>
       <select class="md-select" @change="${this.onSizeSelected}">
diff --git a/chrome/browser/resources/pdf/ink2_manager.ts b/chrome/browser/resources/pdf/ink2_manager.ts
index 62c0ba8..4fcf73b 100644
--- a/chrome/browser/resources/pdf/ink2_manager.ts
+++ b/chrome/browser/resources/pdf/ink2_manager.ts
@@ -6,7 +6,7 @@
 import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js';
 
 import type {AnnotationBrush, Color, Point, TextAnnotation, TextAttributes, TextBoxRect, TextStyles} from './constants.js';
-import {AnnotationBrushType, TextAlignment, TextStyle} from './constants.js';
+import {AnnotationBrushType, TextAlignment, TextStyle, TextTypeface} from './constants.js';
 import {PluginController, PluginControllerEventType} from './controller.js';
 import type {Viewport} from './viewport.js';
 
@@ -40,7 +40,7 @@
   private annotations_: Map<number, Map<number, TextAnnotation>> = new Map();
   // The attributes selected by the user for new annotations.
   private attributes_: TextAttributes = {
-    typeface: '',
+    typeface: TextTypeface.SANS_SERIF,
     size: 12,
     color: {r: 0, g: 0, b: 0},
     alignment: TextAlignment.LEFT,
@@ -54,7 +54,6 @@
   // user is editing. Null if the user is not editing an annotation or is
   // creating a new annotation using |attributes_|.
   private existingAnnotationAttributes_: TextAttributes|null = null;
-  private fontNamesResolver_: PromiseResolver<string[]>|null = null;
   private pageNumber_: number = -1;
   private pluginController_: PluginController = PluginController.getInstance();
   private viewport_: Viewport|null = null;
@@ -220,20 +219,7 @@
     this.setAnnotationBrushInPlugin_();
   }
 
-  getTextAnnotationFontNames(): Promise<string[]> {
-    if (this.fontNamesResolver_ === null) {
-      this.fontNamesResolver_ = new PromiseResolver();
-      this.pluginController_.getTextAnnotFontNames().then(fontsMessage => {
-        assert(this.fontNamesResolver_);
-        this.fontNamesResolver_.resolve(fontsMessage.data);
-        assert(fontsMessage.data.length > 0);
-        this.setTextTypeface(fontsMessage.data[0]!);
-      });
-    }
-    return this.fontNamesResolver_.promise;
-  }
-
-  setTextTypeface(typeface: string) {
+  setTextTypeface(typeface: TextTypeface) {
     const current = this.getCurrentTextAttributes();
     if (current.typeface === typeface) {
       return;
diff --git a/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts b/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts
index 2bb0178..ef975d9 100644
--- a/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts
+++ b/chrome/browser/resources/pdf/pdf_viewer_wrapper.ts
@@ -14,7 +14,7 @@
 export {Bookmark} from './bookmark_type.js';
 export {BrowserApi, ZoomBehavior} from './browser_api.js';
 // <if expr="enable_pdf_ink2">
-export {AnnotationBrush, AnnotationBrushType, Color, TextAlignment, TextAnnotation, TextAttributes, TextStyle} from './constants.js';
+export {AnnotationBrush, AnnotationBrushType, Color, TextAlignment, TextAnnotation, TextAttributes, TextStyle, TextTypeface} from './constants.js';
 // </if>
 // <if expr="enable_pdf_ink2 or enable_ink">
 export {AnnotationMode} from './constants.js';
diff --git a/chrome/browser/resources/print_preview/BUILD.gn b/chrome/browser/resources/print_preview/BUILD.gn
index 9bbb0e1..3f5fca2d 100644
--- a/chrome/browser/resources/print_preview/BUILD.gn
+++ b/chrome/browser/resources/print_preview/BUILD.gn
@@ -29,7 +29,6 @@
     "ui/destination_list.ts",
     "ui/destination_list_item.ts",
     "ui/destination_select.ts",
-    "ui/destination_settings.ts",
     "ui/dpi_settings.ts",
     "ui/duplex_settings.ts",
     "ui/header.ts",
@@ -75,6 +74,8 @@
     "print_preview.ts",
     "print_preview_utils.ts",
     "ui/debouncer.ts",
+    "ui/destination_settings.html.ts",
+    "ui/destination_settings.ts",
     "ui/highlight_utils.ts",
     "ui/input_mixin.ts",
     "ui/input_mixin_lit.ts",
diff --git a/chrome/browser/resources/print_preview/ui/app.html b/chrome/browser/resources/print_preview/ui/app.html
index 4ec27454..d2c513c9 100644
--- a/chrome/browser/resources/print_preview/ui/app.html
+++ b/chrome/browser/resources/print_preview/ui/app.html
@@ -47,11 +47,12 @@
 </div>
 <print-preview-sidebar id="sidebar"
     destination-state="{{destinationState_}}"
-    controls-managed="[[controlsManaged_]]" destination="{{destination_}}"
+    controls-managed="[[controlsManaged_]]" destination="[[destination_]]"
     error="{{error_}}" is-pdf="[[!documentSettings_.isModifiable]]"
     page-count="[[documentSettings_.pageCount]]"
     settings="[[settings]]" state="[[state]]"
     on-focus="onSidebarFocus_"
+    on-destination-changed="onDestinationChanged_"
     on-destination-capabilities-changed="onDestinationCapabilitiesChanged_"
 <if expr="is_macosx">
     on-open-pdf-in-preview="onOpenPdfInPreview_"
diff --git a/chrome/browser/resources/print_preview/ui/app.ts b/chrome/browser/resources/print_preview/ui/app.ts
index 34fcdbd74..fa1b0ad 100644
--- a/chrome/browser/resources/print_preview/ui/app.ts
+++ b/chrome/browser/resources/print_preview/ui/app.ts
@@ -537,6 +537,10 @@
     this.$.state.transitTo(State.CLOSING);
   }
 
+  private onDestinationChanged_(e: CustomEvent<{value: Destination}>) {
+    this.destination_ = e.detail.value;
+  }
+
   private onDestinationCapabilitiesChanged_() {
     this.$.model.updateSettingsFromDestination();
   }
diff --git a/chrome/browser/resources/print_preview/ui/destination_select.html b/chrome/browser/resources/print_preview/ui/destination_select.html
index ca70525..aa5a352e 100644
--- a/chrome/browser/resources/print_preview/ui/destination_select.html
+++ b/chrome/browser/resources/print_preview/ui/destination_select.html
@@ -12,16 +12,19 @@
         style="background-image:
             [[getBackgroundImages_(selectedValue, destination,
                                    noDestinations, dark)]];"
-        disabled$="[[disabled]]"
-        value="[[selectedValue]]" on-change="onSelectChange">
+        disabled$="[[disabled]]" on-change="onSelectChange">
       <template is="dom-repeat" items="[[recentDestinationList]]">
-        <option value="[[item.key]]">[[item.displayName]]</option>
+        <option value="[[item.key]]"
+            selected$="[[isSelected_(item.key, selectedValue)]]">
+          [[item.displayName]]
+        </option>
       </template>
-      <option value="[[pdfDestinationKey_]]" hidden$="[[pdfPrinterDisabled]]">
+      <option value="[[pdfDestinationKey_]]" hidden$="[[pdfPrinterDisabled]]"
+          selected$="[[isSelected_(pdfDestinationKey_, selectedValue)]]">
         $i18n{printToPDF}
       </option>
-      <option value="noDestinations"
-              hidden$="[[!noDestinations]]" selected$="[[noDestinations]]">
+      <option value="noDestinations" hidden$="[[!noDestinations]]"
+          selected$="[[noDestinations]]">
         $i18n{noDestinationsMessage}
       </option>
       <option value="seeMore" aria-label$="[[i18n(seeMoreDestinationsLabel)]]">
diff --git a/chrome/browser/resources/print_preview/ui/destination_select.ts b/chrome/browser/resources/print_preview/ui/destination_select.ts
index 930b822..a58570ad 100644
--- a/chrome/browser/resources/print_preview/ui/destination_select.ts
+++ b/chrome/browser/resources/print_preview/ui/destination_select.ts
@@ -45,19 +45,32 @@
 
   static get properties() {
     return {
-      activeUser: String,
-
-      dark: Boolean,
+      dark: {
+        type: Boolean,
+        value: false,
+      },
 
       destination: Object,
 
-      disabled: Boolean,
+      disabled: {
+        type: Boolean,
+        value: false,
+      },
 
-      loaded: Boolean,
+      loaded: {
+        type: Boolean,
+        value: false,
+      },
 
-      noDestinations: Boolean,
+      noDestinations: {
+        type: Boolean,
+        value: false,
+      },
 
-      pdfPrinterDisabled: Boolean,
+      pdfPrinterDisabled: {
+        type: Boolean,
+        value: false,
+      },
 
       recentDestinationList: Array,
 
@@ -68,7 +81,6 @@
     };
   }
 
-  declare activeUser: string;
   declare dark: boolean;
   declare destination: Destination;
   declare disabled: boolean;
@@ -157,6 +169,10 @@
     return this.shadowRoot!.querySelectorAll<HTMLOptionElement>(
         'option:not([hidden])');
   }
+
+  private isSelected_(destinationKey: string): boolean {
+    return this.selectedValue === destinationKey;
+  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/print_preview/ui/destination_settings.html b/chrome/browser/resources/print_preview/ui/destination_settings.html
deleted file mode 100644
index a03936a..0000000
--- a/chrome/browser/resources/print_preview/ui/destination_settings.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<style include="print-preview-shared">
-</style>
-<print-preview-destination-select id="destinationSelect"
-    active-user="[[activeUser_]]" dark="[[dark]]"
-    destination="[[destination]]"
-    disabled="[[shouldDisableDropdown_(
-                    destinationState, state, disabled)]]"
-    loaded="[[loaded_]]"
-    no-destinations="[[noDestinations_]]"
-    pdf-printer-disabled="[[pdfPrinterDisabled_]]"
-    recent-destination-list="[[displayedDestinations_]]"
-    on-selected-option-change="onSelectedDestinationOptionChange_">
-</print-preview-destination-select>
-<cr-lazy-render id="destinationDialog">
-  <template>
-    <print-preview-destination-dialog
-        destination-store="[[destinationStore_]]"
-        recent-destination-list="[[recentDestinationList_]]"
-        on-close="onDialogClose_">
-    </print-preview-destination-dialog>
-  </template>
-</cr-lazy-render>
diff --git a/chrome/browser/resources/print_preview/ui/destination_settings.html.ts b/chrome/browser/resources/print_preview/ui/destination_settings.html.ts
new file mode 100644
index 0000000..e0e2c305
--- /dev/null
+++ b/chrome/browser/resources/print_preview/ui/destination_settings.html.ts
@@ -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.
+
+import {html} from 'chrome://resources/lit/v3_0/lit.rollup.js';
+
+import type {DestinationSettingsElement} from './destination_settings.js';
+
+export function getHtml(this: DestinationSettingsElement) {
+  // clang-format off
+  return html`<!--_html_template_start_-->
+<print-preview-destination-select id="destinationSelect"
+    ?dark="${this.dark}"
+    .destination="${this.destination}"
+    ?disabled="${this.shouldDisableDropdown_()}"
+    ?loaded="${this.loaded_}"
+    ?no-destinations="${this.noDestinations_}"
+    ?pdf-printer-disabled="${this.pdfPrinterDisabled_}"
+    .recentDestinationList="${this.displayedDestinations_}"
+    @selected-option-change="${this.onSelectedDestinationOptionChange_}">
+</print-preview-destination-select>
+<cr-lazy-render-lit id="destinationDialog" .template="${() => html`
+  <print-preview-destination-dialog
+      .destinationStore="${this.destinationStore_}"
+      @close="${this.onDialogClose_}">
+  </print-preview-destination-dialog>
+`}">
+</cr-lazy-render-lit>
+<!--_html_template_end_-->`;
+  // clang-format on
+}
diff --git a/chrome/browser/resources/print_preview/ui/destination_settings.ts b/chrome/browser/resources/print_preview/ui/destination_settings.ts
index b70cb91..18c175ab8 100644
--- a/chrome/browser/resources/print_preview/ui/destination_settings.ts
+++ b/chrome/browser/resources/print_preview/ui/destination_settings.ts
@@ -2,34 +2,29 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
-import 'chrome://resources/cr_elements/cr_hidden_style.css.js';
-import 'chrome://resources/cr_elements/cr_shared_vars.css.js';
+import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render_lit.js';
 import './destination_dialog.js';
 import './destination_select.js';
-import './print_preview_shared.css.js';
-import './print_preview_vars.css.js';
-import './throbber.css.js';
-import './settings_section.js';
 import '/strings.m.js';
 
-import type {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.js';
-import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js';
-import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener_mixin.js';
+import type {CrLazyRenderLitElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render_lit.js';
+import {I18nMixinLit} from 'chrome://resources/cr_elements/i18n_mixin_lit.js';
+import {WebUiListenerMixinLit} from 'chrome://resources/cr_elements/web_ui_listener_mixin_lit.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {EventTracker} from 'chrome://resources/js/event_tracker.js';
-import {beforeNextRender, PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {CrLitElement} from 'chrome://resources/lit/v3_0/lit.rollup.js';
+import type {PropertyValues} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 
 import type {Destination, RecentDestination} from '../data/destination.js';
 import {createRecentDestinationKey, isPdfPrinter, makeRecentDestination, PrinterType} from '../data/destination.js';
-
 import {DestinationErrorType, DestinationStore, DestinationStoreEventType} from '../data/destination_store.js';
 import {Error, State} from '../data/state.js';
 
 import type {PrintPreviewDestinationDialogElement} from './destination_dialog.js';
 import type {PrintPreviewDestinationSelectElement} from './destination_select.js';
-import {getTemplate} from './destination_settings.html.js';
-import {SettingsMixin} from './settings_mixin.js';
+import {getHtml} from './destination_settings.html.js';
+import {getCss as getPrintPreviewSharedLitCss} from './print_preview_shared_lit.css.js';
+import {SettingsMixinLit} from './settings_mixin_lit.js';
 
 export enum DestinationState {
   INIT = 0,
@@ -50,13 +45,13 @@
 export interface PrintPreviewDestinationSettingsElement {
   $: {
     destinationDialog:
-        CrLazyRenderElement<PrintPreviewDestinationDialogElement>,
+        CrLazyRenderLitElement<PrintPreviewDestinationDialogElement>,
     destinationSelect: PrintPreviewDestinationSelectElement,
   };
 }
 
 const PrintPreviewDestinationSettingsElementBase =
-    I18nMixin(WebUiListenerMixin(SettingsMixin(PolymerElement)));
+    I18nMixinLit(WebUiListenerMixinLit(SettingsMixinLit(CrLitElement)));
 
 export class PrintPreviewDestinationSettingsElement extends
     PrintPreviewDestinationSettingsElementBase {
@@ -64,78 +59,61 @@
     return 'print-preview-destination-settings';
   }
 
-  static get template() {
-    return getTemplate();
+  static override get styles() {
+    return [
+      getPrintPreviewSharedLitCss(),
+    ];
   }
 
-  static get properties() {
+  override render() {
+    return getHtml.bind(this)();
+  }
+
+  static override get properties() {
     return {
-      dark: Boolean,
+      dark: {type: Boolean},
 
       destination: {
         type: Object,
         notify: true,
-        value: null,
       },
 
       destinationState: {
         type: Number,
         notify: true,
-        value: DestinationState.INIT,
-        observer: 'updateDestinationSelect_',
       },
 
-      disabled: Boolean,
+      disabled: {type: Boolean},
 
       error: {
         type: Number,
         notify: true,
-        observer: 'onErrorChanged_',
       },
 
-      firstLoad: Boolean,
-
-      state: Number,
-
-      destinationStore_: {
-        type: Object,
-        value: null,
-      },
-
-      displayedDestinations_: Array,
-
-      isDialogOpen_: {
-        type: Boolean,
-        value: false,
-      },
-
-      noDestinations_: {
-        type: Boolean,
-        value: false,
-      },
-
-      pdfPrinterDisabled_: Boolean,
-
-      loaded_: {
-        type: Boolean,
-        computed: 'computeLoaded_(destinationState, destination)',
-      },
+      firstLoad: {type: Boolean},
+      state: {type: Number},
+      destinationStore_: {type: Object},
+      displayedDestinations_: {type: Array},
+      isDialogOpen_: {type: Boolean},
+      noDestinations_: {type: Boolean},
+      pdfPrinterDisabled_: {type: Boolean},
+      loaded_: {type: Boolean},
     };
   }
 
-  declare dark: boolean;
-  declare destination: Destination;
-  declare destinationState: DestinationState;
-  declare disabled: boolean;
-  declare error: Error;
-  declare firstLoad: boolean;
-  declare state: State;
-  declare private destinationStore_: DestinationStore|null;
-  declare private displayedDestinations_: Destination[];
-  declare private isDialogOpen_: boolean;
-  declare private noDestinations_: boolean;
-  declare private pdfPrinterDisabled_: boolean;
-  declare private loaded_: boolean;
+  accessor dark: boolean;
+  accessor destination: Destination|null = null;
+  accessor destinationState: DestinationState = DestinationState.INIT;
+  accessor disabled: boolean;
+  accessor error: Error;
+  accessor firstLoad: boolean;
+  accessor state: State;
+  protected accessor destinationStore_: DestinationStore|null = null;
+  protected accessor displayedDestinations_: Destination[];
+  private accessor isDialogOpen_: boolean = false;
+  protected accessor noDestinations_: boolean = false;
+  protected accessor pdfPrinterDisabled_: boolean;
+  protected accessor loaded_: boolean;
 
   private lastUser_: string = '';
   private tracker_: EventTracker = new EventTracker();
@@ -172,6 +150,27 @@
     this.tracker_.removeAll();
   }
 
+  override willUpdate(changedProperties: PropertyValues<this>) {
+    super.willUpdate(changedProperties);
+
+    if (changedProperties.has('error')) {
+      this.onErrorChanged_();
+    }
+
+    if (changedProperties.has('destinationState') ||
+        changedProperties.has('destination')) {
+      this.loaded_ = this.computeLoaded_();
+    }
+  }
+
+  override updated(changedProperties: PropertyValues<this>) {
+    super.updated(changedProperties);
+
+    if (changedProperties.has('destinationState')) {
+      this.updateDestinationSelect_();
+    }
+  }
+
   /**
    * @param defaultPrinter The system default printer ID.
    * @param pdfPrinterDisabled Whether the PDF printer is disabled.
@@ -233,11 +232,13 @@
     this.updateRecentDestinations_();
   }
 
-  private onDestinationCapabilitiesReady_() {
-    this.dispatchEvent(new CustomEvent('destination-capabilities-changed', {
-      bubbles: true,
-      composed: true,
-    }));
+  private async onDestinationCapabilitiesReady_() {
+    // Wait for any 'destination-changed' events to be fired first.
+    await this.updateComplete;
+
+    this.fire(
+        'destination-capabilities-changed',
+        this.destinationStore_!.selectedDestination);
     this.updateRecentDestinations_();
     if (this.destinationState === DestinationState.SET) {
       this.destinationState = DestinationState.UPDATED;
@@ -311,12 +312,19 @@
       indexFound = NUM_PERSISTED_DESTINATIONS - 1;
     }
 
+    // Create a clone first, otherwise array modifications will not be detected
+    // by the underlying Observable instance.
+    const recentDestinationsClone = recentDestinations.slice();
+
     if (indexFound !== -1) {
-      this.setSettingSplice('recentDestinations', indexFound, 1, null);
+      // Remove from the list if it already exists, it will be re-added to the
+      // front below.
+      recentDestinationsClone.splice(indexFound, 1);
     }
 
     // Add the most recent destination
-    this.setSettingSplice('recentDestinations', 0, 0, newDestination);
+    recentDestinationsClone.splice(0, 0, newDestination);
+    this.setSetting('recentDestinations', recentDestinationsClone);
 
     // The dropdown needs to be updated if a new printer or one not currently
     // visible in the dropdown has been added.
@@ -351,7 +359,7 @@
   /**
    * @return Whether the destinations dropdown should be disabled.
    */
-  private shouldDisableDropdown_(): boolean {
+  protected shouldDisableDropdown_(): boolean {
     return this.state === State.FATAL_ERROR ||
         (this.destinationState === DestinationState.UPDATED && this.disabled &&
          this.state !== State.NOT_READY);
@@ -369,7 +377,7 @@
    * @param e Event containing the key of the recent destination that was
    *     selected, or "seeMore".
    */
-  private onSelectedDestinationOptionChange_(e: CustomEvent<string>) {
+  protected onSelectedDestinationOptionChange_(e: CustomEvent<string>) {
     const value = e.detail;
     if (value === 'seeMore') {
       this.destinationStore_!.startLoadAllDestinations();
@@ -380,7 +388,7 @@
     }
   }
 
-  private onDialogClose_() {
+  protected onDialogClose_() {
     // Reset the select value if the user dismissed the dialog without
     // selecting a new destination.
     this.updateDestinationSelect_();
@@ -398,12 +406,11 @@
 
     const shouldFocus =
         this.destinationState !== DestinationState.SET && !this.firstLoad;
-    beforeNextRender(this.$.destinationSelect, () => {
-      this.$.destinationSelect.updateDestination();
-      if (shouldFocus) {
-        this.$.destinationSelect.focus();
-      }
-    });
+
+    this.$.destinationSelect.updateDestination();
+    if (shouldFocus) {
+      this.$.destinationSelect.focus();
+    }
   }
 
   getDestinationStoreForTest(): DestinationStore {
@@ -412,6 +419,8 @@
   }
 }
 
+export type DestinationSettingsElement = PrintPreviewDestinationSettingsElement;
+
 declare global {
   interface HTMLElementTagNameMap {
     'print-preview-destination-settings':
diff --git a/chrome/browser/resources/print_preview/ui/sidebar.html b/chrome/browser/resources/print_preview/ui/sidebar.html
index 3c5bbdf..d9a588f 100644
--- a/chrome/browser/resources/print_preview/ui/sidebar.html
+++ b/chrome/browser/resources/print_preview/ui/sidebar.html
@@ -9,7 +9,7 @@
       destination-state="${this.destinationState}"
       @destination-state-changed="${this.onDestinationStateChanged_}"
       error="${this.error}" @error-changed="${this.onErrorChanged_}"
-      ?first-load="${this.firstLoad_}" .settings="${this.settings}"
+      ?first-load="${this.firstLoad_}"
       .state="${this.state}" ?app-kiosk-mode="${this.isInAppKioskMode_}"
       ?disabled="${this.controlsDisabled_}"
       available class="settings-section"
diff --git a/chrome/browser/resources/print_preview/ui/sidebar.ts b/chrome/browser/resources/print_preview/ui/sidebar.ts
index 8aa39f1..e5e71e1 100644
--- a/chrome/browser/resources/print_preview/ui/sidebar.ts
+++ b/chrome/browser/resources/print_preview/ui/sidebar.ts
@@ -229,10 +229,12 @@
 
   protected onDestinationChanged_(e: CustomEvent<{value: Destination}>) {
     this.destination = e.detail.value;
+    this.destinationCapabilities_ = null;
   }
 
-  protected onDestinationCapabilitiesChanged_() {
+  protected onDestinationCapabilitiesChanged_(e: CustomEvent<Destination>) {
     assert(this.destination);
+    assert(e.detail.id === this.destination.id);
     // When `this.destination.capabilities` changes it is always a new object.
     this.destinationCapabilities_ = this.destination.capabilities;
   }
diff --git a/chrome/browser/resources/print_preview/ui/throbber_lit.css b/chrome/browser/resources/print_preview/ui/throbber_lit.css
index 14bd379..c05b602c 100644
--- a/chrome/browser/resources/print_preview/ui/throbber_lit.css
+++ b/chrome/browser/resources/print_preview/ui/throbber_lit.css
@@ -13,4 +13,3 @@
   height: var(--throbber-size);
   width: var(--throbber-size);
 }
-
diff --git a/chrome/browser/resources/side_panel/customize_chrome/app.html.ts b/chrome/browser/resources/side_panel/customize_chrome/app.html.ts
index b3019bb..6504139 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/app.html.ts
+++ b/chrome/browser/resources/side_panel/customize_chrome/app.html.ts
@@ -33,14 +33,14 @@
       </sp-heading>
       <cr-icon icon="cr:chevron-right" slot="suffix-icon"></cr-icon>
     </cr-button>
-     ${this.isSourceTabFirstPartyNtp_ ? html`<hr class="sp-cards-separator">
+     ${this.isSourceTabFirstPartyNtp_() ? html`<hr class="sp-cards-separator">
     <div id="shortcuts" class="section sp-card">
       <sp-heading hide-back-button>
         <h2 slot="heading">$i18n{shortcutsHeader}</h2>
       </sp-heading>
       <customize-chrome-shortcuts></customize-chrome-shortcuts>
     </div>`: ''}
-    ${(this.modulesEnabled_ && this.isSourceTabFirstPartyNtp_) ? html`
+    ${(this.modulesEnabled_ && this.isSourceTabFirstPartyNtp_()) ? html`
       <hr class="sp-cards-separator">
       <div id="modules" class="section sp-card">
         <sp-heading hide-back-button>
@@ -77,7 +77,7 @@
         </div>
       </div>
     ` : ''}
-    ${this.footerEnabled_ ? html`
+    ${(this.footerEnabled_ && this.isSourceTabExtension_()) ? html`
       <hr class="sp-cards-separator">
       <div id="footer" class="section sp-card">
         <sp-heading hide-back-button>
diff --git a/chrome/browser/resources/side_panel/customize_chrome/app.ts b/chrome/browser/resources/side_panel/customize_chrome/app.ts
index ea99b2d..4fbb739a 100644
--- a/chrome/browser/resources/side_panel/customize_chrome/app.ts
+++ b/chrome/browser/resources/side_panel/customize_chrome/app.ts
@@ -29,7 +29,7 @@
 import type {CategoriesElement} from './categories.js';
 import {CustomizeChromeImpression, recordCustomizeChromeImpression} from './common.js';
 import type {BackgroundCollection, CustomizeChromePageHandlerInterface} from './customize_chrome.mojom-webui.js';
-import {ChromeWebStoreCategory, ChromeWebStoreCollection, CustomizeChromeSection} from './customize_chrome.mojom-webui.js';
+import {ChromeWebStoreCategory, ChromeWebStoreCollection, CustomizeChromeSection, NewTabPageType} from './customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from './customize_chrome_api_proxy.js';
 import type {ThemesElement} from './themes.js';
 
@@ -82,7 +82,7 @@
       extensionsCardEnabled_: {type: Boolean},
       footerEnabled_: {type: Boolean},
       wallpaperSearchEnabled_: {type: Boolean},
-      isSourceTabFirstPartyNtp_: {type: Boolean},
+      newTabPageType_: {type: NewTabPageType},
       showEditTheme_: {type: Boolean},
     };
   }
@@ -106,7 +106,8 @@
       loadTimeData.getBoolean('footerEnabled');
   protected accessor wallpaperSearchEnabled_: boolean =
       loadTimeData.getBoolean('wallpaperSearchEnabled');
-  protected accessor isSourceTabFirstPartyNtp_: boolean = true;
+  protected accessor newTabPageType_: NewTabPageType =
+      NewTabPageType.kFirstPartyWebUI;
   protected accessor showEditTheme_: boolean = true;
   private scrollToSectionListenerId_: number|null = null;
   private attachedTabStateUpdatedId_: number|null = null;
@@ -142,17 +143,16 @@
     this.attachedTabStateUpdatedId_ =
         CustomizeChromeApiProxy.getInstance()
             .callbackRouter.attachedTabStateUpdated.addListener(
-                (isSourceTabFirstPartyNtp: boolean) => {
-                  if (this.isSourceTabFirstPartyNtp_ ===
-                      isSourceTabFirstPartyNtp) {
+                (newTabPageType: NewTabPageType) => {
+                  if (this.newTabPageType_ === newTabPageType) {
                     return;
                   }
 
-                  this.isSourceTabFirstPartyNtp_ = isSourceTabFirstPartyNtp;
+                  this.newTabPageType_ = newTabPageType;
 
                   // Since some pages aren't supported in non first party mode,
                   // change the section back to the overview.
-                  if (!this.isSourceTabFirstPartyNtp_ &&
+                  if (!this.isSourceTabFirstPartyNtp_() &&
                       !this.pageSupportedOnNonFirstPartyNtps()) {
                     this.page_ = CustomizeChromePage.OVERVIEW;
                   }
@@ -207,6 +207,14 @@
         this.setThemeEditableId_);
   }
 
+  protected isSourceTabFirstPartyNtp_(): boolean {
+    return this.newTabPageType_ === NewTabPageType.kFirstPartyWebUI;
+  }
+
+  protected isSourceTabExtension_(): boolean {
+    return this.newTabPageType_ === NewTabPageType.kExtension;
+  }
+
   protected async onBackClick_() {
     switch (this.page_) {
       case CustomizeChromePage.CATEGORIES:
diff --git a/chrome/browser/resources/side_panel/read_anything/app.ts b/chrome/browser/resources/side_panel/read_anything/app.ts
index d2f4dd3..8e91616 100644
--- a/chrome/browser/resources/side_panel/read_anything/app.ts
+++ b/chrome/browser/resources/side_panel/read_anything/app.ts
@@ -35,7 +35,7 @@
 import type {ReadAnythingToolbarElement} from './read_anything_toolbar.js';
 import type {SpeechBrowserProxy} from './speech_browser_proxy.js';
 import {SpeechBrowserProxyImpl} from './speech_browser_proxy.js';
-import {areVoicesEqual, AVAILABLE_GOOGLE_TTS_LOCALES, convertLangOrLocaleForVoicePackManager, convertLangOrLocaleToExactVoicePackLocale, convertLangToAnAvailableLangIfPresent, doesLanguageHaveNaturalVoices, getNaturalVoiceOrDefault, getVoicePackConvertedLangIfExists, isNatural, isVoicePackStatusError, isVoicePackStatusSuccess, mojoVoicePackStatusToVoicePackStatusEnum, VoiceClientSideStatusCode, VoicePackServerStatusErrorCode, VoicePackServerStatusSuccessCode} from './voice_language_util.js';
+import {AVAILABLE_GOOGLE_TTS_LOCALES, convertLangOrLocaleForVoicePackManager, convertLangOrLocaleToExactVoicePackLocale, convertLangToAnAvailableLangIfPresent, doesLanguageHaveNaturalVoices, getVoicePackConvertedLangIfExists, isNatural, isVoicePackStatusError, isVoicePackStatusSuccess, mojoVoicePackStatusToVoicePackStatusEnum, VoiceClientSideStatusCode, VoicePackServerStatusErrorCode, VoicePackServerStatusSuccessCode} from './voice_language_util.js';
 import type {VoicePackStatus} from './voice_language_util.js';
 import {VoiceNotificationManager} from './voice_notification_manager.js';
 
@@ -125,7 +125,7 @@
   // request the install.
   private waitingForNewEngine_ = false;
 
-  protected accessor selectedVoice_: SpeechSynthesisVoice|undefined;
+  protected accessor selectedVoice_: SpeechSynthesisVoice|null = null;
   // The set of languages currently enabled for use by Read Aloud. This
   // includes user-enabled languages and auto-downloaded languages. The former
   // are stored in preferences. The latter are not.
@@ -935,129 +935,16 @@
       // If we go from having no available voices to having voices available,
       // restore voice settings from preferences.
       this.restoreEnabledLanguagesFromPref();
-      this.selectPreferredVoice();
+      this.selectUserPreferredVoice();
     }
 
     // If voice was selected automatically and not by the user, check if
     // there's a higher quality voice available now.
-    if (!this.currentVoiceIsUserChosen_()) {
-      const naturalVoicesForLang =
-          this.voicePackController_.getAvailableVoices().filter(
-              voice => isNatural(voice) &&
-                  voice.lang.startsWith(
-                      chrome.readingMode.baseLanguageForSpeech));
-
-      if (naturalVoicesForLang) {
-        this.selectedVoice_ = naturalVoicesForLang[0];
-        this.resetSpeechPostSettingChange_();
-      }
-    }
-
-    // Now that the voice list has changed, refresh the VoicePackStatuses in
-    // case a language has been uninstalled.
-    this.voicePackController_.refreshVoicePackStatuses();
+    this.voicePackController_.updateAutoSelectedVoiceToNaturalVoice();
 
     // If the selected voice is now unavailable, such as after an uninstall,
     // reselect a new voice.
-    if (this.selectedVoice_ &&
-        !this.voicePackController_.isVoiceAvailable(this.selectedVoice_)) {
-      this.selectedVoice_ = undefined;
-    }
-
-    if (!this.selectedVoice_) {
-      this.getSpeechSynthesisVoice();
-    }
-  }
-
-  getSpeechSynthesisVoice(): SpeechSynthesisVoice|undefined {
-    if (!this.selectedVoice_) {
-      this.selectedVoice_ = this.defaultVoice();
-    }
-    return this.selectedVoice_;
-  }
-
-  defaultVoice(): SpeechSynthesisVoice|undefined {
-    const baseLang = this.voicePackController_.getCurrentLanguage();
-    const allPossibleVoices = this.getVoices_();
-    const voicesForLanguage =
-        allPossibleVoices.filter(voice => voice.lang.startsWith(baseLang));
-
-    if (!voicesForLanguage || (voicesForLanguage.length === 0)) {
-      // Stay with the current voice if no voices are available for this
-      // language.
-      return this.selectedVoice_ ? this.selectedVoice_ :
-                                   getNaturalVoiceOrDefault(allPossibleVoices);
-    }
-
-    // First try to choose a voice only from currently enabled locales for this
-    // language.
-    const voicesForCurrentEnabledLocale = voicesForLanguage.filter(
-        v => this.voicePackController_.isLangEnabled(v.lang));
-    if (!voicesForCurrentEnabledLocale ||
-        !voicesForCurrentEnabledLocale.length) {
-      // If there's no enabled locales for this language, check for any other
-      // voices for enabled locales.
-      const allVoicesForEnabledLocales = allPossibleVoices.filter(
-          v => this.voicePackController_.isLangEnabled(v.lang));
-      if (!allVoicesForEnabledLocales.length) {
-        // If there are no voices for the enabled locales, or no enabled
-        // locales at all, we can't select a voice. So return undefined so we
-        // can disable the play button.
-        return undefined;
-      } else {
-        return getNaturalVoiceOrDefault(allVoicesForEnabledLocales);
-      }
-    }
-
-    return getNaturalVoiceOrDefault(voicesForCurrentEnabledLocale);
-  }
-
-  // Attempt to get a new voice using the current language. In theory, the
-  // previously unavailable voice should no longer be showing up in
-  // getVoices, but we ensure that the alternative voice does not match
-  // the previously unavailable voice as an extra measure. This method should
-  // only be called when speech synthesis returns an error.
-  getAlternativeVoice(unavailableVoice: SpeechSynthesisVoice|undefined):
-      SpeechSynthesisVoice|undefined {
-    const newVoice = this.defaultVoice();
-
-    // If the default voice is not the same as the original, unavailable voice,
-    // use that, only if the new voice is also defined.
-    if (newVoice !== undefined && !areVoicesEqual(newVoice, unavailableVoice)) {
-      return newVoice;
-    }
-
-    // If the default voice won't work, try another voice in that language.
-    const baseLang = this.voicePackController_.getCurrentLanguage();
-    const voicesForLanguage =
-        this.getVoices_().filter(voice => voice.lang.startsWith(baseLang));
-
-    // TODO: crbug.com/40927698 - It's possible we can get stuck in an infinite
-    // loop of jumping back and forth between two or more invalid voices, if
-    // multiple voices are invalid. Investigate if we need to do more to handle
-    // this case.
-
-    // TODO: crbug.com/336596926 - If there still aren't voices for the
-    // language, attempt to fallback to the browser language, if we're using
-    // the page language.
-    if (!voicesForLanguage || (voicesForLanguage.length === 0)) {
-      return undefined;
-    }
-
-    let voiceIndex = 0;
-    while (voiceIndex < voicesForLanguage.length) {
-      if (!areVoicesEqual(voicesForLanguage[voiceIndex], unavailableVoice)) {
-        // Return another voice in the same language, ensuring we're not
-        // returning the previously unavailable voice for extra safety.
-        return voicesForLanguage[voiceIndex];
-      }
-      voiceIndex++;
-    }
-
-    // TODO: crbug.com/336596926 - Handle language updates if there aren't any
-    // available voices in the current language other than the unavailable
-    // voice.
-    return undefined;
+    this.voicePackController_.updateUnavailableVoiceToDefaultVoice();
   }
 
   private getVoices_(forceRefresh: boolean = false): SpeechSynthesisVoice[] {
@@ -1133,6 +1020,11 @@
         this.voicePackController_.getDisplayNamesForLocaleCodes();
   }
 
+  onCurrentVoiceChange(): void {
+    this.selectedVoice_ = this.voicePackController_.getCurrentVoice();
+    this.resetSpeechPostSettingChange_();
+  }
+
   private logSpeechPlaySession_() {
     // Don't log a playback session just in case something has gotten out of
     // sync and we call stopSpeech before playSpeech.
@@ -1440,8 +1332,7 @@
           chrome.readingMode.getCurrentTextStartIndex(this.lastReadingId_);
     }
     this.highlighter_.highlightCurrentGranularity(
-        axNodeIds, scrollIntoView, shouldUpdateSentenceHighlight,
-        this.selectedVoice_);
+        axNodeIds, scrollIntoView, shouldUpdateSentenceHighlight);
   }
 
   // Gets the accessible text boundary for the given string.
@@ -1535,7 +1426,7 @@
       }
     };
 
-    const voice = this.getSpeechSynthesisVoice();
+    const voice = this.voicePackController_.getCurrentVoiceOrDefault();
     if (!voice) {
       // TODO: crbug.com/40927698 - Handle when no voices are available.
       return;
@@ -1589,6 +1480,13 @@
       return;
     }
 
+    // When we hit an error, stop speech to clear all utterances, update the
+    // button state, and highlighting in order to give visual feedback that
+    // something went wrong.
+    // TODO: crbug.com/40927698 - Consider showing an error message.
+    this.logger_.logSpeechStopSource(chrome.readingMode.engineErrorStopSource);
+    this.speechController_.stopSpeech(PauseActionSource.DEFAULT);
+
     // No appropriate voice is available for the language designated in
     // SpeechSynthesisUtterance lang.
     if (error.error === 'language-unavailable') {
@@ -1598,21 +1496,8 @@
     // The voice designated in SpeechSynthesisUtterance voice attribute
     // is not available.
     if (error.error === 'voice-unavailable') {
-      let newVoice = this.selectedVoice_ ? this.selectedVoice_ : undefined;
-      this.selectedVoice_ = undefined;
-      newVoice = this.getAlternativeVoice(newVoice);
-
-      if (newVoice) {
-        this.selectedVoice_ = newVoice;
-      }
+      this.voicePackController_.onVoiceUnavailableError();
     }
-
-    // When we hit an error, stop speech to clear all utterances, update the
-    // button state, and highlighting in order to give visual feedback that
-    // something went wrong.
-    // TODO: crbug.com/40927698 - Consider showing an error message.
-    this.logger_.logSpeechStopSource(chrome.readingMode.engineErrorStopSource);
-    this.speechController_.stopSpeech(PauseActionSource.DEFAULT);
   }
 
   private extractTextOf(axNodeIds: number[]): string {
@@ -1657,25 +1542,17 @@
     event.preventDefault();
     event.stopPropagation();
 
-    let localesAreIdentical = false;
-    if (this.selectedVoice_) {
-      localesAreIdentical = this.selectedVoice_.lang.toLowerCase() ===
-          event.detail.selectedVoice.lang.toLowerCase();
-    }
-
-    this.selectedVoice_ = event.detail.selectedVoice;
-    chrome.readingMode.onVoiceChange(
-        this.selectedVoice_.name, this.selectedVoice_.lang);
+    const currentVoice = this.voicePackController_.getCurrentVoice();
+    this.voicePackController_.setUserPreferredVoice(event.detail.selectedVoice);
 
     // If the locales are identical, the voices are likely from the same
     // voice pack and use the same TTS engine, therefore, we don't need
     // to reset the word boundary state.
-    if (!localesAreIdentical) {
+    if (currentVoice?.lang.toLowerCase() !==
+        event.detail.selectedVoice.lang.toLowerCase()) {
       this.wordBoundaries_.resetToDefaultState(
           /*possibleWordBoundarySupportChange=*/ true);
     }
-
-    this.resetSpeechPostSettingChange_();
   }
 
   protected onVoiceLanguageToggle_(event: CustomEvent<{language: string}>) {
@@ -1701,7 +1578,7 @@
     if (!currentlyEnabled && !this.selectedVoice_) {
       // If there were no enabled languages (and thus no selected voice),
       // select a voice.
-      this.getSpeechSynthesisVoice();
+      this.voicePackController_.getCurrentVoiceOrDefault();
     }
   }
 
@@ -1730,7 +1607,7 @@
       // We need to restore enabled languages prior to selecting the preferred
       // voice to ensure we have the right voices available.
       this.restoreEnabledLanguagesFromPref();
-      this.selectPreferredVoice();
+      this.selectUserPreferredVoice();
     }
     this.settingsPrefs_ = {
       ...this.settingsPrefs_,
@@ -1748,8 +1625,7 @@
   }
 
   restoreEnabledLanguagesFromPref() {
-    this.voicePackController_.restoreEnabledLanguagesFromPref(
-        this.defaultVoice()?.lang);
+    this.voicePackController_.restoreEnabledLanguagesFromPref();
 
     for (const lang of this.voicePackController_.getEnabledLangs()) {
       this.installVoicePackIfPossible(
@@ -1758,20 +1634,7 @@
     }
   }
 
-  private currentVoiceIsUserChosen_(): boolean {
-    const storedVoiceName = chrome.readingMode.getStoredVoice();
-
-    // `this.selectedVoice` is not necessarily chosen by the user, it is just
-    // the voice that read aloud is using. It may be a default voice chosen by
-    // read aloud, so we check it against user preferences to see if it was
-    // user-chosen.
-    if (storedVoiceName) {
-      return this.selectedVoice_?.name === storedVoiceName;
-    }
-    return false;
-  }
-
-  selectPreferredVoice() {
+  selectUserPreferredVoice() {
     // TODO: crbug.com/40275871 - decide whether this is the behavior we want.
     // This shouldn't happen often, so just skip selecting a new voice for now.
     // Another option would be to update the voice and the call
@@ -1780,20 +1643,7 @@
       return;
     }
 
-    const storedVoiceName = chrome.readingMode.getStoredVoice();
-    if (!storedVoiceName) {
-      this.selectedVoice_ = this.defaultVoice();
-      return;
-    }
-
-    const selectedVoice =
-        this.getVoices_().filter(voice => voice.name === storedVoiceName);
-    this.selectedVoice_ = selectedVoice && (selectedVoice.length > 0) ?
-        selectedVoice[0] :
-        this.defaultVoice();
-
-    // Enable the locale for the preferred voice for this language.
-    this.voicePackController_.enableLang(this.selectedVoice_?.lang);
+    this.voicePackController_.setUserPreferredVoiceFromPrefs();
   }
 
   protected onLineSpacingChange_() {
@@ -1893,7 +1743,7 @@
     if (!availableLang ||
         (speechSynthesisBaseLang &&
          !availableLang.startsWith(speechSynthesisBaseLang))) {
-      this.selectPreferredVoice();
+      this.selectUserPreferredVoice();
       return;
     }
 
@@ -1916,7 +1766,7 @@
     // Enable the locales so we can select a voice for the given language and
     // show it in the voice menu.
     this.voicePackController_.enableLang(localeToEnable);
-    this.selectPreferredVoice();
+    this.selectUserPreferredVoice();
   }
 
   // Kicks off a workflow to install a voice pack.
@@ -1966,10 +1816,6 @@
       this.onPlayPauseClick_();
     }
   }
-
-  resetVoiceForTesting() {
-    this.selectedVoice_ = undefined;
-  }
 }
 
 declare global {
diff --git a/chrome/browser/resources/side_panel/read_anything/read_aloud/highlighter.ts b/chrome/browser/resources/side_panel/read_anything/read_aloud/highlighter.ts
index 3514ed8..d69ed38 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_aloud/highlighter.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_aloud/highlighter.ts
@@ -8,6 +8,7 @@
 import {NodeStore} from '../node_store.js';
 import {isEspeak} from '../voice_language_util.js';
 
+import {VoicePackController} from './voice_pack_controller.js';
 import {WordBoundaries} from './word_boundaries.js';
 
 // Characters that should be ignored for word highlighting when not accompanied
@@ -30,6 +31,7 @@
   private wordBoundaries_: WordBoundaries;
   private nodeStore_: NodeStore;
   private allowAutoScroll_ = true;
+  private voicePackController_ = VoicePackController.getInstance();
 
   constructor() {
     this.wordBoundaries_ = WordBoundaries.getInstance();
@@ -65,10 +67,8 @@
 
   highlightCurrentGranularity(
       axNodeIds: number[], scrollIntoView: boolean,
-      shouldUpdateSentenceHighlight: boolean,
-      selectedVoice?: SpeechSynthesisVoice): void {
-    const highlightGranularity =
-        this.getEffectiveHighlightingGranularity_(selectedVoice);
+      shouldUpdateSentenceHighlight: boolean): void {
+    const highlightGranularity = this.getEffectiveHighlightingGranularity_();
     switch (highlightGranularity) {
       case chrome.readingMode.noHighlighting:
       // Even without highlighting, we may still need to calculate the sentence
@@ -135,8 +135,9 @@
   isInvalidHighlightForWordHighlighting(textToHighlight?: string): boolean {
     // If a highlight is just white space or punctuation, we can skip
     // highlighting.
-    return !textToHighlight || textToHighlight === '' ||
-        IGNORED_HIGHLIGHT_CHARACTERS_REGEX.test(textToHighlight);
+    const text = textToHighlight?.trim();
+    return !text || text === '' ||
+        IGNORED_HIGHLIGHT_CHARACTERS_REGEX.test(text);
   }
 
   private getCurrentHighlightBounds_(): DOMRect {
@@ -177,8 +178,7 @@
     return ancestor;
   }
 
-  private getEffectiveHighlightingGranularity_(
-      selectedVoice?: SpeechSynthesisVoice): number {
+  private getEffectiveHighlightingGranularity_(): number {
     // Parse all of the conditions that control highlighting and return the
     // effective highlighting granularity.
     const highlight = chrome.readingMode.highlightGranularity;
@@ -188,7 +188,8 @@
       return highlight;
     }
 
-    if (this.wordBoundaries_.notSupported() || isEspeak(selectedVoice)) {
+    if (this.wordBoundaries_.notSupported() ||
+        isEspeak(this.voicePackController_.getCurrentVoice())) {
       // Fall back where word highlighting is not possible. Since espeak
       // boundaries are different than Google TTS word boundaries, fall back
       // to sentence boundaries in that case too.
@@ -231,7 +232,8 @@
     const wordBoundaryState = this.wordBoundaries_.state;
     const index = wordBoundaryState.speechUtteranceStartIndex +
         wordBoundaryState.previouslySpokenIndex;
-    const length = wordBoundaryState.speechUtteranceLength;
+    const speechUtteranceLength = wordBoundaryState.speechUtteranceLength;
+    let alreadyHighlightedSpeechUtteranceLength = 0;
 
     const highlightNodes =
         chrome.readingMode.getHighlightForCurrentSegmentIndex(
@@ -239,7 +241,11 @@
     let hasHighlights = false;
     for (const highlightNode of highlightNodes) {
       const nodeId = highlightNode.nodeId;
-      const highlightLength: number = length ? length : highlightNode.length;
+      const remainingSpeechUtteranceLength = Math.max(
+          speechUtteranceLength - alreadyHighlightedSpeechUtteranceLength, 0);
+      const highlightLength: number = speechUtteranceLength ?
+          (remainingSpeechUtteranceLength) :
+          highlightNode.length;
       const highlightStartIndex = highlightNode.start;
       const endIndex = highlightStartIndex + highlightLength;
       const node = this.nodeStore_.getDomNode(nodeId);
@@ -253,6 +259,10 @@
         continue;
       }
 
+      // Keep track of the highlight length that's been spoken so that
+      // speechUtteranceLength can be used across multiple nodes.
+      alreadyHighlightedSpeechUtteranceLength += highlightLength;
+
       hasHighlights = true;
       const element = node as HTMLElement;
       const highlighted =
diff --git a/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_controller.ts b/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_controller.ts
index a16a2e7..f84351b 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_controller.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_controller.ts
@@ -11,7 +11,7 @@
 import type {SpeechBrowserProxy} from '../speech_browser_proxy.js';
 import {SpeechBrowserProxyImpl} from '../speech_browser_proxy.js';
 import type {VoicePackStatus} from '../voice_language_util.js';
-import {areVoicesEqual, AVAILABLE_GOOGLE_TTS_LOCALES, convertLangOrLocaleForVoicePackManager, convertLangToAnAvailableLangIfPresent, createInitialListOfEnabledLanguages, EXTENSION_RESPONSE_TIMEOUT_MS, getFilteredVoiceList, getVoicePackConvertedLangIfExists, isVoicePackStatusError, isVoicePackStatusSuccess, VoiceClientSideStatusCode, VoicePackServerStatusSuccessCode} from '../voice_language_util.js';
+import {areVoicesEqual, AVAILABLE_GOOGLE_TTS_LOCALES, convertLangOrLocaleForVoicePackManager, convertLangToAnAvailableLangIfPresent, createInitialListOfEnabledLanguages, EXTENSION_RESPONSE_TIMEOUT_MS, getFilteredVoiceList, getNaturalVoiceOrDefault, getVoicePackConvertedLangIfExists, isNatural, isVoicePackStatusError, isVoicePackStatusSuccess, VoiceClientSideStatusCode, VoicePackServerStatusSuccessCode} from '../voice_language_util.js';
 import {VoiceNotificationManager} from '../voice_notification_manager.js';
 
 import {VoicePackModel} from './voice_pack_model.js';
@@ -19,6 +19,7 @@
 export interface VoiceLanguageListener {
   onEnabledLangsChange(): void;
   onAvailableVoicesChange(): void;
+  onCurrentVoiceChange(): void;
 }
 
 export class VoicePackController {
@@ -46,6 +47,17 @@
     this.model_.setCurrentLanguage(language);
   }
 
+  getCurrentVoice(): SpeechSynthesisVoice|null {
+    return this.model_.getCurrentVoice();
+  }
+
+  setCurrentVoice(voice: SpeechSynthesisVoice|null): void {
+    if (!areVoicesEqual(voice, this.getCurrentVoice())) {
+      this.model_.setCurrentVoice(voice);
+      this.listeners_.forEach(l => l.onCurrentVoiceChange());
+    }
+  }
+
   getEnabledLangs(): string[] {
     return [...this.model_.getEnabledLangs()];
   }
@@ -72,6 +84,66 @@
         availableVoice => areVoicesEqual(availableVoice, voice));
   }
 
+  setUserPreferredVoice(selectedVoice: SpeechSynthesisVoice): void {
+    this.setCurrentVoice(selectedVoice);
+    chrome.readingMode.onVoiceChange(selectedVoice.name, selectedVoice.lang);
+  }
+
+  setUserPreferredVoiceFromPrefs(): void {
+    const storedVoiceName = chrome.readingMode.getStoredVoice();
+    if (!storedVoiceName) {
+      this.setCurrentVoice(this.getDefaultVoice_());
+      return;
+    }
+
+    this.refreshAvailableVoices();
+    const selectedVoice = this.getAvailableVoices().filter(
+        voice => voice.name === storedVoiceName);
+    const newVoice = (selectedVoice.length && selectedVoice[0]) ?
+        selectedVoice[0] :
+        this.getDefaultVoice_();
+    this.setCurrentVoice(newVoice);
+
+    // Enable the locale for the preferred voice for this language.
+    this.enableLang(this.getCurrentVoice()?.lang);
+  }
+
+  updateAutoSelectedVoiceToNaturalVoice(): void {
+    if (this.currentVoiceIsUserChosen_()) {
+      return;
+    }
+
+    const naturalVoicesForLang = this.getAvailableVoices().filter(
+        voice => isNatural(voice) &&
+            voice.lang.startsWith(this.getCurrentLanguage()));
+    if (!naturalVoicesForLang.length || !naturalVoicesForLang[0]) {
+      return;
+    }
+
+    this.setCurrentVoice(naturalVoicesForLang[0]);
+  }
+
+  // Checks the voice pack status of the current voice and updates to the
+  // default voice if it's no longer available.
+  updateUnavailableVoiceToDefaultVoice(): void {
+    for (const lang of this.model_.getServerLanguages()) {
+      this.requestInfo_(lang);
+    }
+    const currentVoice = this.getCurrentVoice();
+    if (currentVoice && !this.isVoiceAvailable(currentVoice)) {
+      this.setCurrentVoice(this.getDefaultVoice_());
+    }
+  }
+
+  getCurrentVoiceOrDefault(): SpeechSynthesisVoice|null {
+    const currentVoice = this.getCurrentVoice();
+    if (!currentVoice) {
+      this.setCurrentVoice(this.getDefaultVoice_());
+    }
+
+    return this.getCurrentVoice();
+  }
+
   onLanguageUnavailableError(): void {
     const possibleNewLanguage = convertLangToAnAvailableLangIfPresent(
         this.getCurrentLanguage(), this.getAvailableLangs(),
@@ -81,6 +153,53 @@
     }
   }
 
+  // Attempt to get a new voice using the current language. In theory, the
+  // previously unavailable voice should no longer be showing up in
+  // availableVoices, but we ensure that the alternative voice does not match
+  // the previously unavailable voice as an extra measure. This method should
+  // only be called when speech synthesis returns an error.
+  onVoiceUnavailableError(): void {
+    const currentVoice = this.getCurrentVoice();
+    const newVoice = this.getDefaultVoice_();
+
+    // If the default voice is not the same as the original, unavailable voice,
+    // use that, only if the new voice is also defined.
+    if (newVoice && !areVoicesEqual(newVoice, currentVoice)) {
+      this.setCurrentVoice(newVoice);
+      return;
+    }
+
+    // If the default voice won't work, try another voice in that language.
+    const baseLang = this.getCurrentLanguage();
+    this.refreshAvailableVoices();
+    const voicesForLanguage = this.getAvailableVoices().filter(
+        voice => voice.lang.startsWith(baseLang));
+
+    // TODO: crbug.com/40927698 - It's possible we can get stuck in an infinite
+    // loop of jumping back and forth between two or more invalid voices, if
+    // multiple voices are invalid. Investigate if we need to do more to handle
+    // this case.
+
+    // TODO: crbug.com/336596926 - If there still aren't voices for the
+    // language, attempt to fallback to the browser language, if we're using
+    // the page language.
+    let voiceIndex = 0;
+    while (voiceIndex < voicesForLanguage.length) {
+      if (!areVoicesEqual(voicesForLanguage[voiceIndex], currentVoice)) {
+        // Return another voice in the same language, ensuring we're not
+        // returning the previously unavailable voice for extra safety.
+        this.setCurrentVoice(voicesForLanguage[voiceIndex] || null);
+        return;
+      }
+      voiceIndex++;
+    }
+
+    // TODO: crbug.com/336596926 - Handle language updates if there aren't any
+    // available voices in the current language other than the unavailable
+    // voice.
+    this.setCurrentVoice(null);
+  }
+
   disableLangIfNoVoices(lang: string): void {
     const lowerLang = lang.toLowerCase();
     this.refreshAvailableVoices();
@@ -151,12 +270,13 @@
   }
   // </if>
 
-  restoreEnabledLanguagesFromPref(langOfDefaultVoice?: string): void {
+  restoreEnabledLanguagesFromPref(): void {
     // We need to make sure the languages we choose correspond to voices, so
     // refresh the list of voices and available langs
     this.refreshAvailableVoices();
     this.setCurrentLanguage(chrome.readingMode.baseLanguageForSpeech);
     const storedLanguagesPref = chrome.readingMode.getLanguagesEnabledInPref();
+    const langOfDefaultVoice = this.getDefaultVoice_()?.lang;
     const langs = createInitialListOfEnabledLanguages(
         chrome.readingMode.baseLanguageForSpeech, storedLanguagesPref,
         this.getAvailableLangs(), langOfDefaultVoice);
@@ -220,12 +340,6 @@
     }
   }
 
-  refreshVoicePackStatuses() {
-    for (const lang of this.model_.getServerLanguages()) {
-      this.requestInfo_(lang);
-    }
-  }
-
   triggerInstall(voicePackLanguage: string) {
     // Install the voice if it's not currently installed and it's marked
     // as a language that should be installed
@@ -365,6 +479,55 @@
         v => getVoicePackConvertedLangIfExists(v.lang) === lang);
   }
 
+  private currentVoiceIsUserChosen_(): boolean {
+    const storedVoiceName = chrome.readingMode.getStoredVoice();
+
+    // getCurrentVoice() is not necessarily chosen by the user, it is just
+    // the voice that read aloud is using. It may be a default voice chosen by
+    // read aloud, so we check it against user preferences to see if it was
+    // user-chosen.
+    if (storedVoiceName) {
+      return this.getCurrentVoice()?.name === storedVoiceName;
+    }
+    return false;
+  }
+
+  private getDefaultVoice_(): SpeechSynthesisVoice|null {
+    this.refreshAvailableVoices();
+    const allPossibleVoices = this.getAvailableVoices();
+    const voicesForLanguage = allPossibleVoices.filter(
+        voice => voice.lang.startsWith(this.getCurrentLanguage()));
+
+    if (!voicesForLanguage.length) {
+      // Stay with the current voice if no voices are available for this
+      // language.
+      return this.getCurrentVoice() ?
+          this.getCurrentVoice() :
+          getNaturalVoiceOrDefault(allPossibleVoices);
+    }
+
+    // First try to choose a voice only from currently enabled locales for this
+    // language.
+    const voicesForCurrentEnabledLocale =
+        voicesForLanguage.filter(v => this.isLangEnabled(v.lang));
+    if (!voicesForCurrentEnabledLocale.length) {
+      // If there's no enabled locales for this language, check for any other
+      // voices for enabled locales.
+      const allVoicesForEnabledLocales =
+          allPossibleVoices.filter(v => this.isLangEnabled(v.lang));
+      if (!allVoicesForEnabledLocales.length) {
+        // If there are no voices for the enabled locales, or no enabled
+        // locales at all, we can't select a voice. So return null so we
+        // can disable the play button.
+        return null;
+      } else {
+        return getNaturalVoiceOrDefault(allVoicesForEnabledLocales);
+      }
+    }
+
+    return getNaturalVoiceOrDefault(voicesForCurrentEnabledLocale);
+  }
+
   static getInstance(): VoicePackController {
     return instance || (instance = new VoicePackController());
   }
diff --git a/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_model.ts b/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_model.ts
index c2f6656..6dca8c69 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_model.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_aloud/voice_pack_model.ts
@@ -41,6 +41,7 @@
   // need to download Natural voices for automatically
   private languagesForVoiceDownloads_: Set<string> = new Set();
 
+  private currentVoice_: SpeechSynthesisVoice|null = null;
   private currentLanguage_: string = '';
 
   addLanguageForDownload(lang: string): void {
@@ -119,6 +120,14 @@
     return Array.from(this.voicePackInstallStatusServerResponses_.keys());
   }
 
+  getCurrentVoice(): SpeechSynthesisVoice|null {
+    return this.currentVoice_ || null;
+  }
+
+  setCurrentVoice(voice: SpeechSynthesisVoice|null): void {
+    this.currentVoice_ = voice;
+  }
+
   getCurrentLanguage(): string {
     return this.currentLanguage_;
   }
diff --git a/chrome/browser/resources/side_panel/read_anything/read_anything_logger.ts b/chrome/browser/resources/side_panel/read_anything/read_anything_logger.ts
index ea433357..9b13e16 100644
--- a/chrome/browser/resources/side_panel/read_anything/read_anything_logger.ts
+++ b/chrome/browser/resources/side_panel/read_anything/read_anything_logger.ts
@@ -85,7 +85,7 @@
     this.metrics.recordHighlightGranularity(highlight);
   }
 
-  private logVoiceTypeUsedForReading_(voice: SpeechSynthesisVoice|undefined) {
+  private logVoiceTypeUsedForReading_(voice: SpeechSynthesisVoice|null) {
     if (!voice) {
       return;
     }
@@ -136,8 +136,7 @@
     this.metrics.recordVoiceSpeed(index);
   }
 
-  logSpeechPlaySession(
-      startTime: number, voice: SpeechSynthesisVoice|undefined) {
+  logSpeechPlaySession(startTime: number, voice: SpeechSynthesisVoice|null) {
     this.logVoiceTypeUsedForReading_(voice);
     this.logLanguageUsedForReading_(voice?.lang);
     this.metrics.recordSpeechPlaybackLength(Date.now() - startTime);
diff --git a/chrome/browser/resources/side_panel/read_anything/voice_language_util.ts b/chrome/browser/resources/side_panel/read_anything/voice_language_util.ts
index c5cc22d..216f55e 100644
--- a/chrome/browser/resources/side_panel/read_anything/voice_language_util.ts
+++ b/chrome/browser/resources/side_panel/read_anything/voice_language_util.ts
@@ -156,7 +156,7 @@
   return voice.name.includes(NATURAL_STRING_IDENTIFIER);
 }
 
-export function isEspeak(voice: SpeechSynthesisVoice|undefined) {
+export function isEspeak(voice?: SpeechSynthesisVoice|null) {
   return voice && voice.name.includes(ESPEAK_STRING_IDENTIFIER);
 }
 
@@ -165,9 +165,9 @@
 }
 
 export function getNaturalVoiceOrDefault(voices: SpeechSynthesisVoice[]):
-    SpeechSynthesisVoice|undefined {
+    SpeechSynthesisVoice|null {
   if (voices.length === 0) {
-    return undefined;
+    return null;
   }
 
   const naturalVoice = voices.find(v => isNatural(v));
@@ -177,7 +177,7 @@
 
   const defaultVoice =
       voices.find(({default: isDefaultVoice}) => isDefaultVoice);
-  return defaultVoice ? defaultVoice : voices[0];
+  return defaultVoice ? defaultVoice : (voices[0] || null);
 }
 
 export function getNotification(
diff --git a/chrome/browser/selection/android/java/src/org/chromium/chrome/browser/selection/SelectionPopupBackPressHandler.java b/chrome/browser/selection/android/java/src/org/chromium/chrome/browser/selection/SelectionPopupBackPressHandler.java
index 8ba1e12..a0cc0868 100644
--- a/chrome/browser/selection/android/java/src/org/chromium/chrome/browser/selection/SelectionPopupBackPressHandler.java
+++ b/chrome/browser/selection/android/java/src/org/chromium/chrome/browser/selection/SelectionPopupBackPressHandler.java
@@ -52,6 +52,14 @@
     }
 
     @Override
+    public boolean invokeBackActionOnEscape() {
+        // For Escape key presses, we do not want to clear selection, which matches with Desktop. We
+        // do not also implement a custom {@link BackPressHandler#handleEscPress()} since we don't
+        // want anything to happen and for the manager to move to the next priority handler.
+        return false;
+    }
+
+    @Override
     public ObservableSupplier<Boolean> getHandleBackPressChangedSupplier() {
         return mBackPressChangedSupplier;
     }
diff --git a/chrome/browser/sharesheet/sharesheet_test_util.h b/chrome/browser/sharesheet/sharesheet_test_util.h
index aa6c8468..62d018c80a 100644
--- a/chrome/browser/sharesheet/sharesheet_test_util.h
+++ b/chrome/browser/sharesheet/sharesheet_test_util.h
@@ -13,13 +13,13 @@
 
 namespace sharesheet {
 
-const char kTestText[] = "text";
-const char kTestTitle[] = "title";
-const char kTestUrl[] = "https://fake-url.com/fake";
-const char kTestTextFile[] = "path/to/text.txt";
-const char kTestPdfFile[] = "path/to/file.pdf";
-const char kMimeTypeText[] = "text/plain";
-const char kMimeTypePdf[] = "application/pdf";
+inline constexpr char kTestText[] = "text";
+inline constexpr char kTestTitle[] = "title";
+inline constexpr char kTestUrl[] = "https://fake-url.com/fake";
+inline constexpr char kTestTextFile[] = "path/to/text.txt";
+inline constexpr char kTestPdfFile[] = "path/to/file.pdf";
+inline constexpr char kMimeTypeText[] = "text/plain";
+inline constexpr char kMimeTypePdf[] = "application/pdf";
 
 apps::IntentPtr CreateValidTextIntent();
 
diff --git a/chrome/browser/signin/e2e_tests/signin_util.h b/chrome/browser/signin/e2e_tests/signin_util.h
index dae19ae3..49013e7a 100644
--- a/chrome/browser/signin/e2e_tests/signin_util.h
+++ b/chrome/browser/signin/e2e_tests/signin_util.h
@@ -23,7 +23,7 @@
 
 // A wrapper importing the settings module when the chrome://settings serve the
 // Polymer 3 version.
-const char kSettingsScriptWrapperFormat[] =
+inline constexpr char kSettingsScriptWrapperFormat[] =
     "import('./settings.js').then(settings => {%s});";
 
 signin::IdentityManager* identity_manager(Browser* browser);
diff --git a/chrome/browser/site_isolation/about_flags.h b/chrome/browser/site_isolation/about_flags.h
index 862fc54..6c0b8bd 100644
--- a/chrome/browser/site_isolation/about_flags.h
+++ b/chrome/browser/site_isolation/about_flags.h
@@ -11,7 +11,7 @@
 
 namespace about_flags {
 
-constexpr char kSiteIsolationTrialOptOutInternalName[] =
+inline constexpr char kSiteIsolationTrialOptOutInternalName[] =
     "site-isolation-trial-opt-out";
 
 inline std::string SiteIsolationTrialOptOutChoiceEnabled() {
diff --git a/chrome/browser/speech/on_device_speech_recognition_impl.cc b/chrome/browser/speech/on_device_speech_recognition_impl.cc
index 8096216b..e7a6511 100644
--- a/chrome/browser/speech/on_device_speech_recognition_impl.cc
+++ b/chrome/browser/speech/on_device_speech_recognition_impl.cc
@@ -4,7 +4,10 @@
 
 #include "chrome/browser/speech/on_device_speech_recognition_impl.h"
 
+#include "base/rand_util.h"
 #include "base/strings/string_util.h"
+#include "base/task/task_runner.h"
+#include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -42,6 +45,17 @@
       language_code);
 }
 
+bool IsLanguageInstalled(const std::string& language_code) {
+  for (const auto& language : g_browser_process->local_state()->GetList(
+           prefs::kSodaRegisteredLanguagePacks)) {
+    if (language.GetString() == language_code) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 }  // namespace
 #endif  // !BUILDFLAG(IS_ANDROID)
 
@@ -76,15 +90,7 @@
     return;
   }
 
-  media::mojom::AvailabilityStatus availability_status =
-      IsOnDeviceSpeechRecognitionAvailable(language);
-  if (availability_status == media::mojom::AvailabilityStatus::kAvailable &&
-      !HasOnDeviceLanguageDownloaded(language)) {
-    std::move(callback).Run(media::mojom::AvailabilityStatus::kDownloadable);
-    return;
-  }
-
-  std::move(callback).Run(availability_status);
+  std::move(callback).Run(GetMaskedAvailabilityStatus(language));
 #endif  // BUILDFLAG(IS_ANDROID)
 }
 
@@ -120,18 +126,13 @@
     return;
   }
 
-  language_installation_callbacks_[language].push_back(std::move(callback));
-  // `InstallSoda` will only install the SODA binary if it is not already
-  // installed.
-  speech::SodaInstaller::GetInstance()->InstallSoda(
-      g_browser_process->local_state());
-
-  // `InstallLanguage` will only install languages that are not already
-  // installed.
-  speech::SodaInstaller::GetInstance()->InstallLanguage(
-      language_config.value().language_name, g_browser_process->local_state());
-
-  SetOnDeviceLanguageDownloaded(language);
+  base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&OnDeviceSpeechRecognitionImpl::InstallLanguageInternal,
+                     weak_ptr_factory_.GetWeakPtr(),
+                     language_config.value().language_name,
+                     std::move(callback)),
+      GetDownloadDelay(language_config.value().language_name));
 }
 
 void OnDeviceSpeechRecognitionImpl::OnSodaInstalled(
@@ -196,6 +197,25 @@
 }
 
 #if !BUILDFLAG(IS_ANDROID)
+void OnDeviceSpeechRecognitionImpl::InstallLanguageInternal(
+    const std::string& language,
+    OnDeviceSpeechRecognitionImpl::InstallOnDeviceSpeechRecognitionCallback
+        callback) {
+  language_installation_callbacks_[language].push_back(std::move(callback));
+
+  // `InstallSoda` will only install the SODA binary if it is not already
+  // installed.
+  speech::SodaInstaller::GetInstance()->InstallSoda(
+      g_browser_process->local_state());
+
+  // `InstallLanguage` will only install languages that are not already
+  // installed.
+  speech::SodaInstaller::GetInstance()->InstallLanguage(
+      language, g_browser_process->local_state());
+
+  SetOnDeviceLanguageDownloaded(language);
+}
+
 void OnDeviceSpeechRecognitionImpl::RunAndRemoveInstallationCallbacks(
     const std::string& language,
     bool installation_success) {
@@ -235,6 +255,19 @@
           std::move(on_device_languages_downloaded));
 }
 
+media::mojom::AvailabilityStatus
+OnDeviceSpeechRecognitionImpl::GetMaskedAvailabilityStatus(
+    const std::string& language) {
+  media::mojom::AvailabilityStatus availability_status =
+      IsOnDeviceSpeechRecognitionAvailable(language);
+  if (availability_status == media::mojom::AvailabilityStatus::kAvailable &&
+      !HasOnDeviceLanguageDownloaded(language)) {
+    return media::mojom::AvailabilityStatus::kDownloadable;
+  }
+
+  return availability_status;
+}
+
 bool OnDeviceSpeechRecognitionImpl::HasOnDeviceLanguageDownloaded(
     const std::string& language) {
   base::Value on_device_languages_downloaded_value =
@@ -269,6 +302,20 @@
   SetOnDeviceLanguagesDownloadedContentSetting(
       std::move(on_device_languages_downloaded_value));
 }
+
+base::TimeDelta OnDeviceSpeechRecognitionImpl::GetDownloadDelay(
+    const std::string& language) {
+  // Check if SODA is already installed for the given language. If it is and the
+  // origin isn't supposed to know that, then add a delay to simulate a real
+  // download before proceeding.
+  if (GetMaskedAvailabilityStatus(language) ==
+          media::mojom::AvailabilityStatus::kDownloadable &&
+      IsLanguageInstalled(language)) {
+    return base::RandTimeDelta(base::Seconds(2), base::Seconds(3));
+  }
+
+  return base::TimeDelta();
+}
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 DOCUMENT_USER_DATA_KEY_IMPL(OnDeviceSpeechRecognitionImpl);
diff --git a/chrome/browser/speech/on_device_speech_recognition_impl.h b/chrome/browser/speech/on_device_speech_recognition_impl.h
index 6ae5255c..1873067 100644
--- a/chrome/browser/speech/on_device_speech_recognition_impl.h
+++ b/chrome/browser/speech/on_device_speech_recognition_impl.h
@@ -7,6 +7,7 @@
 
 #include <string>
 
+#include "base/memory/weak_ptr.h"
 #include "content/public/browser/document_user_data.h"
 #include "media/mojo/mojom/speech_recognizer.mojom.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -79,6 +80,10 @@
   bool CanRenderFrameHostUseOnDeviceSpeechRecognition();
 
 #if !BUILDFLAG(IS_ANDROID)
+  void InstallLanguageInternal(
+      const std::string& language,
+      OnDeviceSpeechRecognitionImpl::InstallOnDeviceSpeechRecognitionCallback
+          callback);
   void RunAndRemoveInstallationCallbacks(const std::string& language,
                                          bool installation_success);
   base::Value GetOnDeviceLanguagesDownloadedValue();
@@ -87,6 +92,16 @@
   bool HasOnDeviceLanguageDownloaded(const std::string& language);
   void SetOnDeviceLanguageDownloaded(const std::string&);
 
+  // Mask on-device speech recognition availability by requiring a call to
+  // installOnDevice() for a language before the language is available to the
+  // origin.
+  media::mojom::AvailabilityStatus GetMaskedAvailabilityStatus(
+      const std::string& language);
+
+  // Returns a delay when installing on-device speech recognition language packs
+  // to safeguard against fingerprinting resulting from timing the installation.
+  base::TimeDelta GetDownloadDelay(const std::string& language);
+
   base::flat_map<std::string,
                  std::list<InstallOnDeviceSpeechRecognitionCallback>>
       language_installation_callbacks_;
@@ -97,6 +112,7 @@
 
   mojo::Receiver<media::mojom::OnDeviceSpeechRecognition> receiver_{this};
 
+  base::WeakPtrFactory<OnDeviceSpeechRecognitionImpl> weak_ptr_factory_{this};
   DOCUMENT_USER_DATA_KEY_DECL();
 };
 
diff --git a/chrome/browser/speech/on_device_speech_recognition_impl_browsertest.cc b/chrome/browser/speech/on_device_speech_recognition_impl_browsertest.cc
index 3999fe9..68f7810 100644
--- a/chrome/browser/speech/on_device_speech_recognition_impl_browsertest.cc
+++ b/chrome/browser/speech/on_device_speech_recognition_impl_browsertest.cc
@@ -8,6 +8,7 @@
 
 #include "base/functional/bind.h"
 #include "base/run_loop.h"
+#include "base/test/run_until.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h"
 #include "chrome/browser/profiles/profile.h"
@@ -30,6 +31,7 @@
 namespace {
 constexpr char kValidLanguageCode[] = "en-US";
 constexpr char kInvalidLanguageCode[] = "xx-XX";
+
 }  // namespace
 
 namespace speech {
@@ -49,26 +51,24 @@
   void SetUpOnMainThread() override;
 
   void OnDeviceWebSpeechAvailableCallback(
+      media::mojom::AvailabilityStatus actual_status);
+  void OnDeviceWebSpeechAvailableCallbackAndAssertStatus(
       media::mojom::AvailabilityStatus expected_status,
       media::mojom::AvailabilityStatus actual_status);
+  void InstallOnDeviceSpeechRecognition();
   void InstallOnDeviceSpeechRecognitionCallback(bool expected_success,
                                                 bool actual_success);
-  void WaitForCallback();
+  void WaitUntilAvailable();
   void NavigateToUrl(const std::string& url_string);
   void ClearSiteContentSettings();
+  OnDeviceSpeechRecognitionImpl* on_device_speech_recognition();
 
  protected:
-  std::unique_ptr<base::RunLoop> run_loop_;
-  mojo::Remote<media::mojom::OnDeviceSpeechRecognition>
-      on_device_speech_recognition_;
   base::test::ScopedFeatureList scoped_feature_list_;
+  media::mojom::AvailabilityStatus availability_status_;
 };
 
 void OnDeviceSpeechRecognitionImplBrowserTest::SetUpOnMainThread() {
-  OnDeviceSpeechRecognitionImpl::GetOrCreateForCurrentDocument(
-      chrome_test_utils::GetActiveWebContents(this)->GetPrimaryMainFrame())
-      ->Bind(on_device_speech_recognition_.BindNewPipeAndPassReceiver());
-
   host_resolver()->AddRule("*", "127.0.0.1");
   embedded_https_test_server().ServeFilesFromSourceDirectory(
       GetChromeTestDataDir());
@@ -79,25 +79,49 @@
 
 void OnDeviceSpeechRecognitionImplBrowserTest::
     OnDeviceWebSpeechAvailableCallback(
+        media::mojom::AvailabilityStatus actual_status) {
+  OnDeviceWebSpeechAvailableCallbackAndAssertStatus(actual_status,
+                                                    actual_status);
+}
+
+void OnDeviceSpeechRecognitionImplBrowserTest::
+    OnDeviceWebSpeechAvailableCallbackAndAssertStatus(
         media::mojom::AvailabilityStatus expected_status,
         media::mojom::AvailabilityStatus actual_status) {
   ASSERT_EQ(expected_status, actual_status);
-  run_loop_->Quit();
+  availability_status_ = actual_status;
+}
+
+void OnDeviceSpeechRecognitionImplBrowserTest::
+    InstallOnDeviceSpeechRecognition() {
+  // Install on-device speech recognition and simulate the installation of the
+  // SODA library and language pack.
+  on_device_speech_recognition()->InstallOnDeviceSpeechRecognition(
+      kValidLanguageCode,
+      base::BindOnce(&OnDeviceSpeechRecognitionImplBrowserTest::
+                         InstallOnDeviceSpeechRecognitionCallback,
+                     base::Unretained(this), true));
+
+  speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting();
+  speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(
+      speech::LanguageCode::kEnUs);
 }
 
 void OnDeviceSpeechRecognitionImplBrowserTest::
     InstallOnDeviceSpeechRecognitionCallback(bool expected_success,
                                              bool actual_success) {
   ASSERT_EQ(expected_success, actual_success);
-  run_loop_->Quit();
 }
 
-void OnDeviceSpeechRecognitionImplBrowserTest::WaitForCallback() {
-  ASSERT_FALSE(run_loop_);
-  run_loop_ = std::make_unique<base::RunLoop>(
-      base::RunLoop::Type::kNestableTasksAllowed);
-  run_loop_->Run();
-  run_loop_ = nullptr;
+void OnDeviceSpeechRecognitionImplBrowserTest::WaitUntilAvailable() {
+  ASSERT_TRUE(base::test::RunUntil([&]() {
+    on_device_speech_recognition()->OnDeviceWebSpeechAvailable(
+        kValidLanguageCode,
+        base::BindOnce(&OnDeviceSpeechRecognitionImplBrowserTest::
+                           OnDeviceWebSpeechAvailableCallback,
+                       base::Unretained(this)));
+    return availability_status_ == media::mojom::AvailabilityStatus::kAvailable;
+  }));
 }
 
 void OnDeviceSpeechRecognitionImplBrowserTest::NavigateToUrl(
@@ -105,10 +129,6 @@
   const GURL kUrl(
       embedded_https_test_server().GetURL(url_string, "/empty.html"));
   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kUrl));
-  on_device_speech_recognition_.reset();
-  OnDeviceSpeechRecognitionImpl::GetOrCreateForCurrentDocument(
-      chrome_test_utils::GetActiveWebContents(this)->GetPrimaryMainFrame())
-      ->Bind(on_device_speech_recognition_.BindNewPipeAndPassReceiver());
 }
 
 void OnDeviceSpeechRecognitionImplBrowserTest::ClearSiteContentSettings() {
@@ -122,23 +142,26 @@
   observer.BlockUntilCompletion();
 }
 
+OnDeviceSpeechRecognitionImpl*
+OnDeviceSpeechRecognitionImplBrowserTest::on_device_speech_recognition() {
+  return OnDeviceSpeechRecognitionImpl::GetOrCreateForCurrentDocument(
+      chrome_test_utils::GetActiveWebContents(this)->GetPrimaryMainFrame());
+}
+
 IN_PROC_BROWSER_TEST_F(OnDeviceSpeechRecognitionImplBrowserTest,
                        OnDeviceWebSpeechAvailable) {
-  on_device_speech_recognition_->OnDeviceWebSpeechAvailable(
+  on_device_speech_recognition()->OnDeviceWebSpeechAvailable(
       kInvalidLanguageCode,
       base::BindOnce(&OnDeviceSpeechRecognitionImplBrowserTest::
-                         OnDeviceWebSpeechAvailableCallback,
+                         OnDeviceWebSpeechAvailableCallbackAndAssertStatus,
                      base::Unretained(this),
                      media::mojom::AvailabilityStatus::kUnavailable));
-  WaitForCallback();
-
-  on_device_speech_recognition_->OnDeviceWebSpeechAvailable(
+  on_device_speech_recognition()->OnDeviceWebSpeechAvailable(
       kValidLanguageCode,
       base::BindOnce(&OnDeviceSpeechRecognitionImplBrowserTest::
-                         OnDeviceWebSpeechAvailableCallback,
+                         OnDeviceWebSpeechAvailableCallbackAndAssertStatus,
                      base::Unretained(this),
                      media::mojom::AvailabilityStatus::kDownloadable));
-  WaitForCallback();
 }
 
 IN_PROC_BROWSER_TEST_F(OnDeviceSpeechRecognitionImplBrowserTest,
@@ -147,93 +170,51 @@
 
   // Verify that on-device speech recognition is downloadable before it is
   // installed.
-  on_device_speech_recognition_->OnDeviceWebSpeechAvailable(
+  on_device_speech_recognition()->OnDeviceWebSpeechAvailable(
       kValidLanguageCode,
       base::BindOnce(&OnDeviceSpeechRecognitionImplBrowserTest::
-                         OnDeviceWebSpeechAvailableCallback,
+                         OnDeviceWebSpeechAvailableCallbackAndAssertStatus,
                      base::Unretained(this),
                      media::mojom::AvailabilityStatus::kDownloadable));
-  WaitForCallback();
-
-  // Install on-device speech recognition and simulate the installation of the
-  // SODA library and language pack.
-  on_device_speech_recognition_->InstallOnDeviceSpeechRecognition(
-      kValidLanguageCode,
-      base::BindOnce(&OnDeviceSpeechRecognitionImplBrowserTest::
-                         InstallOnDeviceSpeechRecognitionCallback,
-                     base::Unretained(this), true));
-
-  // Run the loop until idle, otherwise SODA might get install before the
-  // `InstallOnDeviceSpeechRecognition` message is flushed through the pipe.
-  // This won't happen outside of tests because
-  // `InstallOnDeviceSpeechRecognition` triggers the installation of SODA.
-  base::RunLoop().RunUntilIdle();
-
-  speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting();
-  speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(
-      speech::LanguageCode::kEnUs);
-  WaitForCallback();
+  InstallOnDeviceSpeechRecognition();
 
   // Verify that on-device speech recognition is available after it is
   // installed.
-  on_device_speech_recognition_->OnDeviceWebSpeechAvailable(
-      kValidLanguageCode,
-      base::BindOnce(&OnDeviceSpeechRecognitionImplBrowserTest::
-                         OnDeviceWebSpeechAvailableCallback,
-                     base::Unretained(this),
-                     media::mojom::AvailabilityStatus::kAvailable));
-  WaitForCallback();
+  WaitUntilAvailable();
 
   // On-device speech recognition availability is masked by origin, so the
   // previously installed language pack should not be available to a different
   // origin even if it's already installed.
   NavigateToUrl("bar.com");
-  on_device_speech_recognition_->OnDeviceWebSpeechAvailable(
+  on_device_speech_recognition()->OnDeviceWebSpeechAvailable(
       kValidLanguageCode,
       base::BindOnce(&OnDeviceSpeechRecognitionImplBrowserTest::
-                         OnDeviceWebSpeechAvailableCallback,
+                         OnDeviceWebSpeechAvailableCallbackAndAssertStatus,
                      base::Unretained(this),
                      media::mojom::AvailabilityStatus::kDownloadable));
-  WaitForCallback();
 
   // Verify that on-device speech recognition can be installed on the second
   // origin.
-  on_device_speech_recognition_->InstallOnDeviceSpeechRecognition(
-      kValidLanguageCode,
-      base::BindOnce(&OnDeviceSpeechRecognitionImplBrowserTest::
-                         InstallOnDeviceSpeechRecognitionCallback,
-                     base::Unretained(this), true));
-  base::RunLoop().RunUntilIdle();
-  speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting();
-  speech::SodaInstaller::GetInstance()->NotifySodaInstalledForTesting(
-      speech::LanguageCode::kEnUs);
-  WaitForCallback();
-  on_device_speech_recognition_->OnDeviceWebSpeechAvailable(
-      kValidLanguageCode,
-      base::BindOnce(&OnDeviceSpeechRecognitionImplBrowserTest::
-                         OnDeviceWebSpeechAvailableCallback,
-                     base::Unretained(this),
-                     media::mojom::AvailabilityStatus::kAvailable));
-  WaitForCallback();
+  InstallOnDeviceSpeechRecognition();
+
+  WaitUntilAvailable();
 
   // Verify that clearing site content settings resets the on-device speech
   // recognition mask for both origins.
   ClearSiteContentSettings();
-  on_device_speech_recognition_->OnDeviceWebSpeechAvailable(
+  on_device_speech_recognition()->OnDeviceWebSpeechAvailable(
       kValidLanguageCode,
       base::BindOnce(&OnDeviceSpeechRecognitionImplBrowserTest::
-                         OnDeviceWebSpeechAvailableCallback,
+                         OnDeviceWebSpeechAvailableCallbackAndAssertStatus,
                      base::Unretained(this),
                      media::mojom::AvailabilityStatus::kDownloadable));
-  WaitForCallback();
   NavigateToUrl("foo.com");
-  on_device_speech_recognition_->OnDeviceWebSpeechAvailable(
+  on_device_speech_recognition()->OnDeviceWebSpeechAvailable(
       kValidLanguageCode,
       base::BindOnce(&OnDeviceSpeechRecognitionImplBrowserTest::
-                         OnDeviceWebSpeechAvailableCallback,
+                         OnDeviceWebSpeechAvailableCallbackAndAssertStatus,
                      base::Unretained(this),
                      media::mojom::AvailabilityStatus::kDownloadable));
-  WaitForCallback();
 }
 
 }  // namespace speech
diff --git a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
index 156a6953..a6563ff2 100644
--- a/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
+++ b/chrome/browser/storage_access_api/storage_access_grant_permission_context_unittest.cc
@@ -797,10 +797,10 @@
         /*entries=*/
         {{net::SchemefulSite(GetTopLevelURL()),
           {net::FirstPartySetEntry(net::SchemefulSite(GetTopLevelURL()),
-                                   net::SiteType::kPrimary, std::nullopt)}},
+                                   net::SiteType::kPrimary)}},
          {net::SchemefulSite(GetRequesterURL()),
           {net::FirstPartySetEntry(net::SchemefulSite(GetTopLevelURL()),
-                                   net::SiteType::kAssociated, 0)}}},
+                                   net::SiteType::kAssociated)}}},
         /*aliases=*/{}));
   }
 
diff --git a/chrome/browser/supervised_user/supervised_user_extensions_manager.h b/chrome/browser/supervised_user/supervised_user_extensions_manager.h
index 8a2e49f..fd872c2 100644
--- a/chrome/browser/supervised_user/supervised_user_extensions_manager.h
+++ b/chrome/browser/supervised_user/supervised_user_extensions_manager.h
@@ -25,9 +25,10 @@
 class ExtensionRegistry;
 
 // UMA metrics for auto-approved extensions.
-constexpr char kInitialLocallyApprovedExtensionCountWinLinuxMacHistogramName[] =
-    "SupervisedUsers.InitialLocallyApprovedExtensionsCountOnWinLinuxMac";
-constexpr char kExtensionApprovalsCountOnExtensionToggleHistogramName[] =
+inline constexpr char
+    kInitialLocallyApprovedExtensionCountWinLinuxMacHistogramName[] =
+        "SupervisedUsers.InitialLocallyApprovedExtensionsCountOnWinLinuxMac";
+inline constexpr char kExtensionApprovalsCountOnExtensionToggleHistogramName[] =
     "SupervisedUsers.ExtensionApprovalsCountOnExtensionToggle";
 
 // This class groups all the functionality to handle extensions
diff --git a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
index ff5e7c9..f993662 100644
--- a/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_nigori_sync_test.cc
@@ -60,6 +60,7 @@
 #include "components/trusted_vault/command_line_switches.h"
 #include "components/trusted_vault/securebox.h"
 #include "components/trusted_vault/standalone_trusted_vault_client.h"
+#include "components/trusted_vault/standalone_trusted_vault_server_constants.h"
 #include "components/trusted_vault/test/fake_security_domains_server.h"
 #include "components/trusted_vault/trusted_vault_client.h"
 #include "components/trusted_vault/trusted_vault_connection.h"
diff --git a/chrome/browser/tabmodel/BUILD.gn b/chrome/browser/tabmodel/BUILD.gn
index f5f68129..8ea7f34 100644
--- a/chrome/browser/tabmodel/BUILD.gn
+++ b/chrome/browser/tabmodel/BUILD.gn
@@ -9,6 +9,7 @@
   resources_package = "org.chromium.chrome.browser.tabmodel"
 
   sources = [
+    "android/java/src/org/chromium/chrome/browser/tabmodel/ArchivedTabCountSupplier.java",
     "android/java/src/org/chromium/chrome/browser/tabmodel/ArchivedTabModelSelectorHolder.java",
     "android/java/src/org/chromium/chrome/browser/tabmodel/AsyncTabCreationParams.java",
     "android/java/src/org/chromium/chrome/browser/tabmodel/AsyncTabLauncher.java",
@@ -103,6 +104,7 @@
 
 robolectric_library("junit") {
   sources = [
+    "android/java/src/org/chromium/chrome/browser/tabmodel/ArchivedTabCountSupplierUnitTest.java",
     "android/java/src/org/chromium/chrome/browser/tabmodel/TabClosureParamsUnitTest.java",
     "android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupCollapsedUtilsUnitTest.java",
     "android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupColorUtilsUnitTest.java",
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/ArchivedTabCountSupplier.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/ArchivedTabCountSupplier.java
new file mode 100644
index 0000000..177d968
--- /dev/null
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/ArchivedTabCountSupplier.java
@@ -0,0 +1,121 @@
+// 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.tabmodel;
+
+import org.chromium.base.Callback;
+import org.chromium.base.lifetime.Destroyable;
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.supplier.ObservableSupplierImpl;
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.components.tab_group_sync.LocalTabGroupId;
+import org.chromium.components.tab_group_sync.SavedTabGroup;
+import org.chromium.components.tab_group_sync.TabGroupSyncService;
+import org.chromium.components.tab_group_sync.TabGroupSyncService.Observer;
+import org.chromium.components.tab_group_sync.TriggerSource;
+
+/**
+ * An {@link ObservableSupplier} which manages the total tab count in the archived surface, which
+ * includes tab counts in the archived {@link TabModel} and any tab groups from the {@link
+ * TabGroupSyncService}.
+ */
+@NullMarked
+public class ArchivedTabCountSupplier extends ObservableSupplierImpl<Integer>
+        implements Destroyable {
+    private final TabModel mArchivedTabModel;
+    private final ObservableSupplier<Integer> mArchivedTabModelTabCountSupplier;
+    private final Callback<Integer> mArchivedTabModelTabCountObserver =
+            (tabModelTabCount) -> {
+                updateArchivedTabCount();
+            };
+    private final @Nullable TabGroupSyncService mTabGroupSyncService;
+
+    private final Observer mTabGroupSyncObserver =
+            new Observer() {
+                @Override
+                public void onInitialized() {
+                    updateArchivedTabCount();
+                }
+
+                @Override
+                public void onTabGroupAdded(SavedTabGroup group, @TriggerSource int source) {
+                    updateArchivedTabCount();
+                }
+
+                @Override
+                public void onTabGroupUpdated(SavedTabGroup group, @TriggerSource int source) {
+                    updateArchivedTabCount();
+                }
+
+                @Override
+                public void onTabGroupRemoved(LocalTabGroupId localId, @TriggerSource int source) {
+                    updateArchivedTabCount();
+                }
+
+                @Override
+                public void onTabGroupRemoved(String syncId, @TriggerSource int source) {
+                    updateArchivedTabCount();
+                }
+            };
+
+    /**
+     * Creates an instance of {@link ArchivedTabCountSupplier}.
+     *
+     * @param archivedTabModel The {@link TabModel} representing archived tabs.
+     * @param tabGroupSyncService The {@link TabGroupSyncService} governing synced tab groups.
+     * @return The supplier that manages tab count updates from both the tab model and sync service.
+     */
+    public ArchivedTabCountSupplier(
+            TabModel archivedTabModel, @Nullable TabGroupSyncService tabGroupSyncService) {
+        mArchivedTabModel = archivedTabModel;
+        mArchivedTabModelTabCountSupplier = mArchivedTabModel.getTabCountSupplier();
+        mArchivedTabModelTabCountSupplier.addObserver(mArchivedTabModelTabCountObserver);
+        mTabGroupSyncService = tabGroupSyncService;
+
+        if (mTabGroupSyncService != null) {
+            mTabGroupSyncService.addObserver(mTabGroupSyncObserver);
+        }
+
+        // Set this supplier once so there is a base value at minimum.
+        super.set(mArchivedTabModelTabCountSupplier.get());
+    }
+
+    private void updateArchivedTabCount() {
+        int totalTabCount = getArchivedTabGroupTabCount();
+        totalTabCount += mArchivedTabModel.getCount();
+        super.set(totalTabCount);
+    }
+
+    private int getArchivedTabGroupTabCount() {
+        int archivedTabGroupTabCount = 0;
+        if (mTabGroupSyncService != null) {
+            for (String syncId : mTabGroupSyncService.getAllGroupIds()) {
+                SavedTabGroup savedTabGroup = mTabGroupSyncService.getGroup(syncId);
+
+                if (savedTabGroup != null && savedTabGroup.archivalTimeMs != null) {
+                    archivedTabGroupTabCount += savedTabGroup.savedTabs.size();
+                }
+            }
+        }
+
+        return archivedTabGroupTabCount;
+    }
+
+    @Override
+    public void set(Integer tabCount) {
+        assert false : "ArchivedTabCountSupplier should only be set through its observers.";
+    }
+
+    @Override
+    public void destroy() {
+        if (mArchivedTabModelTabCountSupplier != null) {
+            mArchivedTabModelTabCountSupplier.removeObserver(mArchivedTabModelTabCountObserver);
+        }
+
+        if (mTabGroupSyncService != null) {
+            mTabGroupSyncService.removeObserver(mTabGroupSyncObserver);
+        }
+    }
+}
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/ArchivedTabCountSupplierUnitTest.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/ArchivedTabCountSupplierUnitTest.java
new file mode 100644
index 0000000..0f65a3d9
--- /dev/null
+++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/ArchivedTabCountSupplierUnitTest.java
@@ -0,0 +1,124 @@
+// 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.tabmodel;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import org.chromium.base.Callback;
+import org.chromium.base.supplier.ObservableSupplier;
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.components.tab_group_sync.SavedTabGroup;
+import org.chromium.components.tab_group_sync.SavedTabGroupTab;
+import org.chromium.components.tab_group_sync.TabGroupSyncService;
+import org.chromium.components.tab_group_sync.TabGroupSyncService.Observer;
+
+import java.util.List;
+
+/** Unit tests for {@link ArchivedTabCountSupplier}. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class ArchivedTabCountSupplierUnitTest {
+    private static final int BASE_TAB_COUNT = 1;
+    private static final int TAB_MODEL_TAB_COUNT = 2;
+    private static final String SYNC_GROUP_ID = "test_sync_group_id1";
+
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock private ObservableSupplier<Integer> mArchivedTabModelTabCountSupplier;
+    @Mock private TabModel mTabModel;
+    @Mock private TabGroupSyncService mTabGroupSyncService;
+
+    @Captor ArgumentCaptor<Callback<Integer>> mArchivedTabModelTabCountObserverCaptor;
+    @Captor ArgumentCaptor<Observer> mTabGroupSyncServiceObserverCaptor;
+
+    private ArchivedTabCountSupplier mArchivedTabCountSupplier;
+
+    @Before
+    public void setUp() {
+        when(mTabModel.getTabCountSupplier()).thenReturn(mArchivedTabModelTabCountSupplier);
+        when(mArchivedTabModelTabCountSupplier.get()).thenReturn(BASE_TAB_COUNT);
+        doReturn(null)
+                .when(mArchivedTabModelTabCountSupplier)
+                .addObserver(mArchivedTabModelTabCountObserverCaptor.capture());
+        doNothing()
+                .when(mTabGroupSyncService)
+                .addObserver(mTabGroupSyncServiceObserverCaptor.capture());
+
+        mArchivedTabCountSupplier = new ArchivedTabCountSupplier(mTabModel, mTabGroupSyncService);
+    }
+
+    @Test
+    public void testTabModelUpdate() {
+        SavedTabGroupTab savedTab = new SavedTabGroupTab();
+        SavedTabGroup savedTabGroup = new SavedTabGroup();
+        savedTabGroup.syncId = SYNC_GROUP_ID;
+        savedTabGroup.savedTabs = List.of(savedTab);
+        savedTabGroup.archivalTimeMs = System.currentTimeMillis();
+
+        when(mTabGroupSyncService.getGroup(SYNC_GROUP_ID)).thenReturn(savedTabGroup);
+        when(mTabGroupSyncService.getAllGroupIds()).thenReturn(new String[] {SYNC_GROUP_ID});
+        when(mTabModel.getCount()).thenReturn(TAB_MODEL_TAB_COUNT);
+
+        mArchivedTabModelTabCountObserverCaptor.getValue().onResult(TAB_MODEL_TAB_COUNT);
+        assertEquals(
+                TAB_MODEL_TAB_COUNT + savedTabGroup.savedTabs.size(),
+                mArchivedTabCountSupplier.get().intValue());
+    }
+
+    @Test
+    public void testTabGroupSyncUpdate() {
+        SavedTabGroupTab savedTab = new SavedTabGroupTab();
+        SavedTabGroup savedTabGroup = new SavedTabGroup();
+        savedTabGroup.syncId = SYNC_GROUP_ID;
+        savedTabGroup.savedTabs = List.of(savedTab);
+        savedTabGroup.archivalTimeMs = System.currentTimeMillis();
+
+        when(mTabGroupSyncService.getGroup(SYNC_GROUP_ID)).thenReturn(savedTabGroup);
+        when(mTabGroupSyncService.getAllGroupIds()).thenReturn(new String[] {SYNC_GROUP_ID});
+        when(mTabModel.getCount()).thenReturn(TAB_MODEL_TAB_COUNT);
+
+        mTabGroupSyncServiceObserverCaptor.getValue().onInitialized();
+        assertEquals(
+                TAB_MODEL_TAB_COUNT + savedTabGroup.savedTabs.size(),
+                mArchivedTabCountSupplier.get().intValue());
+    }
+
+    @Test
+    public void testAllObserversUpdate() {
+        SavedTabGroupTab savedTab = new SavedTabGroupTab();
+        SavedTabGroup savedTabGroup = new SavedTabGroup();
+        savedTabGroup.syncId = SYNC_GROUP_ID;
+        savedTabGroup.savedTabs = List.of(savedTab);
+        savedTabGroup.archivalTimeMs = System.currentTimeMillis();
+
+        when(mTabGroupSyncService.getGroup(SYNC_GROUP_ID)).thenReturn(savedTabGroup);
+        when(mTabGroupSyncService.getAllGroupIds()).thenReturn(new String[] {SYNC_GROUP_ID});
+        when(mTabModel.getCount()).thenReturn(BASE_TAB_COUNT);
+
+        mTabGroupSyncServiceObserverCaptor.getValue().onInitialized();
+        assertEquals(
+                BASE_TAB_COUNT + savedTabGroup.savedTabs.size(),
+                mArchivedTabCountSupplier.get().intValue());
+
+        when(mTabModel.getCount()).thenReturn(TAB_MODEL_TAB_COUNT);
+
+        mArchivedTabModelTabCountObserverCaptor.getValue().onResult(TAB_MODEL_TAB_COUNT);
+        assertEquals(
+                TAB_MODEL_TAB_COUNT + savedTabGroup.savedTabs.size(),
+                mArchivedTabCountSupplier.get().intValue());
+    }
+}
diff --git a/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context_unittest.cc b/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context_unittest.cc
index 51cab4bd..be4d2b72 100644
--- a/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context_unittest.cc
+++ b/chrome/browser/top_level_storage_access_api/top_level_storage_access_permission_context_unittest.cc
@@ -216,9 +216,9 @@
         /*entries=*/
         {
             {net::SchemefulSite(GetRequesterURL()),
-             net::FirstPartySetEntry(top_level, net::SiteType::kAssociated, 0)},
-            {top_level, net::FirstPartySetEntry(
-                            top_level, net::SiteType::kPrimary, std::nullopt)},
+             net::FirstPartySetEntry(top_level, net::SiteType::kAssociated)},
+            {top_level,
+             net::FirstPartySetEntry(top_level, net::SiteType::kPrimary)},
         },
         /*aliases=*/{}));
   }
diff --git a/chrome/browser/tpcd/support/trial_test_utils.h b/chrome/browser/tpcd/support/trial_test_utils.h
index f98a236..50a72ac 100644
--- a/chrome/browser/tpcd/support/trial_test_utils.h
+++ b/chrome/browser/tpcd/support/trial_test_utils.h
@@ -66,7 +66,7 @@
 // Origin Trials token for `kTrialEnabledDomain` generated with:
 // tools/origin_trials/generate_token.py  https://example.test TopLevelTpcd
 // --expire-days 5000
-const char k1pDeprecationTrialToken[] =
+inline constexpr char k1pDeprecationTrialToken[] =
     "A5sGfiy3qkhJES3yFHkBd7i0jX8rC+"
     "pCA2M0tAhfmetOLkvOVTAR2589eHxZHbdv3QgX7BtANaw3A+"
     "A3NvgAtwIAAABXeyJvcmlnaW4iOiAiaHR0cHM6Ly9leGFtcGxlLnRlc3Q6NDQzIiwgImZlYXR1"
@@ -76,7 +76,7 @@
 // generated with:
 // tools/origin_trials/generate_token.py https://example.test TopLevelTpcd
 // --is-subdomain --expire-days 5000
-const char k1pDeprecationTrialSubdomainMatchingToken[] =
+inline constexpr char k1pDeprecationTrialSubdomainMatchingToken[] =
     "A5+BZIDRMyQWn2lWBHXWd3egEk2WqNdtEuzEbDZV0qXwYM8nKiqlHNYjGrfXuFgmUQ+"
     "j0wpk0EBVJC51I3K0gQkAAABseyJvcmlnaW4iOiAiaHR0cHM6Ly9leGFtcGxlLnRlc3Q6NDQzI"
     "iwgImZlYXR1cmUiOiAiVG9wTGV2ZWxUcGNkIiwgImV4cGlyeSI6IDIxMzkzMzg0NjcsICJpc1N"
@@ -85,7 +85,7 @@
 // Origin Trials token for `kTrialEnabledSiteSubdomain` generated with:
 // tools/origin_trials/generate_token.py  https://sub.example.test TopLevelTpcd
 // --expire-days 5000
-const char kSubdomain1pDeprecationTrialToken[] =
+inline constexpr char kSubdomain1pDeprecationTrialToken[] =
     "A7CJlPHXa8yQc2lJRvM/"
     "mq4Oi5+"
     "SJHbT4nnUmWiYKeuguuMkTd6y8DHBRAdEgvLXPajr9Qm2cMe4f5qzovm07QwAAABbeyJvcmlna"
@@ -96,7 +96,7 @@
 // generated with:
 // tools/origin_trials/generate_token.py https://sub.example.test TopLevelTpcd
 // --is-subdomain --expire-days 5000
-const char kSubdomain1pDeprecationTrialSubdomainMatchingToken[] =
+inline constexpr char kSubdomain1pDeprecationTrialSubdomainMatchingToken[] =
     "Ayuwtl4l9AC0MUBPlPDMZ3on5Db2hTQtFJdRM4fC1Bj03JLXWKNoe9bg4m5CslS5wFG9WQQsKu"
     "q/"
     "IbnFBxzGXwMAAABweyJvcmlnaW4iOiAiaHR0cHM6Ly9zdWIuZXhhbXBsZS50ZXN0OjQ0MyIsIC"
@@ -106,7 +106,7 @@
 // Origin Trials token for `kOtherTrialEnabledDomain` generated with:
 // tools/origin_trials/generate_token.py  https://example.test TopLevelTpcd
 // --expire-days 5000
-const char kOtherDomain1pDeprecationTrialToken[] =
+inline constexpr char kOtherDomain1pDeprecationTrialToken[] =
     "A7Dsv5nB89HuyiPl64hqJ0V0FporBq7g33dvjUABxJ3K4z3QdjCuurIGcF9wJJE0s1piYA2y4T"
     "0B+"
     "oO2n6sbIQwAAABVeyJvcmlnaW4iOiAiaHR0cHM6Ly9vdGhlci50ZXN0OjQ0MyIsICJmZWF0dXJ"
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index e1f99b8b4..0647eac 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -81,8 +81,6 @@
     "passwords/ui_utils.h",
     "passwords/well_known_change_password_navigation_throttle.cc",
     "passwords/well_known_change_password_navigation_throttle.h",
-    "plus_addresses/plus_address_creation_controller.h",
-    "plus_addresses/plus_address_creation_view.h",
     "profiles/profile_error_dialog.cc",
     "profiles/profile_error_dialog.h",
     "recently_audible_helper.cc",
@@ -421,6 +419,8 @@
     "//chrome/browser/ui/page_action:icon_type",
     "//chrome/browser/ui/page_info",
     "//chrome/browser/ui/page_info:impl",
+    "//chrome/browser/ui/plus_addresses",
+    "//chrome/browser/ui/plus_addresses:impl",
     "//chrome/browser/ui/prefs",
     "//chrome/browser/ui/prefs:impl",
     "//chrome/browser/ui/safety_hub",
@@ -1941,6 +1941,7 @@
       # TODO(crbug.com/382237520): Remove this once NTP code is modularized.
       "//chrome/browser/ui/lens:impl",
 
+      "//chrome/browser/ui/webui/new_tab_footer:impl",
       "//chrome/browser/ui/signin:impl",
       "//chrome/browser/ui/webui/commerce:impl",
       "//chrome/browser/ui/webui/signin:signin_impl",
@@ -3677,11 +3678,6 @@
   if (toolkit_views) {
     sources += [
       "bubble_anchor_util.h",
-      "plus_addresses/plus_address_creation_controller_desktop.h",
-      "plus_addresses/plus_address_error_dialog.cc",
-      "plus_addresses/plus_address_error_dialog.h",
-      "plus_addresses/plus_address_menu_model.cc",
-      "plus_addresses/plus_address_menu_model.h",
       "plus_addresses/views/plus_address_creation_controller_desktop.cc",
       "plus_addresses/views/plus_address_creation_dialog_delegate.cc",
       "plus_addresses/views/plus_address_creation_dialog_delegate.h",
diff --git a/chrome/browser/ui/android/android_about_app_info.cc b/chrome/browser/ui/android/android_about_app_info.cc
index aa6b83a1..7864759c 100644
--- a/chrome/browser/ui/android/android_about_app_info.cc
+++ b/chrome/browser/ui/android/android_about_app_info.cc
@@ -26,14 +26,3 @@
              embedder_support::IncludeAndroidBuildNumber::Include,
              embedder_support::IncludeAndroidModel::Include);
 }
-
-std::string AndroidAboutAppInfo::GetTargetsUInfo() {
-  std::string targets_u_info =
-      base::android::BuildInfo::GetInstance()->is_at_least_u() ? "true"
-                                                               : "false";
-  targets_u_info += "/";
-  targets_u_info +=
-      base::android::BuildInfo::GetInstance()->targets_at_least_u() ? "true"
-                                                                    : "false";
-  return targets_u_info;
-}
diff --git a/chrome/browser/ui/android/android_about_app_info.h b/chrome/browser/ui/android/android_about_app_info.h
index 06bea07..eef9d2b 100644
--- a/chrome/browser/ui/android/android_about_app_info.h
+++ b/chrome/browser/ui/android/android_about_app_info.h
@@ -15,10 +15,6 @@
 
   // Returns a string containing detailed info about the os environment.
   static std::string GetOsInfo();
-
-  // Returns a string containing info about whether the device is at least
-  // Android U and whether Chrome targets at least U.
-  static std::string GetTargetsUInfo();
 };
 
 #endif  // CHROME_BROWSER_UI_ANDROID_ANDROID_ABOUT_APP_INFO_H_
diff --git a/chrome/browser/ui/android/omnibox/BUILD.gn b/chrome/browser/ui/android/omnibox/BUILD.gn
index 8b8171d..2ab5ece3 100644
--- a/chrome/browser/ui/android/omnibox/BUILD.gn
+++ b/chrome/browser/ui/android/omnibox/BUILD.gn
@@ -81,6 +81,7 @@
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsVisualState.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/PreWarmingRecycledViewPool.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/RecyclerViewSelectionController.java",
+    "java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionController.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionCommonProperties.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHorizontalDivider.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHost.java",
@@ -305,6 +306,7 @@
     "java/res/drawable-xxxhdpi/btn_suggestion_refine.png",
     "java/res/drawable-xxxhdpi/ic_history_googblue_24dp.png",
     "java/res/drawable-xxxhdpi/ic_suggestion_magnifier.png",
+    "java/res/drawable/hairline_circle.xml",
     "java/res/drawable/ic_book_round.xml",
     "java/res/drawable/ic_content_copy_black.xml",
     "java/res/drawable/ic_equals_sign_round.xml",
@@ -424,6 +426,7 @@
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/OmniboxSuggestionsDropdownUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/PreWarmingRecycledViewPoolTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/RecyclerViewSelectionControllerUnitTest.java",
+    "java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionControllerUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHorizontalDividerTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionListViewBinderUnitTest.java",
     "java/src/org/chromium/chrome/browser/omnibox/suggestions/UnsyncedSuggestionsListAnimationDriverTest.java",
diff --git a/chrome/browser/ui/android/omnibox/java/res/drawable/hairline_circle.xml b/chrome/browser/ui/android/omnibox/java/res/drawable/hairline_circle.xml
new file mode 100644
index 0000000..b214e195
--- /dev/null
+++ b/chrome/browser/ui/android/omnibox/java/res/drawable/hairline_circle.xml
@@ -0,0 +1,14 @@
+<!--
+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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+  <item android:gravity="center">
+    <shape android:shape="oval">
+      <solid android:color="@android:color/transparent"/>
+      <stroke android:width="2dp" android:color="@android:color/black"/>
+      <size android:width="32dp" android:height="32dp" />
+    </shape>
+  </item>
+</layer-list>
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionController.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionController.java
new file mode 100644
index 0000000..e0f2af9
--- /dev/null
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionController.java
@@ -0,0 +1,205 @@
+// 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.omnibox.suggestions;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
+
+import org.chromium.base.MathUtils;
+import org.chromium.build.annotations.NullMarked;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.OptionalInt;
+
+/**
+ * Helper class allowing advancing forward/backward while saturating outside the valid range.
+ *
+ * <p>TODO(344930378): Explore possibility to reconcile this with RecyclerViewSelectionController.
+ * The two classes serve similar purpose, but the complexity of view recycling may make the merge
+ * difficult. This controller expands RVSC capabilities, however the following aspects make
+ * immediate merge difficult: - volume of items changing at runtime, - exposure triggering (to
+ * ensure we can locate views for items not currently bound), - reused views propagate the selected
+ * state when rebound to a different item,
+ *
+ * <p>Consider adding WRAPPING and WRAPPING_WITH_SENTINEL variants to allow cycling through.
+ */
+@NullMarked
+public class SelectionController {
+    /**
+     * Operational modes of the SelectionController
+     *
+     * <ul>
+     *   <li>SATURATING:
+     *       <ul>
+     *         <li>forward: A -> B -> C -> C -> C
+     *         <li>backward: C -> B -> A -> A -> A
+     *       </ul>
+     *   <li>SATURATING_WITH_SENTINEL:
+     *       <ul>
+     *         <li>forward: ∅- -> A -> B -> C -> ∅+ -> ∅+
+     *         <li>backward: ∅+ -> C -> B -> A -> ∅- -> ∅-
+     *       </ul>
+     * </ul>
+     */
+    @IntDef({Mode.SATURATING, Mode.SATURATING_WITH_SENTINEL})
+    @Retention(RetentionPolicy.SOURCE)
+    @Target(ElementType.TYPE_USE)
+    public @interface Mode {
+        int SATURATING = 0;
+        int SATURATING_WITH_SENTINEL = 1;
+    }
+
+    private final OnSelectionChangedListener mListener;
+    private final @Mode int mMode;
+    private final int mDefaultPosition;
+    private int mMaxPosition;
+    private int mPosition;
+
+    @FunctionalInterface
+    public interface OnSelectionChangedListener {
+        /**
+         * Invoked whenever selected state at specific position changed.
+         *
+         * @param position the position to apply selection change to
+         * @param isSelected whether that position should be selected
+         * @return whether selection was applied at requested position
+         */
+        boolean onSelectionChanged(int position, boolean isSelected);
+    }
+
+    /**
+     * SelectionController constructor.
+     *
+     * @param listener the listener receiving notifications about selection changes
+     */
+    public SelectionController(OnSelectionChangedListener listener, @Mode int mode) {
+        this(listener, 0, mode);
+    }
+
+    /**
+     * SelectionController constructor.
+     *
+     * @param listener the listener receiving notifications about selection changes
+     * @param maxPosition the maximum valid position that can be reported to the listener
+     * @param mode Selection mode that defines how the controller will behave
+     */
+    public SelectionController(
+            OnSelectionChangedListener listener, int maxPosition, @Mode int mode) {
+        assert maxPosition < Integer.MAX_VALUE;
+        assert maxPosition >= 0;
+
+        switch (mode) {
+            case Mode.SATURATING:
+                mDefaultPosition = 0;
+                break;
+
+            case Mode.SATURATING_WITH_SENTINEL:
+            default:
+                mDefaultPosition = -1; // Just before the first entry.
+                break;
+        }
+
+        // Initialization step only, to ensure we do not emit bogus selection change event.
+        mPosition = Integer.MIN_VALUE;
+        mListener = listener;
+        mMode = mode;
+        updateMaxPosition(maxPosition);
+    }
+
+    /**
+     * Update range of valid positions.
+     *
+     * @param maxPosition the upper value in the selection range (inclusive)
+     */
+    public void updateMaxPosition(int maxPosition) {
+        if (!isParkedAtSentinel()) {
+            mListener.onSelectionChanged(mPosition, false);
+        }
+
+        mMaxPosition = maxPosition;
+        mPosition = mDefaultPosition;
+
+        if (!isParkedAtSentinel()) {
+            mListener.onSelectionChanged(mPosition, true);
+        }
+    }
+
+    /** Resets the controller, making the current position point to default item. */
+    public void reset() {
+        setPosition(mDefaultPosition);
+    }
+
+    /**
+     * Advances the counter towards the maxPosition, returning false if the held value has
+     * saturated.
+     *
+     * @return whether selection was applied to the new element.
+     */
+    public boolean advanceForward() {
+        return setPosition(mPosition + 1);
+    }
+
+    /**
+     * Advances the counter towards the minPosition, returning false if the held value has
+     * saturated.
+     *
+     * @return whether selection was applied to the new element.
+     */
+    public boolean advanceBack() {
+        return setPosition(mPosition - 1);
+    }
+
+    /** Returns true if selection controller is currently parked outside the valid range. */
+    public boolean isParkedAtSentinel() {
+        return mPosition < 0 || mPosition > mMaxPosition;
+    }
+
+    /** Returns current counter value (unless saturated). */
+    public OptionalInt getPosition() {
+        if (isParkedAtSentinel()) return OptionalInt.empty();
+        return OptionalInt.of(mPosition);
+    }
+
+    /**
+     * Set the new counter value, saturating it according to @Mode.
+     *
+     * @param newPosition - new value to apply to the mPosition
+     * @return whether selection was applied to the new element.
+     */
+    @VisibleForTesting
+    boolean setPosition(int newPosition) {
+        if (!isParkedAtSentinel()) {
+            mListener.onSelectionChanged(mPosition, false);
+        }
+
+        int oldPosition = mPosition;
+        mPosition = newPosition;
+        switch (mMode) {
+            case Mode.SATURATING:
+                mPosition = MathUtils.clamp(mPosition, 0, mMaxPosition);
+                break;
+
+            case Mode.SATURATING_WITH_SENTINEL:
+                // Park outside the valid range, keeping the information which edge we hit.
+                mPosition = MathUtils.clamp(mPosition, -1, mMaxPosition + 1);
+                break;
+        }
+
+        if (isParkedAtSentinel()) return false;
+
+        // Select new item, fall back to old position if not possible.
+        if (!mListener.onSelectionChanged(mPosition, true)) {
+            mPosition = oldPosition;
+            mListener.onSelectionChanged(mPosition, true);
+            // We failed to select the requested entry.
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionControllerUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionControllerUnitTest.java
new file mode 100644
index 0000000..bc0d767
--- /dev/null
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SelectionControllerUnitTest.java
@@ -0,0 +1,190 @@
+// 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.omnibox.suggestions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.chrome.browser.omnibox.suggestions.SelectionController.Mode;
+
+import java.util.OptionalInt;
+
+/** Robolectric unit tests for {@link SelectionController}. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class SelectionControllerUnitTest {
+    private static final int MAX_POSITION = 2; // Items 0‒2 inclusive.
+
+    public @Rule MockitoRule mMockitoRule = MockitoJUnit.rule();
+    private @Mock SelectionController.OnSelectionChangedListener mListener;
+
+    @Before
+    public void setUp() {
+        // Allow selection changes to succeed unless explicitly overridden.
+        when(mListener.onSelectionChanged(anyInt(), eq(true))).thenReturn(true);
+        when(mListener.onSelectionChanged(anyInt(), eq(false))).thenReturn(true);
+    }
+
+    private void verifyPositionReset(SelectionController c, int position) {
+        verify(mListener).onSelectionChanged(position, false);
+        assertEquals(OptionalInt.empty(), c.getPosition());
+        assertTrue(c.isParkedAtSentinel());
+        clearInvocations(mListener);
+    }
+
+    private void verifyPositionSet(SelectionController c, int position) {
+        verify(mListener).onSelectionChanged(position, true);
+        assertEquals(OptionalInt.of(position), c.getPosition());
+        assertFalse(c.isParkedAtSentinel());
+        clearInvocations(mListener);
+    }
+
+    private void verifyPositionChanged(SelectionController c, int from, int to) {
+        verify(mListener).onSelectionChanged(from, false);
+        verifyPositionSet(c, to);
+    }
+
+    @Test
+    public void advanceForward_saturating() {
+        SelectionController c = new SelectionController(mListener, MAX_POSITION, Mode.SATURATING);
+        verifyPositionSet(c, 0);
+
+        assertTrue(c.advanceForward());
+        verifyPositionChanged(c, 0, 1);
+
+        assertTrue(c.advanceForward());
+        verifyPositionChanged(c, 1, 2);
+
+        assertTrue(c.advanceForward());
+        verifyPositionChanged(c, 2, 2);
+
+        assertTrue(c.advanceForward());
+        verifyPositionChanged(c, 2, 2);
+    }
+
+    @Test
+    public void advanceForward_saturatingWithSentinel() {
+        SelectionController c =
+                new SelectionController(mListener, MAX_POSITION, Mode.SATURATING_WITH_SENTINEL);
+        assertTrue(c.isParkedAtSentinel());
+
+        assertTrue(c.advanceForward());
+        verifyPositionSet(c, 0);
+
+        assertTrue(c.advanceForward());
+        verifyPositionChanged(c, 0, 1);
+
+        assertTrue(c.advanceForward());
+        verifyPositionChanged(c, 1, 2);
+
+        assertFalse(c.advanceForward());
+        verifyPositionReset(c, 2);
+
+        assertFalse(c.advanceForward());
+        verifyNoMoreInteractions(mListener);
+    }
+
+    @Test
+    public void advanceBack_saturating() {
+        SelectionController c = new SelectionController(mListener, MAX_POSITION, Mode.SATURATING);
+        c.setPosition(MAX_POSITION);
+        verifyPositionChanged(c, 0, 2);
+
+        assertTrue(c.advanceBack());
+        verifyPositionChanged(c, 2, 1);
+
+        assertTrue(c.advanceBack());
+        verifyPositionChanged(c, 1, 0);
+
+        assertTrue(c.advanceBack());
+        verifyPositionChanged(c, 0, 0);
+
+        assertTrue(c.advanceBack());
+        verifyPositionChanged(c, 0, 0);
+    }
+
+    @Test
+    public void advanceBack_saturatingWithSentinel() {
+        SelectionController c =
+                new SelectionController(mListener, MAX_POSITION, Mode.SATURATING_WITH_SENTINEL);
+        c.setPosition(MAX_POSITION);
+        verifyPositionSet(c, 2);
+
+        assertTrue(c.advanceBack());
+        verifyPositionChanged(c, 2, 1);
+
+        assertTrue(c.advanceBack());
+        verifyPositionChanged(c, 1, 0);
+
+        assertFalse(c.advanceBack());
+        verifyPositionReset(c, 0);
+
+        assertFalse(c.advanceBack());
+        verifyNoMoreInteractions(mListener);
+    }
+
+    @Test
+    public void advanceForward_saturating_listenerReturnsFalse() {
+        when(mListener.onSelectionChanged(1, true)).thenReturn(false);
+
+        SelectionController c =
+                new SelectionController(
+                        mListener, MAX_POSITION, SelectionController.Mode.SATURATING);
+        verifyPositionSet(c, 0);
+        assertFalse(c.advanceForward());
+        verifyPositionChanged(c, 0, 0);
+    }
+
+    @Test
+    public void updateMaxPosition() {
+        SelectionController c = new SelectionController(mListener, MAX_POSITION, Mode.SATURATING);
+        verifyPositionSet(c, 0);
+
+        // Grow list of items
+        c.updateMaxPosition(4);
+        verifyPositionSet(c, 0);
+
+        assertTrue(c.advanceForward()); // Should now reach index 4 without saturating
+        verifyPositionChanged(c, 0, 1);
+        assertTrue(c.advanceForward()); // 2
+        verifyPositionChanged(c, 1, 2);
+        assertTrue(c.advanceForward()); // 3
+        verifyPositionChanged(c, 2, 3);
+        assertTrue(c.advanceForward()); // 4
+        verifyPositionChanged(c, 3, 4);
+
+        // Shrink list of items
+        c.updateMaxPosition(2);
+        verifyPositionSet(c, 0);
+    }
+
+    @Test
+    public void reset_saturating() {
+        SelectionController c = new SelectionController(mListener, MAX_POSITION, Mode.SATURATING);
+        verifyPositionSet(c, 0);
+
+        c.advanceForward(); // 1
+        verifyPositionChanged(c, 0, 1);
+        c.advanceForward(); // 2
+        verifyPositionChanged(c, 1, 2);
+        c.reset(); // back to default (0)
+        verifyPositionChanged(c, 2, 0);
+    }
+}
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
index 8449f13..848b140 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionView.java
@@ -13,17 +13,21 @@
 
 import androidx.annotation.LayoutRes;
 import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.content.res.AppCompatResources;
 import androidx.appcompat.widget.AppCompatImageView;
 
 import org.chromium.build.annotations.CheckDiscard;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.browser.omnibox.R;
+import org.chromium.chrome.browser.omnibox.suggestions.SelectionController;
 import org.chromium.chrome.browser.util.KeyNavigationUtil;
 import org.chromium.components.browser_ui.widget.RoundedCornerOutlineProvider;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
+import java.util.OptionalInt;
 
 /**
  * Base layout for common suggestion types. Includes support for a configurable suggestion content
@@ -39,6 +43,7 @@
     public final RoundedCornerOutlineProvider decorationIconOutline;
     private final List<ImageView> mActionButtons;
     private Optional<Runnable> mOnFocusViaSelectionListener = Optional.empty();
+    private @Nullable SelectionController mActionButtonsHighlighter;
 
     /**
      * Constructs a new suggestion view and inflates supplied layout as the contents view.
@@ -95,6 +100,15 @@
         } else if (currentViewCount > desiredViewCount) {
             decreaseActionButtonsCount(desiredViewCount);
         }
+
+        mActionButtonsHighlighter = null;
+        if (desiredViewCount > 0) {
+            mActionButtonsHighlighter =
+                    new SelectionController(
+                            this::highlightActionButton,
+                            desiredViewCount - 1,
+                            SelectionController.Mode.SATURATING_WITH_SENTINEL);
+        }
     }
 
     /**
@@ -105,6 +119,24 @@
     }
 
     /**
+     * Applies / removes selection hairline from action button.
+     *
+     * @param buttonIndex the index of an action button
+     * @param isSelected whether to apply hairline
+     * @return the highlight state of the specified action button.
+     */
+    private boolean highlightActionButton(int buttonIndex, boolean isHighlighted) {
+        mActionButtons
+                .get(buttonIndex)
+                .setForeground(
+                        isHighlighted
+                                ? AppCompatResources.getDrawable(
+                                        getContext(), R.drawable.hairline_circle)
+                                : null);
+        return isHighlighted;
+    }
+
+    /**
      * Create additional action buttons for the suggestion view.
      *
      * @param desiredViewCount Desired number of action buttons.
@@ -141,8 +173,31 @@
         // navigation.
         if (actionChipsView.onKeyDown(keyCode, event)) return true;
         if (KeyNavigationUtil.isEnter(event)) {
+            if (mActionButtonsHighlighter != null
+                    && !mActionButtonsHighlighter.isParkedAtSentinel()) {
+                OptionalInt selection = mActionButtonsHighlighter.getPosition();
+                return mActionButtons.get(selection.getAsInt()).performClick();
+            }
             return performClick();
         }
+
+        // Allow browsing through right hand side buttons.
+        if (keyCode == KeyEvent.KEYCODE_TAB) {
+            if (!event.isShiftPressed()) {
+                // Pass the TAB key to Action Buttons, then to Action Chips.
+                if (mActionButtonsHighlighter != null
+                        && mActionButtonsHighlighter.advanceForward()) {
+                    return true;
+                }
+                return super_onKeyDown(keyCode, event);
+            } else {
+                // Pass the TAB key to Action Chips, then to Action Buttons.
+                if (super_onKeyDown(keyCode, event)) return true;
+                return (mActionButtonsHighlighter != null
+                        && mActionButtonsHighlighter.advanceBack());
+            }
+        }
+
         return super_onKeyDown(keyCode, event);
     }
 
@@ -155,6 +210,7 @@
     @Override
     public void setSelected(boolean selected) {
         super.setSelected(selected);
+        if (mActionButtonsHighlighter != null) mActionButtonsHighlighter.reset();
         if (selected) mOnFocusViaSelectionListener.ifPresent(Runnable::run);
     }
 
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
index e3db3989..3902f73 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/base/BaseSuggestionViewBinder.java
@@ -215,6 +215,7 @@
         final List<ImageView> actionViews = view.getActionButtons();
         for (int index = 0; index < actionViews.size(); index++) {
             ImageView actionView = actionViews.get(index);
+
             applySelectableBackground(model, actionView);
             updateIcon(
                     actionView,
@@ -348,6 +349,7 @@
         }
 
         view.setImageDrawable(sds.drawable);
+        view.setForegroundTintList(tint);
         ImageViewCompat.setImageTintList(view, tint);
     }
 
diff --git a/chrome/browser/ui/android/toolbar/BUILD.gn b/chrome/browser/ui/android/toolbar/BUILD.gn
index a4c053f..8de9b93 100644
--- a/chrome/browser/ui/android/toolbar/BUILD.gn
+++ b/chrome/browser/ui/android/toolbar/BUILD.gn
@@ -64,6 +64,8 @@
     "java/src/org/chromium/chrome/browser/toolbar/extensions/ExtensionToolbarManager.java",
     "java/src/org/chromium/chrome/browser/toolbar/home_button/HomeButton.java",
     "java/src/org/chromium/chrome/browser/toolbar/home_button/HomeButtonCoordinator.java",
+    "java/src/org/chromium/chrome/browser/toolbar/home_page_button/HomePageButtonView.java",
+    "java/src/org/chromium/chrome/browser/toolbar/home_page_button/HomePageButtonsContainerView.java",
     "java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressCoordinator.java",
     "java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressMediator.java",
     "java/src/org/chromium/chrome/browser/toolbar/load_progress/LoadProgressProperties.java",
@@ -320,6 +322,7 @@
     "java/res/layout/bottom_control_container.xml",
     "java/res/layout/control_container.xml",
     "java/res/layout/extension_toolbar_container.xml",
+    "java/res/layout/home_page_buttons_layout.xml",
     "java/res/layout/menu_button.xml",
     "java/res/layout/navigation_popup_item.xml",
     "java/res/layout/optional_button_layout.xml",
diff --git a/chrome/browser/ui/android/toolbar/java/res/layout/home_page_buttons_layout.xml b/chrome/browser/ui/android/toolbar/java/res/layout/home_page_buttons_layout.xml
new file mode 100644
index 0000000..cce9f33
--- /dev/null
+++ b/chrome/browser/ui/android/toolbar/java/res/layout/home_page_buttons_layout.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+
+<org.chromium.chrome.browser.toolbar.home_page_button.HomePageButtonsContainerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/home_page_buttons_layout"
+    android:layout_height="match_parent"
+    android:layout_width="wrap_content"
+    android:orientation="horizontal">
+
+    <org.chromium.chrome.browser.toolbar.home_page_button.HomePageButtonView
+        android:id="@+id/home_button"
+        android:src="@drawable/btn_toolbar_home"
+        android:contentDescription="@string/accessibility_toolbar_btn_home"
+        android:tooltipText="@string/accessibility_toolbar_btn_home"
+        style="@style/ToolbarHoverableButton"
+        android:visibility="gone"
+        app:menuVerticalOverlapAnchor="false"
+        app:menuMaxWidth="@dimen/home_button_list_menu_width"
+        app:menuPositionedAtStart="true"
+        app:tint="@color/default_icon_color_tint_list" />
+
+    <org.chromium.chrome.browser.toolbar.home_page_button.HomePageButtonView
+        android:id="@+id/ntp_customization_button"
+        android:src="@drawable/edit_icon"
+        android:contentDescription="@string/ntp_customization_title"
+        style="@style/ToolbarHoverableButton"
+        android:visibility="gone"
+        app:tint="@color/default_icon_color_tint_list" />
+
+</org.chromium.chrome.browser.toolbar.home_page_button.HomePageButtonsContainerView>
\ No newline at end of file
diff --git a/chrome/browser/ui/android/toolbar/java/res/layout/toolbar_phone.xml b/chrome/browser/ui/android/toolbar/java/res/layout/toolbar_phone.xml
index 7987b2df..592005f 100644
--- a/chrome/browser/ui/android/toolbar/java/res/layout/toolbar_phone.xml
+++ b/chrome/browser/ui/android/toolbar/java/res/layout/toolbar_phone.xml
@@ -27,6 +27,13 @@
         app:menuPositionedAtStart="true"
         app:tint="@color/default_icon_color_tint_list" />
 
+    <ViewStub
+        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        android:id="@+id/home_page_buttons_stub"
+        android:layout="@layout/home_page_buttons_layout"
+        android:visibility="gone" />
+
     <org.chromium.chrome.browser.omnibox.LocationBarPhone
         android:id="@+id/location_bar"
         android:layout_width="match_parent"
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java
index 7d0890f..a00f865 100644
--- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/adaptive/AdaptiveToolbarButtonController.java
@@ -41,6 +41,7 @@
 import org.chromium.chrome.browser.toolbar.R;
 import org.chromium.chrome.browser.toolbar.adaptive.AdaptiveToolbarStatePredictor.UiState;
 import org.chromium.chrome.browser.toolbar.adaptive.settings.AdaptiveToolbarSettingsFragment;
+import org.chromium.chrome.browser.toolbar.optional_button.BaseButtonDataProvider;
 import org.chromium.chrome.browser.toolbar.optional_button.ButtonData;
 import org.chromium.chrome.browser.toolbar.optional_button.ButtonData.ButtonSpec;
 import org.chromium.chrome.browser.toolbar.optional_button.ButtonDataImpl;
@@ -178,6 +179,20 @@
         mButtonDataProviderMap.put(variant, buttonProvider);
     }
 
+    /**
+     * Invoke Price Insights UI. TODO(crbug.com/391931899): Consider making this method generic to
+     * support other button variants.
+     */
+    public void runPriceInsightsAction() {
+        var buttonDataProvider =
+                mButtonDataProviderMap.get(AdaptiveToolbarButtonVariant.PRICE_INSIGHTS);
+        if (buttonDataProvider instanceof BaseButtonDataProvider toolbarButtonProvider) {
+            toolbarButtonProvider.onClick(new View(mContext)); // Param is not used.
+        } else {
+            assert false : "PriceInsightButtonController must inherit BaseButtonDataProvider!";
+        }
+    }
+
     @Override
     // Suppress to observe SharedPreferences, which is discouraged; use another messaging channel
     // instead.
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/home_page_button/HomePageButtonView.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/home_page_button/HomePageButtonView.java
new file mode 100644
index 0000000..ba3d365
--- /dev/null
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/home_page_button/HomePageButtonView.java
@@ -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.
+
+package org.chromium.chrome.browser.toolbar.home_page_button;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.ui.listmenu.ListMenuButton;
+
+/** The home page button. */
+@NullMarked
+public class HomePageButtonView extends ListMenuButton {
+    public HomePageButtonView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/home_page_button/HomePageButtonsContainerView.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/home_page_button/HomePageButtonsContainerView.java
new file mode 100644
index 0000000..4dda09c
--- /dev/null
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/home_page_button/HomePageButtonsContainerView.java
@@ -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.
+
+package org.chromium.chrome.browser.toolbar.home_page_button;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+
+/** The container of the two home page buttons. */
+@NullMarked
+public class HomePageButtonsContainerView extends LinearLayout {
+    public HomePageButtonsContainerView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+}
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/home_page_button/OWNERS b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/home_page_button/OWNERS
new file mode 100644
index 0000000..e153613
--- /dev/null
+++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/home_page_button/OWNERS
@@ -0,0 +1,2 @@
+hanxi@chromium.org
+xinyiji@chromium.org
\ No newline at end of file
diff --git a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediator.java b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediator.java
index 6870815..52772d7 100644
--- a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediator.java
+++ b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediator.java
@@ -44,7 +44,6 @@
     private final ScrimManager mScrimManager;
     private final Supplier<List<Rect>> mNonDraggableAreasSupplier;
     private final ObservableSupplierImpl<Integer> mWidthSupplier;
-    private final Callback<Integer> mOnWidthChangedCallback;
     private final ThemeColorProvider mThemeColorProvider;
     private final int mWebAppMinHeaderHeight;
     private @Nullable AppHeaderState mCurrentHeaderState;
@@ -96,12 +95,10 @@
 
         mWidthSupplier = new ObservableSupplierImpl<>();
         mAppHeaderUnoccludedWidthSupplier = new ObservableSupplierImpl<>();
-        mOnWidthChangedCallback = (width) -> updateNonDraggableAreas();
-        mWidthSupplier.addObserver(mOnWidthChangedCallback);
 
         mModel = model;
         // View should notify us about initial width.
-        mModel.set(WebAppHeaderLayoutProperties.WIDTH_CHANGED_CALLBACK, mWidthSupplier::set);
+        mModel.set(WebAppHeaderLayoutProperties.WIDTH_CHANGED_CALLBACK, this::onLayoutWidthUpdated);
 
         final var appHeaderState = desktopWindowStateManager.getAppHeaderState();
         if (appHeaderState != null) {
@@ -113,6 +110,13 @@
         mThemeColorProvider.addThemeColorObserver(this);
     }
 
+    private void onLayoutWidthUpdated(int width) {
+        mWidthSupplier.set(width);
+
+        // Update draggable area even if width hasn't changed, because children might've changed.
+        updateNonDraggableAreas();
+    }
+
     @Override
     public void onThemeColorChanged(int color, boolean shouldAnimate) {
         mDesktopWindowStateManager.updateForegroundColor(color);
@@ -166,7 +170,9 @@
         }
 
         final var areas = mNonDraggableAreasSupplier.get();
-        mModel.set(WebAppHeaderLayoutProperties.NON_DRAGGABLE_AREAS, areas);
+        mModel.set(
+                WebAppHeaderLayoutProperties.NON_DRAGGABLE_AREAS,
+                areas == null || areas.isEmpty() ? List.of(EMPTY_NON_DRAGGABLE_AREA) : areas);
     }
 
     /** Navigates back in the navigation history of the current {@link Tab}. */
@@ -190,7 +196,6 @@
     /** Destroys the mediator, existing instance is not usable after this method is called */
     public void destroy() {
         mDesktopWindowStateManager.removeObserver(this);
-        mWidthSupplier.removeObserver(mOnWidthChangedCallback);
         mThemeColorProvider.removeThemeColorObserver(this);
         mScrimManager.getScrimVisibilitySupplier().removeObserver(mScrimVisibilityObserver);
     }
diff --git a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediatorTest.java b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediatorTest.java
index 343a25e..88f14f61 100644
--- a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediatorTest.java
+++ b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutMediatorTest.java
@@ -282,7 +282,10 @@
     public void testInDWButNotInWindow_AllAreaIsDraggable() {
         setupDesktopWindowing(/* isInDesktopWindow= */ false, WIDEST_UNOCCLUDED_RECT);
 
+        final var nonDraggableAreas = List.of(new Rect(0, 0, 10, 10), new Rect(10, 0, 10, 10));
+        mNonDraggableAreasSupplier.set(nonDraggableAreas);
         mMediator.onAppHeaderStateChanged(mAppHeaderState);
+
         mModel.get(WebAppHeaderLayoutProperties.WIDTH_CHANGED_CALLBACK).onResult(SCREEN_WIDTH);
 
         final var areas = mModel.get(WebAppHeaderLayoutProperties.NON_DRAGGABLE_AREAS);
@@ -313,6 +316,38 @@
     }
 
     @Test
+    public void testInDwLayoutStructureChanges_SetNonDraggableAreaOnEachUpdate() {
+        setupDesktopWindowing(/* isInDesktopWindow= */ true, WIDEST_UNOCCLUDED_RECT);
+
+        // Setup layout without children.
+        final List<Rect> initialNonDraggableArea = List.of();
+        mNonDraggableAreasSupplier.set(initialNonDraggableArea);
+        mMediator.onAppHeaderStateChanged(mAppHeaderState);
+        mModel.get(WebAppHeaderLayoutProperties.WIDTH_CHANGED_CALLBACK).onResult(SCREEN_WIDTH);
+
+        // Verify area is empty.
+        var areas = mModel.get(WebAppHeaderLayoutProperties.NON_DRAGGABLE_AREAS);
+        assertEquals("There should be only one area in the list", 1, areas.size());
+        assertEquals(
+                "The area should be an empty area that allows to drag everywhere",
+                new Rect(0, 0, 0, 0),
+                areas.get(0));
+
+        // Children has laid out and layout update is sent with the same width.
+        final var nonDraggableAreas = List.of(new Rect(0, 0, 10, 10), new Rect(10, 0, 10, 10));
+        mNonDraggableAreasSupplier.set(nonDraggableAreas);
+        mModel.get(WebAppHeaderLayoutProperties.WIDTH_CHANGED_CALLBACK).onResult(SCREEN_WIDTH);
+
+        // Verify non-draggable area is updated.
+        areas = mModel.get(WebAppHeaderLayoutProperties.NON_DRAGGABLE_AREAS);
+        assertEquals("There should be only 2 non draggable areas", 2, areas.size());
+        assertArrayEquals(
+                "Non draggable areas from supplier should match model areas",
+                areas.toArray(),
+                mModel.get(WebAppHeaderLayoutProperties.NON_DRAGGABLE_AREAS).toArray());
+    }
+
+    @Test
     public void testSetInitialTheme() {
         setupDesktopWindowing(/* isInDesktopWindow= */ true, WIDEST_UNOCCLUDED_RECT);
         assertEquals(
diff --git a/chrome/browser/ui/ash/in_session_auth/BUILD.gn b/chrome/browser/ui/ash/in_session_auth/BUILD.gn
index 4ab90452..7d27315ae 100644
--- a/chrome/browser/ui/ash/in_session_auth/BUILD.gn
+++ b/chrome/browser/ui/ash/in_session_auth/BUILD.gn
@@ -49,6 +49,7 @@
     "//base/test:test_support",
     "//chrome/browser/ash/login/users:test_support",
     "//chrome/browser/ash/profiles",
+    "//chrome/test:test_support",
     "//chromeos/ash/components/cryptohome",
     "//chromeos/ash/components/dbus/userdataauth",
     "//chromeos/ash/components/install_attributes:test_support",
diff --git a/chrome/browser/ui/ash/in_session_auth/in_session_auth_dialog_client.cc b/chrome/browser/ui/ash/in_session_auth/in_session_auth_dialog_client.cc
index 3bcb2c3..c810ecff 100644
--- a/chrome/browser/ui/ash/in_session_auth/in_session_auth_dialog_client.cc
+++ b/chrome/browser/ui/ash/in_session_auth/in_session_auth_dialog_client.cc
@@ -8,9 +8,11 @@
 
 #include "ash/constants/ash_features.h"
 #include "ash/public/cpp/webauthn_dialog_controller.h"
+#include "base/check_deref.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
 #include "base/logging.h"
+#include "base/memory/raw_ref.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/browser/ash/auth/cryptohome_pin_engine.h"
 #include "chrome/browser/ash/auth/legacy_fingerprint_engine.h"
@@ -38,6 +40,8 @@
 using ::ash::Key;
 using ::ash::UserContext;
 
+class PrefService;
+
 namespace {
 
 const char kInSessionAuthHelpPageUrl[] =
@@ -47,8 +51,9 @@
 
 }  // namespace
 
-InSessionAuthDialogClient::InSessionAuthDialogClient()
-    : auth_performer_(ash::UserDataAuthClient::Get()) {
+InSessionAuthDialogClient::InSessionAuthDialogClient(PrefService* local_state)
+    : local_state_(CHECK_DEREF(local_state)),
+      auth_performer_(ash::UserDataAuthClient::Get()) {
   ash::WebAuthNDialogController::Get()->SetClient(this);
 
   DCHECK(!g_auth_dialog_client_instance);
@@ -274,7 +279,7 @@
 
   // Take temporary ownership of user_context to pass on later.
   user_context_ = std::move(user_context);
-  pin_engine_.emplace(&auth_performer_);
+  pin_engine_.emplace(&local_state_.get(), &auth_performer_);
   legacy_fingerprint_engine_.emplace(&auth_performer_);
   std::move(callback).Run(true);
 }
diff --git a/chrome/browser/ui/ash/in_session_auth/in_session_auth_dialog_client.h b/chrome/browser/ui/ash/in_session_auth/in_session_auth_dialog_client.h
index 85322b4..a70f374e 100644
--- a/chrome/browser/ui/ash/in_session_auth/in_session_auth_dialog_client.h
+++ b/chrome/browser/ui/ash/in_session_auth/in_session_auth_dialog_client.h
@@ -12,6 +12,7 @@
 #include "ash/public/cpp/in_session_auth_dialog_client.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_forward.h"
+#include "base/memory/raw_ref.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
@@ -32,6 +33,7 @@
 }
 
 class AccountId;
+class PrefService;
 
 // Handles method calls sent from Ash to ChromeOS.
 class InSessionAuthDialogClient
@@ -43,7 +45,7 @@
   using FingerprintScanDoneCallback =
       base::OnceCallback<void(bool, ash::FingerprintState)>;
 
-  InSessionAuthDialogClient();
+  explicit InSessionAuthDialogClient(PrefService* local_state);
   InSessionAuthDialogClient(const InSessionAuthDialogClient&) = delete;
   InSessionAuthDialogClient& operator=(const InSessionAuthDialogClient&) =
       delete;
@@ -139,6 +141,8 @@
       bool is_pin_auth_available,
       std::unique_ptr<ash::UserContext> user_context);
 
+  const raw_ref<PrefService> local_state_;
+
   // State associated with a pending authentication attempt.
   std::optional<AuthState> pending_auth_state_;
 
diff --git a/chrome/browser/ui/ash/in_session_auth/in_session_auth_dialog_client_unittest.cc b/chrome/browser/ui/ash/in_session_auth/in_session_auth_dialog_client_unittest.cc
index ad24c20c..7d3a38b 100644
--- a/chrome/browser/ui/ash/in_session_auth/in_session_auth_dialog_client_unittest.cc
+++ b/chrome/browser/ui/ash/in_session_auth/in_session_auth_dialog_client_unittest.cc
@@ -13,6 +13,8 @@
 #include "base/test/bind.h"
 #include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
 #include "chrome/browser/ash/profiles/profile_helper.h"
+#include "chrome/test/base/scoped_testing_local_state.h"
+#include "chrome/test/base/testing_browser_process.h"
 #include "chromeos/ash/components/cryptohome/system_salt_getter.h"
 #include "chromeos/ash/components/dbus/userdataauth/fake_cryptohome_misc_client.h"
 #include "chromeos/ash/components/dbus/userdataauth/fake_userdataauth_client.h"
@@ -68,7 +70,8 @@
     ash::CryptohomeMiscClient::InitializeFake();
     ash::SystemSaltGetter::Initialize();
 
-    client_ = std::make_unique<InSessionAuthDialogClient>();
+    client_ = std::make_unique<InSessionAuthDialogClient>(
+        TestingBrowserProcess::GetGlobal()->local_state());
   }
 
   ~InSessionAuthDialogClientTest() override {
@@ -129,6 +132,9 @@
  protected:
   const content::BrowserTaskEnvironment task_environment_;
 
+  ScopedTestingLocalState scoped_testing_local_state_{
+      TestingBrowserProcess::GetGlobal()};
+
   ash::ScopedStubInstallAttributes install_attributes{
       ash::StubInstallAttributes::CreateConsumerOwned()};
   user_manager::TypedScopedUserManager<ash::FakeChromeUserManager>
diff --git a/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc
index 84a6c8ff..76d2c03 100644
--- a/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/ash/main_extra_parts/chrome_browser_main_extra_parts_ash.cc
@@ -262,8 +262,8 @@
       ash::input_method::InputMethodManager::Get());
   ime_controller_client_->Init();
 
-  in_session_auth_dialog_client_ =
-      std::make_unique<InSessionAuthDialogClient>();
+  in_session_auth_dialog_client_ = std::make_unique<InSessionAuthDialogClient>(
+      g_browser_process->local_state());
 
   in_session_auth_token_provider_ =
       std::make_unique<ash::InSessionAuthTokenProviderImpl>();
diff --git a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
index cba18b8..a8c2c69 100644
--- a/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
+++ b/chrome/browser/ui/ash/shelf/chrome_shelf_controller_browsertest.cc
@@ -1248,12 +1248,12 @@
   registration_waiter.AwaitRegistration();
 
   // Install PWA.
-  web_app::SetAutoAcceptPWAInstallConfirmationForTesting(true);
+  auto auto_accept_pwa_install_confirmation =
+      web_app::SetAutoAcceptPWAInstallConfirmationForTesting();
   web_app::WebAppTestInstallWithOsHooksObserver install_observer(profile());
   install_observer.BeginListening();
   chrome::ExecuteCommand(browser(), IDC_INSTALL_PWA);
   const webapps::AppId app_id = install_observer.Wait();
-  web_app::SetAutoAcceptPWAInstallConfirmationForTesting(false);
 
   // Find the native window for the app.
   gfx::NativeWindow native_window = gfx::NativeWindow();
@@ -2445,12 +2445,12 @@
   ASSERT_TRUE(AddTabAtIndex(1, url, ui::PAGE_TRANSITION_LINK));
   registration_waiter.AwaitRegistration();
   // Install PWA.
-  web_app::SetAutoAcceptPWAInstallConfirmationForTesting(true);
+  auto auto_accept_pwa_install_confirmation =
+      web_app::SetAutoAcceptPWAInstallConfirmationForTesting();
   web_app::WebAppTestInstallWithOsHooksObserver install_observer(profile());
   install_observer.BeginListening();
   chrome::ExecuteCommand(browser(), IDC_INSTALL_PWA);
   const webapps::AppId app_id = install_observer.Wait();
-  web_app::SetAutoAcceptPWAInstallConfirmationForTesting(false);
 
   ash::ShelfID shelf_id(app_id);
   EXPECT_FALSE(ChromeShelfController::instance()->IsPinned(shelf_id));
diff --git a/chrome/browser/ui/autofill/BUILD.gn b/chrome/browser/ui/autofill/BUILD.gn
index 2d8dedc..0b6dfb7d 100644
--- a/chrome/browser/ui/autofill/BUILD.gn
+++ b/chrome/browser/ui/autofill/BUILD.gn
@@ -135,6 +135,7 @@
     "//chrome/browser/ui/autofill/payments",
     "//chrome/browser/ui/hats",
     "//chrome/browser/ui/page_info",
+    "//chrome/browser/ui/plus_addresses",
     "//components/autofill/content/browser",
     "//components/autofill/content/browser:risk_proto",
     "//components/compose/core/browser",
diff --git a/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc b/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc
index 8eb7a34a0..bde58b88 100644
--- a/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc
+++ b/chrome/browser/ui/bookmarks/bookmark_utils_desktop.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/simple_message_box.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
 #include "chrome/grit/branded_strings.h"
@@ -36,6 +35,7 @@
 #include "components/bookmarks/browser/bookmark_node.h"
 #include "components/bookmarks/browser/bookmark_utils.h"
 #include "components/tab_groups/tab_group_id.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/page_navigator.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 30f168f3..2f0de74 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -1229,7 +1229,7 @@
           /*navigation_handle_callback=*/{});
 }
 
-const SessionID& Browser::GetSessionID() {
+const SessionID& Browser::GetSessionID() const {
   return session_id_;
 }
 
diff --git a/chrome/browser/ui/browser.h b/chrome/browser/ui/browser.h
index 9841afe..c28ef87 100644
--- a/chrome/browser/ui/browser.h
+++ b/chrome/browser/ui/browser.h
@@ -868,7 +868,7 @@
       const content::OpenURLParams& params,
       base::OnceCallback<void(content::NavigationHandle&)>
           navigation_handle_callback) override;
-  const SessionID& GetSessionID() override;
+  const SessionID& GetSessionID() const override;
   TabStripModel* GetTabStripModel() override;
   bool IsTabStripVisible() override;
   bool ShouldHideUIForFullscreen() const override;
diff --git a/chrome/browser/ui/browser_actions.cc b/chrome/browser/ui/browser_actions.cc
index 38faf66..98fe0ca 100644
--- a/chrome/browser/ui/browser_actions.cc
+++ b/chrome/browser/ui/browser_actions.cc
@@ -669,9 +669,12 @@
                    actions::ActionInvocationContext context) {
                   // TODO(crbug.com/356468503): Figure out how to capture
                   // action invocation location.
-                  browser->browser_window_features()
-                      ->cast_browser_controller()
-                      ->ToggleDialog();
+                  auto* cast_browser_controller =
+                      browser->browser_window_features()
+                          ->cast_browser_controller();
+                  if (cast_browser_controller) {
+                    cast_browser_controller->ToggleDialog();
+                  }
                 },
                 base::Unretained(browser)),
             kActionRouteMedia, IDS_MEDIA_ROUTER_MENU_ITEM_TITLE,
diff --git a/chrome/browser/ui/browser_commands.cc b/chrome/browser/ui/browser_commands.cc
index 3987c0ee..488381a2 100644
--- a/chrome/browser/ui/browser_commands.cc
+++ b/chrome/browser/ui/browser_commands.cc
@@ -96,7 +96,6 @@
 #include "chrome/browser/ui/tabs/organization/tab_organization_service_factory.h"
 #include "chrome/browser/ui/tabs/organization/tab_organization_session.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_enums.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
@@ -157,6 +156,8 @@
 #include "components/sessions/core/tab_restore_service.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "components/tabs/public/split_tab_visual_data.h"
+#include "components/tabs/public/tab_interface.h"
 #include "components/translate/core/browser/language_state.h"
 #include "components/translate/core/browser/translate_manager.h"
 #include "components/user_education/common/feature_promo/feature_promo_controller.h"
@@ -980,6 +981,30 @@
 
 void CloseTab(Browser* browser) {
   base::RecordAction(UserMetricsAction("CloseTab_Accelerator"));
+
+  if (!toast_features::IsEnabled(toast_features::kPinnedTabToastOnClose)) {
+    browser->tab_strip_model()->CloseSelectedTabs();
+    return;
+  }
+
+  ToastController* toast_controller = browser->GetFeatures().toast_controller();
+  if (!toast_controller) {
+    browser->tab_strip_model()->CloseSelectedTabs();
+    return;
+  }
+
+  tabs::TabInterface* tab = browser->tab_strip_model()->GetActiveTab();
+  bool single_tab_selected =
+      browser->tab_strip_model()->selection_model().size() == 1;
+  if (tab->IsPinned() && single_tab_selected) {
+    // Pinned tabs should show a toast asking the user to continue holding the
+    // command for some time before a pinned tab is closed. The toast will
+    // handle the call to TabStripModel::CloseSelectedTabs so we don't need to
+    // handle it here.
+    toast_controller->MaybeShowToast(ToastParams(ToastId::kClosePinnedTab));
+    return;
+  }
+
   browser->tab_strip_model()->CloseSelectedTabs();
 }
 
diff --git a/chrome/browser/ui/browser_tab_strip_model_delegate.cc b/chrome/browser/ui/browser_tab_strip_model_delegate.cc
index 63e325c..a19d5014 100644
--- a/chrome/browser/ui/browser_tab_strip_model_delegate.cc
+++ b/chrome/browser/ui/browser_tab_strip_model_delegate.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_keyed_service.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_service_factory.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_deletion_dialog_controller.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
@@ -46,6 +45,7 @@
 #include "components/sessions/core/session_id.h"
 #include "components/sessions/core/tab_restore_service.h"
 #include "components/tab_groups/tab_group_id.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_delegate.h"
diff --git a/chrome/browser/ui/browser_window/public/browser_window_interface.h b/chrome/browser/ui/browser_window/public/browser_window_interface.h
index 457cde1..8fda121 100644
--- a/chrome/browser/ui/browser_window/public/browser_window_interface.h
+++ b/chrome/browser/ui/browser_window/public/browser_window_interface.h
@@ -78,7 +78,7 @@
                         WindowOpenDisposition disposition) = 0;
 
   // Returns a session-unique ID.
-  virtual const SessionID& GetSessionID() = 0;
+  virtual const SessionID& GetSessionID() const = 0;
 
   virtual TabStripModel* GetTabStripModel() = 0;
 
diff --git a/chrome/browser/ui/browser_window/test/mock_browser_window_interface.h b/chrome/browser/ui/browser_window/test/mock_browser_window_interface.h
index ddb2088..1b52a92 100644
--- a/chrome/browser/ui/browser_window/test/mock_browser_window_interface.h
+++ b/chrome/browser/ui/browser_window/test/mock_browser_window_interface.h
@@ -19,7 +19,7 @@
               OpenGURL,
               (const GURL& gurl, WindowOpenDisposition disposition),
               (override));
-  MOCK_METHOD(const SessionID&, GetSessionID, (), (override));
+  MOCK_METHOD(const SessionID&, GetSessionID, (), (const override));
   MOCK_METHOD(TabStripModel*, GetTabStripModel, (), (override));
   MOCK_METHOD(bool, IsTabStripVisible, (), (override));
   MOCK_METHOD(bool, ShouldHideUIForFullscreen, (), (const, override));
diff --git a/chrome/browser/ui/lens/lens_preselection_bubble.cc b/chrome/browser/ui/lens/lens_preselection_bubble.cc
index d0c99135..e13ffcf 100644
--- a/chrome/browser/ui/lens/lens_preselection_bubble.cc
+++ b/chrome/browser/ui/lens/lens_preselection_bubble.cc
@@ -65,7 +65,7 @@
   set_close_on_deactivate(false);
   DialogDelegate::SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone));
   set_corner_radius(48);
-  set_background_color(kColorLensOverlayToastBackground);
+  SetBackgroundColor(kColorLensOverlayToastBackground);
   SetProperty(views::kElementIdentifierKey, kLensPreselectionBubbleElementId);
   SetAccessibleWindowRole(ax::mojom::Role::kAlertDialog);
   SetCancelCallback(std::move(on_cancel_callback));
diff --git a/chrome/browser/ui/plus_addresses/BUILD.gn b/chrome/browser/ui/plus_addresses/BUILD.gn
new file mode 100644
index 0000000..174e46a4
--- /dev/null
+++ b/chrome/browser/ui/plus_addresses/BUILD.gn
@@ -0,0 +1,143 @@
+# 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("//build/config/ui.gni")
+
+assert(toolkit_views || is_android)
+
+# TODO(crbug.com/413572035): Pull in android and views impl and test code.
+
+source_set("plus_addresses") {
+  sources = [
+    "plus_address_creation_controller.h",
+    "plus_address_creation_view.h",
+  ]
+
+  public_deps = [
+    "//components/plus_addresses:types",
+    "//content/public/browser",
+    "//ui/base",
+    "//url",
+  ]
+
+  if (toolkit_views) {
+    sources += [
+      "plus_address_creation_controller_desktop.h",
+      "plus_address_error_dialog.h",
+      "plus_address_menu_model.h",
+    ]
+
+    public_deps += [
+      "//base",
+      "//components/autofill/core/browser",
+      "//components/autofill/core/common",
+      "//components/plus_addresses/metrics",
+      "//components/plus_addresses/settings",
+      "//ui/menus",
+    ]
+
+    deps = [
+      "//components/constrained_window",
+      "//components/plus_addresses/resources/strings",
+      "//components/plus_addresses/resources/strings:strings_grit",
+    ]
+  }
+}
+
+source_set("impl") {
+  sources = []
+  public_deps = [
+    "//chrome/browser:browser_public_dependencies",
+    "//components/plus_addresses:types",
+    "//content/public/browser",
+    "//ui/base",
+    "//url",
+  ]
+
+  if (toolkit_views) {
+    sources += [
+      "plus_address_error_dialog.cc",
+      "plus_address_menu_model.cc",
+    ]
+
+    public_deps += [
+      "//base",
+      "//components/autofill/core/browser",
+      "//components/autofill/core/common",
+      "//components/plus_addresses/metrics",
+      "//components/plus_addresses/settings",
+      "//ui/menus",
+    ]
+
+    deps = [
+      ":plus_addresses",
+      "//components/constrained_window",
+      "//components/plus_addresses/resources/strings",
+      "//components/plus_addresses/resources/strings:strings_grit",
+    ]
+  }
+}
+
+source_set("interactive_ui_tests") {
+  testonly = true
+  sources = []
+
+  if (!is_android && !is_chromeos_device) {
+    sources += [
+      "plus_address_error_dialog_interactive_uitest.cc",
+      "views/plus_address_creation_dialog_interactive_uitest.cc",
+    ]
+    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+    deps = [
+      ":plus_addresses",
+      "//base",
+      "//base/test:test_support",
+      "//chrome/browser/plus_addresses",
+      "//chrome/test:test_support",
+      "//chrome/test:test_support_ui",
+      "//components/plus_addresses:test_support",
+      "//components/plus_addresses/resources/strings:strings_grit",
+      "//components/plus_addresses/settings:test_support",
+      "//components/signin/public/identity_manager:test_support",
+      "//components/sync:test_support",
+      "//content/test:test_support",
+      "//ui/views:test_support",
+    ]
+  }
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = []
+
+  if (!is_android) {
+    sources += [
+      "plus_address_creation_controller_desktop_unittest.cc",
+      "plus_address_menu_model_unittest.cc",
+    ]
+    deps = [
+      ":plus_addresses",
+      "//base",
+      "//base/test:test_support",
+      "//chrome/browser/plus_addresses",
+      "//chrome/browser/ui/hats",
+      "//chrome/test:test_support",
+      "//components/autofill/content/browser:test_support",
+      "//components/autofill/core/common",
+      "//components/plus_addresses",
+      "//components/plus_addresses:features",
+      "//components/plus_addresses:prefs",
+      "//components/plus_addresses:test_support",
+      "//components/plus_addresses:types",
+      "//components/plus_addresses/metrics",
+      "//components/plus_addresses/settings:test_support",
+      "//components/signin/public/identity_manager:test_support",
+      "//components/sync_preferences:test_support",
+      "//content/public/browser",
+      "//content/test:test_support",
+      "//testing/gmock",
+      "//testing/gtest",
+    ]
+  }
+}
diff --git a/chrome/browser/ui/tabs/BUILD.gn b/chrome/browser/ui/tabs/BUILD.gn
index 74e3e358..912c65f 100644
--- a/chrome/browser/ui/tabs/BUILD.gn
+++ b/chrome/browser/ui/tabs/BUILD.gn
@@ -255,11 +255,8 @@
       "pinned_tab_codec.h",
       "pinned_tab_service.h",
       "pinned_tab_service_factory.h",
-      "split_tab_collection.h",
-      "split_tab_data.h",
       "split_tab_menu_model.h",
       "split_tab_util.h",
-      "split_tab_visual_data.h",
       "tab_collection_node.h",
       "tab_renderer_data.h",
       "tab_style.h",
@@ -309,11 +306,8 @@
       "pinned_tab_service.cc",
       "pinned_tab_service_factory.cc",
       "recent_tabs_sub_menu_model.cc",
-      "split_tab_collection.cc",
-      "split_tab_data.cc",
       "split_tab_menu_model.cc",
       "split_tab_util.cc",
-      "split_tab_visual_data.cc",
       "tab_collection_node.cc",
       "tab_dialog_manager.cc",
       "tab_features.cc",
diff --git a/chrome/browser/ui/tabs/public/tab_dialog_manager.h b/chrome/browser/ui/tabs/public/tab_dialog_manager.h
index 5106dfc5..82bb4fe 100644
--- a/chrome/browser/ui/tabs/public/tab_dialog_manager.h
+++ b/chrome/browser/ui/tabs/public/tab_dialog_manager.h
@@ -51,12 +51,14 @@
   // TODO(kylixrd):
   //   (1) Call-sites expect to own the Widget using CLIENT_OWNS_WIDGET and be
   //       updated accordingly.
-  void ShowDialogAndBlockTabInteraction(views::Widget* dialog);
+  void ShowDialogAndBlockTabInteraction(views::Widget* dialog,
+                                        bool close_on_navigation = true);
   // Combines the above two functions into a single invocation. This is the most
   // commonly used version. Only use the other APIs if the caller must do
   // something unique to the Widget before showing it.
   std::unique_ptr<views::Widget> CreateShowDialogAndBlockTabInteraction(
-      views::DialogDelegate* delegate);
+      views::DialogDelegate* delegate,
+      bool close_on_navigation = true);
 
   void CloseDialog();
 
@@ -90,6 +92,8 @@
   std::unique_ptr<TabDialogWidgetObserver> tab_dialog_widget_observer_;
   std::unique_ptr<BrowserWindowWidgetObserver> browser_window_widget_observer_;
   std::unique_ptr<ScopedTabModalUI> showing_modal_ui_;
+
+  bool close_on_navigation_ = true;
 };
 
 }  // namespace tabs
diff --git a/chrome/browser/ui/tabs/split_tab_util.cc b/chrome/browser/ui/tabs/split_tab_util.cc
index 6d9ee14..5a1a683 100644
--- a/chrome/browser/ui/tabs/split_tab_util.cc
+++ b/chrome/browser/ui/tabs/split_tab_util.cc
@@ -9,8 +9,8 @@
 #include "base/check_op.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
 #include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
-#include "chrome/browser/ui/tabs/split_tab_data.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "components/tabs/public/split_tab_data.h"
 #include "components/tabs/public/split_tab_id.h"
 
 namespace split_tabs {
diff --git a/chrome/browser/ui/tabs/tab_collection_unittest.cc b/chrome/browser/ui/tabs/tab_collection_unittest.cc
index 4704755..4d8fe0662 100644
--- a/chrome/browser/ui/tabs/tab_collection_unittest.cc
+++ b/chrome/browser/ui/tabs/tab_collection_unittest.cc
@@ -9,9 +9,6 @@
 #include <optional>
 
 #include "chrome/browser/ui/tabs/features.h"
-#include "chrome/browser/ui/tabs/split_tab_collection.h"
-#include "chrome/browser/ui/tabs/split_tab_data.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_group_tab_collection.h"
 #include "chrome/browser/ui/tabs/tab_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_collection.h"
@@ -23,7 +20,10 @@
 #include "chrome/test/base/testing_profile.h"
 #include "components/tab_groups/tab_group_visual_data.h"
 #include "components/tabs/public/pinned_tab_collection.h"
+#include "components/tabs/public/split_tab_collection.h"
+#include "components/tabs/public/split_tab_data.h"
 #include "components/tabs/public/split_tab_id.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "components/tabs/public/tab_collection_storage.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_renderer_host.h"
diff --git a/chrome/browser/ui/tabs/tab_dialog_manager.cc b/chrome/browser/ui/tabs/tab_dialog_manager.cc
index 73ad497..9afa89f 100644
--- a/chrome/browser/ui/tabs/tab_dialog_manager.cc
+++ b/chrome/browser/ui/tabs/tab_dialog_manager.cc
@@ -184,19 +184,26 @@
       widget, host_browser_window));
 }
 
+// The dialog widget should be visible if and only if the tab is in the
+// foreground and activated, and the host window is not minimized.
+bool GetDialogWidgetVisibility(bool activated, bool minimized) {
+  return activated && !minimized;
+}
+
 }  // namespace
 
 // Applies positioning changes from the browser window widget to the tracked
-// Widget.
+// Widget. This class relies on the assumption that it is scoped to the lifetime
+// of a single tab, in a single browser, and that it will be destroyed
+// before the tab moves between browser windows.
 class BrowserWindowWidgetObserver : public views::WidgetObserver {
  public:
-  BrowserWindowWidgetObserver(BrowserWindowInterface* host_browser_window,
+  BrowserWindowWidgetObserver(TabInterface* tab_interface,
                               views::Widget* dialog_widget)
-      : host_(host_browser_window), dialog_widget_(dialog_widget) {
-    CHECK(host_);
+      : tab_(tab_interface), dialog_widget_(dialog_widget) {
     CHECK(dialog_widget_);
     browser_window_widget_observation_.Observe(
-        host_browser_window->TopContainer()->GetWidget());
+        tab_->GetBrowserWindowInterface()->TopContainer()->GetWidget());
   }
   BrowserWindowWidgetObserver(const BrowserWindowWidgetObserver&) = delete;
   BrowserWindowWidgetObserver& operator=(const BrowserWindowWidgetObserver&) =
@@ -206,17 +213,22 @@
   // WidgetObserver:
   void OnWidgetBoundsChanged(views::Widget* widget,
                              const gfx::Rect& new_bounds) override {
-    CHECK(host_);
     if (dialog_widget_->IsVisible()) {
       UpdateModalDialogPosition(
-          dialog_widget_, host_,
+          dialog_widget_, tab_->GetBrowserWindowInterface(),
           dialog_widget_->GetRootView()->GetPreferredSize({}));
     }
   }
 
+  void OnWidgetShowStateChanged(views::Widget* widget) override {
+    bool minimized = widget->IsMinimized();
+    bool activated = tab_->IsActivated();
+    dialog_widget_->SetVisible(GetDialogWidgetVisibility(activated, minimized));
+  }
+
  private:
-  // The modal host for the widget that owns this observer.
-  raw_ptr<BrowserWindowInterface> host_;
+  // The tab that owns this dialog manager.
+  raw_ptr<TabInterface> tab_;
 
   // The widget being tracked.
   raw_ptr<views::Widget> dialog_widget_;
@@ -251,7 +263,10 @@
       delegate, gfx::NativeWindow(), host->GetNativeView()));
 }
 
-void TabDialogManager::ShowDialogAndBlockTabInteraction(views::Widget* widget) {
+void TabDialogManager::ShowDialogAndBlockTabInteraction(
+    views::Widget* widget,
+    bool close_on_navigation) {
+  close_on_navigation_ = close_on_navigation;
   widget_ = widget;
   auto* browser_window_interface = tab_interface_->GetBrowserWindowInterface();
   ConfigureDesiredBoundsDelegate(widget_.get(), browser_window_interface);
@@ -268,19 +283,21 @@
   tab_dialog_widget_observer_ =
       std::make_unique<TabDialogWidgetObserver>(this, widget_.get());
   showing_modal_ui_ = tab_interface_->ShowModalUI();
-  if (tab_interface_->IsActivated()) {
-    browser_window_widget_observer_ =
-        std::make_unique<BrowserWindowWidgetObserver>(browser_window_interface,
-                                                      widget_.get());
-    widget_->Show();
-  }
+  browser_window_widget_observer_ =
+      std::make_unique<BrowserWindowWidgetObserver>(tab_interface_,
+                                                    widget_.get());
+  bool minimized = browser_window_interface->IsMinimized();
+  bool activated = tab_interface_->IsActivated();
+  widget_->Show();
+  widget_->SetVisible(GetDialogWidgetVisibility(activated, minimized));
 }
 
 std::unique_ptr<views::Widget>
 TabDialogManager::CreateShowDialogAndBlockTabInteraction(
-    views::DialogDelegate* delegate) {
+    views::DialogDelegate* delegate,
+    bool close_on_navigation) {
   auto widget = CreateTabScopedDialog(delegate);
-  ShowDialogAndBlockTabInteraction(widget.get());
+  ShowDialogAndBlockTabInteraction(widget.get(), close_on_navigation);
   return widget;
 }
 
@@ -328,8 +345,9 @@
             back_forward_cache::DisabledReasonId::kModalDialog));
   }
 
-  // Close modal dialogs if necessary.
-  if (!net::registry_controlled_domains::SameDomainOrHost(
+  // Close modal dialogs if navigation is to a new domain/host.
+  if (close_on_navigation_ &&
+      !net::registry_controlled_domains::SameDomainOrHost(
           navigation_handle->GetPreviousPrimaryMainFrameURL(),
           navigation_handle->GetURL(),
           net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
@@ -343,8 +361,8 @@
                               tab_interface_->GetBrowserWindowInterface(),
                               widget_->GetRootView()->GetPreferredSize({}));
     browser_window_widget_observer_ =
-        std::make_unique<BrowserWindowWidgetObserver>(
-            tab_interface_->GetBrowserWindowInterface(), widget_.get());
+        std::make_unique<BrowserWindowWidgetObserver>(tab_interface_,
+                                                      widget_.get());
     // Check if the tab was detached and dragged to a new browser window. This
     // ensures the widget is properly reparented.
     auto* parent_widget =
@@ -352,7 +370,8 @@
     if (parent_widget != widget_->parent()) {
       widget_->Reparent(parent_widget);
     }
-    widget_->SetVisible(true);
+    widget_->SetVisible(
+        GetDialogWidgetVisibility(/*activated=*/true, widget_->IsMinimized()));
   }
 }
 
diff --git a/chrome/browser/ui/tabs/tab_iterator_unittest.cc b/chrome/browser/ui/tabs/tab_iterator_unittest.cc
index fea7fb2..cee8d577 100644
--- a/chrome/browser/ui/tabs/tab_iterator_unittest.cc
+++ b/chrome/browser/ui/tabs/tab_iterator_unittest.cc
@@ -5,8 +5,6 @@
 #include <memory>
 #include <utility>
 
-#include "chrome/browser/ui/tabs/split_tab_collection.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_group_tab_collection.h"
 #include "chrome/browser/ui/tabs/tab_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -14,6 +12,8 @@
 #include "chrome/browser/ui/tabs/test_util.h"
 #include "chrome/browser/ui/tabs/unpinned_tab_collection.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/tabs/public/split_tab_collection.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "components/tabs/public/tab_collection.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_task_environment.h"
diff --git a/chrome/browser/ui/tabs/tab_model.cc b/chrome/browser/ui/tabs/tab_model.cc
index cc096e3f..f1b2cd5 100644
--- a/chrome/browser/ui/tabs/tab_model.cc
+++ b/chrome/browser/ui/tabs/tab_model.cc
@@ -15,13 +15,13 @@
 #include "chrome/browser/ui/tabs/features.h"
 #include "chrome/browser/ui/tabs/public/tab_dialog_manager.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
-#include "chrome/browser/ui/tabs/split_tab_collection.h"
 #include "chrome/browser/ui/tabs/tab_enums.h"
 #include "chrome/browser/ui/tabs/tab_group_tab_collection.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "components/constrained_window/constrained_window_views.h"
+#include "components/tabs/public/split_tab_collection.h"
 #include "components/tabs/public/split_tab_id.h"
 #include "components/tabs/public/tab_collection.h"
 #include "components/web_modal/modal_dialog_host.h"
@@ -265,6 +265,10 @@
   return GetModelForTabInterface()->delegate()->GetBrowserWindowInterface();
 }
 
+const BrowserWindowInterface* TabModel::GetBrowserWindowInterface() const {
+  return GetModelForTabInterface()->delegate()->GetBrowserWindowInterface();
+}
+
 tabs::TabFeatures* TabModel::GetTabFeatures() {
   return tab_features_.get();
 }
diff --git a/chrome/browser/ui/tabs/tab_model.h b/chrome/browser/ui/tabs/tab_model.h
index 35ed161..7f3881b 100644
--- a/chrome/browser/ui/tabs/tab_model.h
+++ b/chrome/browser/ui/tabs/tab_model.h
@@ -138,6 +138,7 @@
 
   bool IsInNormalWindow() const override;
   BrowserWindowInterface* GetBrowserWindowInterface() override;
+  const BrowserWindowInterface* GetBrowserWindowInterface() const override;
   tabs::TabFeatures* GetTabFeatures() override;
   bool IsPinned() const override;
   bool IsSplit() const override;
diff --git a/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn b/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn
index b346f7d..42f4fe9 100644
--- a/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn
+++ b/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn
@@ -25,16 +25,28 @@
           mojom = "tabs_api.mojom.TabId.Type"
           cpp = "enum ::tabs_api::TabId::Type"
         },
+        {
+          mojom = "tabs_api.mojom.TabAlertState"
+          cpp = "::TabAlertState"
+        },
+        {
+          mojom = "tabs_api.mojom.TabNetworkState"
+          cpp = "::TabNetworkState"
+        },
       ]
       traits_headers = [
         "//chrome/browser/ui/tabs/tab_strip_api/tab_id.h",
         "//chrome/browser/ui/tabs/tab_strip_api/tab_id_traits.h",
+        "//chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.h",
       ]
-      traits_sources =
-          [ "//chrome/browser/ui/tabs/tab_strip_api/tab_id_traits.cc" ]
+      traits_sources = [
+        "//chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.cc",
+        "//chrome/browser/ui/tabs/tab_strip_api/tab_id_traits.cc",
+      ]
       traits_public_deps = [
         ":types",
         "//base",
+        "//chrome/browser/ui/tabs",
       ]
     },
   ]
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.cc b/chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.cc
new file mode 100644
index 0000000..7f91e41
--- /dev/null
+++ b/chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.cc
@@ -0,0 +1,136 @@
+// 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 "chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.h"
+
+MojoTabNetworkState
+mojo::EnumTraits<MojoTabNetworkState, NativeTabNetworkState>::ToMojom(
+    NativeTabNetworkState input) {
+  switch (input) {
+    case NativeTabNetworkState::kNone:
+      return MojoTabNetworkState::kNone;
+    case NativeTabNetworkState::kWaiting:
+      return MojoTabNetworkState::kWaiting;
+    case NativeTabNetworkState::kLoading:
+      return MojoTabNetworkState::kLoading;
+    case NativeTabNetworkState::kError:
+      return MojoTabNetworkState::kError;
+  }
+
+  NOTREACHED();
+}
+
+bool mojo::EnumTraits<MojoTabNetworkState, NativeTabNetworkState>::FromMojom(
+    MojoTabNetworkState in,
+    NativeTabNetworkState* out) {
+  switch (in) {
+    case MojoTabNetworkState::kNone:
+      *out = NativeTabNetworkState::kNone;
+      return true;
+    case MojoTabNetworkState::kWaiting:
+      *out = NativeTabNetworkState::kWaiting;
+      return true;
+    case MojoTabNetworkState::kLoading:
+      *out = NativeTabNetworkState::kLoading;
+      return true;
+    case MojoTabNetworkState::kError:
+      *out = NativeTabNetworkState::kError;
+      return true;
+  }
+
+  NOTREACHED();
+}
+
+MojoTabAlertState
+mojo::EnumTraits<MojoTabAlertState, NativeTabAlertState>::ToMojom(
+    NativeTabAlertState input) {
+  switch (input) {
+    case NativeTabAlertState::MEDIA_RECORDING:
+      return MojoTabAlertState::kMediaRecording;
+    case NativeTabAlertState::TAB_CAPTURING:
+      return MojoTabAlertState::kTabCapturing;
+    case NativeTabAlertState::AUDIO_PLAYING:
+      return MojoTabAlertState::kAudioPlaying;
+    case NativeTabAlertState::AUDIO_MUTING:
+      return MojoTabAlertState::kAudioMuting;
+    case NativeTabAlertState::BLUETOOTH_CONNECTED:
+      return MojoTabAlertState::kBluetoothConnected;
+    case NativeTabAlertState::BLUETOOTH_SCAN_ACTIVE:
+      return MojoTabAlertState::kBluetoothScanActive;
+    case NativeTabAlertState::USB_CONNECTED:
+      return MojoTabAlertState::kUsbConnected;
+    case NativeTabAlertState::HID_CONNECTED:
+      return MojoTabAlertState::kHidConnected;
+    case NativeTabAlertState::SERIAL_CONNECTED:
+      return MojoTabAlertState::kSerialConnected;
+    case NativeTabAlertState::PIP_PLAYING:
+      return MojoTabAlertState::kPipPlaying;
+    case NativeTabAlertState::DESKTOP_CAPTURING:
+      return MojoTabAlertState::kDesktopCapturing;
+    case NativeTabAlertState::VR_PRESENTING_IN_HEADSET:
+      return MojoTabAlertState::kVrPresentingInHeadset;
+    case NativeTabAlertState::AUDIO_RECORDING:
+      return MojoTabAlertState::kAudioRecording;
+    case NativeTabAlertState::VIDEO_RECORDING:
+      return MojoTabAlertState::kVideoRecording;
+    case NativeTabAlertState::GLIC_ACCESSING:
+      return MojoTabAlertState::kGlicAccessing;
+  }
+
+  NOTREACHED();
+}
+
+bool mojo::EnumTraits<MojoTabAlertState, NativeTabAlertState>::FromMojom(
+    MojoTabAlertState in,
+    NativeTabAlertState* out) {
+  switch (in) {
+    case MojoTabAlertState::kMediaRecording:
+      *out = NativeTabAlertState::MEDIA_RECORDING;
+      return true;
+    case MojoTabAlertState::kTabCapturing:
+      *out = NativeTabAlertState::TAB_CAPTURING;
+      return true;
+    case MojoTabAlertState::kAudioPlaying:
+      *out = NativeTabAlertState::AUDIO_PLAYING;
+      return true;
+    case MojoTabAlertState::kAudioMuting:
+      *out = NativeTabAlertState::AUDIO_MUTING;
+      return true;
+    case MojoTabAlertState::kBluetoothConnected:
+      *out = NativeTabAlertState::BLUETOOTH_CONNECTED;
+      return true;
+    case MojoTabAlertState::kBluetoothScanActive:
+      *out = NativeTabAlertState::BLUETOOTH_SCAN_ACTIVE;
+      return true;
+    case MojoTabAlertState::kUsbConnected:
+      *out = NativeTabAlertState::USB_CONNECTED;
+      return true;
+    case MojoTabAlertState::kHidConnected:
+      *out = NativeTabAlertState::HID_CONNECTED;
+      return true;
+    case MojoTabAlertState::kSerialConnected:
+      *out = NativeTabAlertState::SERIAL_CONNECTED;
+      return true;
+    case MojoTabAlertState::kPipPlaying:
+      *out = NativeTabAlertState::PIP_PLAYING;
+      return true;
+    case MojoTabAlertState::kDesktopCapturing:
+      *out = NativeTabAlertState::DESKTOP_CAPTURING;
+      return true;
+    case MojoTabAlertState::kVrPresentingInHeadset:
+      *out = NativeTabAlertState::VR_PRESENTING_IN_HEADSET;
+      return true;
+    case MojoTabAlertState::kAudioRecording:
+      *out = NativeTabAlertState::AUDIO_RECORDING;
+      return true;
+    case MojoTabAlertState::kVideoRecording:
+      *out = NativeTabAlertState::VIDEO_RECORDING;
+      return true;
+    case MojoTabAlertState::kGlicAccessing:
+      *out = NativeTabAlertState::GLIC_ACCESSING;
+      return true;
+  }
+
+  NOTREACHED();
+}
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.h b/chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.h
new file mode 100644
index 0000000..d2b547d
--- /dev/null
+++ b/chrome/browser/ui/tabs/tab_strip_api/tab_enum_traits.h
@@ -0,0 +1,34 @@
+// 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 CHROME_BROWSER_UI_TABS_TAB_STRIP_API_TAB_ENUM_TRAITS_H_
+#define CHROME_BROWSER_UI_TABS_TAB_STRIP_API_TAB_ENUM_TRAITS_H_
+
+#include "chrome/browser/ui/tabs/tab_enums.h"
+#include "chrome/browser/ui/tabs/tab_network_state.h"
+#include "chrome/browser/ui/tabs/tab_strip_api/tab_strip_api.mojom.h"
+#include "mojo/public/cpp/bindings/enum_traits.h"
+#include "mojo/public/cpp/bindings/struct_traits.h"
+
+using MojoTabNetworkState = tabs_api::mojom::TabNetworkState;
+using NativeTabNetworkState = enum TabNetworkState;
+
+// TabNetworkState Enum mapping.
+template <>
+struct mojo::EnumTraits<MojoTabNetworkState, NativeTabNetworkState> {
+  static MojoTabNetworkState ToMojom(NativeTabNetworkState input);
+  static bool FromMojom(MojoTabNetworkState in, NativeTabNetworkState* out);
+};
+
+using MojoTabAlertState = tabs_api::mojom::TabAlertState;
+using NativeTabAlertState = enum TabAlertState;
+
+// TabAlertState Enum mapping.
+template <>
+struct mojo::EnumTraits<MojoTabAlertState, NativeTabAlertState> {
+  static MojoTabAlertState ToMojom(NativeTabAlertState input);
+  static bool FromMojom(MojoTabAlertState in, NativeTabAlertState* out);
+};
+
+#endif  // CHROME_BROWSER_UI_TABS_TAB_STRIP_API_TAB_ENUM_TRAITS_H_
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_api.mojom b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_api.mojom
index ceaaba5d4..4a3db38 100644
--- a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_api.mojom
+++ b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_api.mojom
@@ -7,6 +7,35 @@
 import "mojo/public/mojom/base/error.mojom";
 import "url/mojom/url.mojom";
 
+// In C++, this enum is typemapped to
+// "//chrome/browser/ui/tabs/tab_enums.h"
+enum TabAlertState {
+  kMediaRecording,
+  kTabCapturing,
+  kAudioPlaying,
+  kAudioMuting,
+  kBluetoothConnected,
+  kBluetoothScanActive,
+  kUsbConnected,
+  kHidConnected,
+  kSerialConnected,
+  kPipPlaying,
+  kDesktopCapturing,
+  kVrPresentingInHeadset,
+  kAudioRecording,
+  kVideoRecording,
+  kGlicAccessing,
+};
+
+// In C++, this enum is typemapped to
+// "//chrome/browser/ui/tabs/tab_network_state.h"
+enum TabNetworkState {
+  kNone,
+  kWaiting,
+  kLoading,
+  kError,
+};
+
 // References a tab object. The id can refer to different types of underlying
 // resource. The |type| field can be used to differentiate between the type
 // of underlying resources.
@@ -31,6 +60,19 @@
   Type type;
 };
 
+struct Tab {
+  TabId id;
+  string title;
+  url.mojom.Url url;
+
+  // TODO(crbug.com/414630734). The favicon should be typemapped to ImageModel
+  // in c++. Leave this as a data uri for now.
+  url.mojom.Url favicon_url;
+
+  array<TabAlertState> alert_states;
+  TabNetworkState network_state;
+};
+
 // Position is an ephemeral object that should not be saved nor act as an
 // identifier. It is purely used in this API to determine the position within
 // the TabstripModel.
@@ -49,13 +91,14 @@
   // empty, the new tab will be appended to the end of the Tabstrip.
   // Url specifies what is loaded in the Tab. If url is empty, then the new
   // tab-page is loaded instead.
-  //
-  // TODO (crbug.com/409821664). Returns a result type of bool for success or
-  // Error for errors. The success type is temporary and will be changed once
-  // it is defined.
   [Sync]
   CreateTabAt(Position? pos, url.mojom.Url? url)
       => result<bool, mojo_base.mojom.Error>;
+
+  // Get tab data based on the Tab id. Currently the TabId refers to the Tab
+  // handle value.
+  [Sync]
+  GetTab(TabId id) => result<Tab, mojo_base.mojom.Error>;
 };
 
 // TODO (crbug.com/412955607)
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl.cc b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl.cc
index d83db22..6479652 100644
--- a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl.cc
+++ b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl.cc
@@ -3,11 +3,15 @@
 // found in the LICENSE file.
 #include "chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl.h"
 
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
 #include "base/types/expected.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/tabs/tab_renderer_data.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
+#include "chrome/browser/ui/tabs/tab_utils.h"
 #include "mojo/public/mojom/base/error.mojom.h"
 #include "url/gurl.h"
 
@@ -64,6 +68,48 @@
                                    index, true);
 }
 
+tabs_api::mojom::TabPtr TabStripServiceImpl::ConvertTabToData(
+    tabs::TabInterface* tab_interface,
+    int index) {
+  auto result = tabs_api::mojom::Tab::New();
+
+  auto tab_renderer_data = TabRendererData::FromTabInModel(model_, index);
+  result->title = base::UTF16ToUTF8(tab_renderer_data.title);
+  // TODO(crbug.com/414630734). Integrate the favicon_url after it is
+  // typemapped.
+  result->url = tab_renderer_data.visible_url;
+  result->network_state = tab_renderer_data.network_state;
+  for (const auto alert_state :
+       GetTabAlertStatesForContents(tab_interface->GetContents())) {
+    result->alert_states.push_back(alert_state);
+  }
+
+  return result;
+}
+
+void TabStripServiceImpl::GetTab(const tabs_api::TabId& tab_mojom_id,
+                                 GetTabCallback callback) {
+  int32_t tab_id;
+  tabs_api::mojom::TabPtr tab_result;
+  if (base::StringToInt(tab_mojom_id.Id(), &tab_id)) {
+    // TODO (crbug.com/412709270) TabStripModel or TabCollections should have an
+    // api that can fetch id without of relying on indexes.
+    for (int index = 0; index < model_->count(); index++) {
+      tabs::TabInterface* tab = model_->GetTabAtIndex(index);
+      if (tab_id == tab->GetHandle().raw_value()) {
+        tab_result = ConvertTabToData(tab, index);
+      }
+    }
+  }
+
+  if (tab_result) {
+    std::move(callback).Run(std::move(tab_result));
+  } else {
+    std::move(callback).Run(base::unexpected(mojo_base::mojom::Error::New(
+        mojo_base::mojom::Code::kNotFound, "Tab not found")));
+  }
+}
+
 void TabStripServiceImpl::OnTabStripModelChanged(
     TabStripModel* tab_strip_model,
     const TabStripModelChange& change,
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl.h b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl.h
index 1487da99..508857c8 100644
--- a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl.h
+++ b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl.h
@@ -8,6 +8,7 @@
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
 #include "base/observer_list.h"
+#include "chrome/browser/ui/tabs/tab_strip_api/tab_id.h"
 #include "chrome/browser/ui/tabs/tab_strip_api/tab_strip_api.mojom.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
@@ -37,6 +38,8 @@
                    const std::optional<GURL>& url,
                    CreateTabAtCallback callback) override;
 
+  void GetTab(const tabs_api::TabId& id, GetTabCallback callback) override;
+
   // TabStripModelObserver
   void OnTabStripModelChanged(
       TabStripModel* tab_strip_model,
@@ -47,6 +50,8 @@
   // Helper method used to add tab. This is primarily to mock for unit tests
   // until there is a better way to mock chrome::AddAndReturnTabAt.
   virtual content::WebContents* AddTabAt(const GURL& url, int index);
+  tabs_api::mojom::TabPtr ConvertTabToData(tabs::TabInterface* tab_interface,
+                                           int index);
 
  private:
   void OnTabStripModelChangeAdded(const TabStripModelChange::Insert& change);
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl_unittest.cc b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl_unittest.cc
index 9428acb..3259762 100644
--- a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl_unittest.cc
+++ b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl_unittest.cc
@@ -86,4 +86,14 @@
   ASSERT_EQ(result.error()->code, mojo_base::mojom::Code::kFailedPrecondition);
 }
 
+TEST_F(TabStripServiceImplTest, GetTab) {
+  tabs_api::mojom::TabStripService::GetTabResult result;
+  tabs_api::TabId tab_id;
+  bool success = client_->GetTab(tab_id, &result);
+
+  ASSERT_TRUE(success);
+  ASSERT_FALSE(result.has_value());
+  ASSERT_EQ(result.error()->code, mojo_base::mojom::Code::kNotFound);
+}
+
 }  // namespace
diff --git a/chrome/browser/ui/tabs/tab_strip_collection.cc b/chrome/browser/ui/tabs/tab_strip_collection.cc
index bad3e448..8e5ead0 100644
--- a/chrome/browser/ui/tabs/tab_strip_collection.cc
+++ b/chrome/browser/ui/tabs/tab_strip_collection.cc
@@ -11,12 +11,12 @@
 
 #include "base/containers/adapters.h"
 #include "base/memory/ptr_util.h"
-#include "chrome/browser/ui/tabs/split_tab_collection.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_group_tab_collection.h"
 #include "chrome/browser/ui/tabs/unpinned_tab_collection.h"
 #include "components/tabs/public/pinned_tab_collection.h"
+#include "components/tabs/public/split_tab_collection.h"
 #include "components/tabs/public/split_tab_id.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "components/tabs/public/tab_collection.h"
 #include "components/tabs/public/tab_collection_storage.h"
 #include "components/tabs/public/tab_interface.h"
diff --git a/chrome/browser/ui/tabs/tab_strip_collection.h b/chrome/browser/ui/tabs/tab_strip_collection.h
index 5cecf97..5a3d3a52 100644
--- a/chrome/browser/ui/tabs/tab_strip_collection.h
+++ b/chrome/browser/ui/tabs/tab_strip_collection.h
@@ -9,8 +9,8 @@
 #include <optional>
 #include <unordered_map>
 
-#include "chrome/browser/ui/tabs/split_tab_data.h"
 #include "components/tab_groups/tab_group_id.h"
+#include "components/tabs/public/split_tab_data.h"
 #include "components/tabs/public/split_tab_id.h"
 #include "components/tabs/public/tab_collection.h"
 
diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc
index 5b3911c..54a0471c 100644
--- a/chrome/browser/ui/tabs/tab_strip_model.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model.cc
@@ -61,9 +61,6 @@
 #include "chrome/browser/ui/tabs/organization/tab_organization_service.h"
 #include "chrome/browser/ui/tabs/organization/tab_organization_service_factory.h"
 #include "chrome/browser/ui/tabs/organization/tab_organization_session.h"
-#include "chrome/browser/ui/tabs/split_tab_collection.h"
-#include "chrome/browser/ui/tabs/split_tab_data.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_change_type.h"
 #include "chrome/browser/ui/tabs/tab_enums.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
@@ -93,7 +90,10 @@
 #include "components/reading_list/core/reading_list_model.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "components/tabs/public/split_tab_collection.h"
+#include "components/tabs/public/split_tab_data.h"
 #include "components/tabs/public/split_tab_id.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "components/tabs/public/tab_interface.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "components/webapps/common/web_app_id.h"
@@ -421,7 +421,7 @@
   CHECK(!group_model_->ContainsTabGroup(group->collection_->GetTabGroupId()));
 
   // Notify tab is added to model.
-  for (tabs::TabInterface* tab : group->collection_->GetTabsRecursive()) {
+  for (tabs::TabInterface* tab : *(group->collection_)) {
     static_cast<tabs::TabModel*>(tab)->OnAddedToModel(this);
   }
 
@@ -525,7 +525,7 @@
   group_model_->RemoveTabGroup(group_id, base::PassKey<TabStripModel>());
 
   // Notify tab is removed from model
-  for (tabs::TabInterface* tab : group_collection->GetTabsRecursive()) {
+  for (tabs::TabInterface* tab : *group_collection) {
     static_cast<tabs::TabModel*>(tab)->OnRemovedFromModel();
   }
 
@@ -564,7 +564,7 @@
       detached_group->collection_->GetTabGroupId();
   tabs::TabGroupTabCollection* group_collection =
       detached_group->collection_.get();
-  for (tabs::TabInterface* tab : group_collection->GetTabsRecursive()) {
+  for (const tabs::TabInterface* tab : *group_collection) {
     delegate()->WillAddWebContents(tab->GetContents());
   }
 
@@ -593,7 +593,7 @@
 
   ValidateTabStripModel();
 
-  for (tabs::TabInterface* tab : group_collection->GetTabsRecursive()) {
+  for (tabs::TabInterface* tab : *group_collection) {
     static_cast<tabs::TabModel*>(tab)->DidInsert(
         base::PassKey<TabStripModel>());
   }
@@ -898,11 +898,12 @@
 }
 
 int TabStripModel::GetIndexOfWebContents(const WebContents* contents) const {
-  std::vector<tabs::TabInterface*> tabs = contents_data_->GetTabsRecursive();
-  for (size_t i = 0; i < tabs.size(); i++) {
-    if (tabs[i]->GetContents() == contents) {
-      return i;
+  int index = 0;
+  for (const tabs::TabInterface* tab : *this) {
+    if (tab->GetContents() == contents) {
+      return index;
     }
+    index++;
   }
   return kNoTab;
 }
@@ -984,7 +985,7 @@
 }
 
 bool TabStripModel::TabsNeedLoadingUI() const {
-  for (tabs::TabInterface* tab : contents_data_->GetTabsRecursive()) {
+  for (const tabs::TabInterface* tab : *this) {
     if (tab->GetContents()->ShouldShowLoadingUI()) {
       return true;
     }
@@ -2674,7 +2675,7 @@
 }
 
 void TabStripModel::ForgetAllOpeners() {
-  for (tabs::TabInterface* tab : contents_data_->GetTabsRecursive()) {
+  for (tabs::TabInterface* tab : *this) {
     static_cast<tabs::TabModel*>(tab)->set_opener(nullptr);
   }
 }
@@ -3322,7 +3323,7 @@
   }
 
   DCHECK([&]() {
-    for (tabs::TabInterface* tab : contents_data_->GetTabsRecursive()) {
+    for (const tabs::TabInterface* tab : *this) {
       if (tab->GetGroup() == new_group) {
         return false;
       }
@@ -4113,9 +4114,8 @@
 void TabStripModel::FixOpeners(int index) {
   tabs::TabModel* old_tab = GetTabModelAtIndex(index);
   tabs::TabInterface* new_opener = old_tab ? old_tab->opener() : nullptr;
-  std::vector<tabs::TabInterface*> tabs = contents_data_->GetTabsRecursive();
 
-  for (tabs::TabInterface* tab : tabs) {
+  for (tabs::TabInterface* tab : *this) {
     auto* tab_model = static_cast<tabs::TabModel*>(tab);
     if (tab_model->opener() != old_tab) {
       continue;
@@ -4128,7 +4128,7 @@
   // Sanity check that none of the tabs' openers refer |old_tab| or
   // themselves.
   DCHECK([&]() {
-    return std::none_of(tabs.begin(), tabs.end(), [&](tabs::TabInterface* tab) {
+    return std::none_of(begin(), end(), [&](tabs::TabInterface* tab) {
       tabs::TabInterface* opener = static_cast<tabs::TabModel*>(tab)->opener();
       return opener == old_tab || opener == tab;
     });
@@ -4465,9 +4465,8 @@
   // All the tabs in a split should be contiguous. Instead of using
   // GetIndexOfTab multiple times, call it on the first tab, then increment by
   // one for each subsequent tab.
-  std::vector<tabs::TabInterface*> tabs = split->GetTabsRecursive();
-  for (size_t index = GetIndexOfTab(tabs[0]);
-       tabs::TabInterface* split_tab : tabs) {
+  for (size_t index = GetIndexOfTab(split->GetTabAtIndexRecursive(0));
+       tabs::TabInterface* split_tab : *split) {
     split_tabs_with_indices.emplace_back(split_tab, index++);
   }
 
@@ -4482,9 +4481,8 @@
     return gfx::Range();
   }
 
-  std::vector<tabs::TabInterface*> tabs = split->GetTabsRecursive();
-  size_t start = GetIndexOfTab(tabs[0]);
-  return gfx::Range(start, start + tabs.size());
+  size_t start = GetIndexOfTab(split->GetTabAtIndexRecursive(0));
+  return gfx::Range(start, start + split->TabCountRecursive());
 }
 
 TabStripModel::ScopedTabStripModalUIImpl::ScopedTabStripModalUIImpl(
diff --git a/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc b/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc
index e09fca2d..e4469b8 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model_browsertest.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/ui/tabs/organization/tab_organization_service.h"
 #include "chrome/browser/ui/tabs/organization/tab_organization_service_factory.h"
 #include "chrome/browser/ui/tabs/organization/tab_organization_session.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
@@ -35,6 +34,7 @@
 #include "components/policy/policy_constants.h"
 #include "components/saved_tab_groups/public/features.h"
 #include "components/tab_groups/tab_group_id.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "components/tabs/public/tab_interface.h"
 #include "components/webapps/common/web_app_id.h"
 #include "content/public/browser/web_contents.h"
diff --git a/chrome/browser/ui/tabs/tab_strip_model_observer.cc b/chrome/browser/ui/tabs/tab_strip_model_observer.cc
index 1a785f5..631865d 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_observer.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model_observer.cc
@@ -9,10 +9,10 @@
 
 #include "base/check_op.h"
 #include "base/trace_event/trace_event.h"
-#include "chrome/browser/ui/tabs/split_tab_collection.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_group_tab_collection.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "components/tabs/public/split_tab_collection.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "components/tabs/public/tab_interface.h"
 #include "content/public/browser/web_contents.h"
 #include "third_party/perfetto/include/perfetto/tracing/traced_value.h"
diff --git a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
index 9214c67..b2e88e1 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
@@ -32,8 +32,6 @@
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h"
 #include "chrome/browser/ui/tabs/features.h"
-#include "chrome/browser/ui/tabs/split_tab_data.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_enums.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_model.h"
@@ -51,6 +49,8 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/tab_groups/tab_group_color.h"
 #include "components/tab_groups/tab_group_id.h"
+#include "components/tabs/public/split_tab_data.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "components/tabs/public/tab_interface.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
diff --git a/chrome/browser/ui/tabs/test/mock_tab_interface.h b/chrome/browser/ui/tabs/test/mock_tab_interface.h
index d3002206..44590d1 100644
--- a/chrome/browser/ui/tabs/test/mock_tab_interface.h
+++ b/chrome/browser/ui/tabs/test/mock_tab_interface.h
@@ -70,6 +70,10 @@
               GetBrowserWindowInterface,
               (),
               (override));
+  MOCK_METHOD(const BrowserWindowInterface*,
+              GetBrowserWindowInterface,
+              (),
+              (const override));
   MOCK_METHOD(TabFeatures*, GetTabFeatures, (), (override));
   MOCK_METHOD(bool, IsPinned, (), (const override));
   MOCK_METHOD(bool, IsSplit, (), (const override));
diff --git a/chrome/browser/ui/tabs/test/split_tabs_interactive_test_mixin.h b/chrome/browser/ui/tabs/test/split_tabs_interactive_test_mixin.h
index 400ea52..9bcec0e 100644
--- a/chrome/browser/ui/tabs/test/split_tabs_interactive_test_mixin.h
+++ b/chrome/browser/ui/tabs/test/split_tabs_interactive_test_mixin.h
@@ -16,6 +16,7 @@
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/interaction/interaction_test_util_browser.h"
 #include "chrome/test/interaction/interactive_browser_test.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "ui/base/interaction/interactive_test.h"
 
 // Template to be used as a mixin class for split tabs tests extending
diff --git a/chrome/browser/ui/toasts/api/toast_id.cc b/chrome/browser/ui/toasts/api/toast_id.cc
index 086f6f8..efecbd6be 100644
--- a/chrome/browser/ui/toasts/api/toast_id.cc
+++ b/chrome/browser/ui/toasts/api/toast_id.cc
@@ -40,6 +40,8 @@
       return "TabGroupSyncRemovedFromGroup";
     case ToastId::kVideoFrameCopied:
       return "VideoFrameCopied";
+    case ToastId::kClosePinnedTab:
+      return "ClosePinnedTab";
   }
 
   NOTREACHED();
diff --git a/chrome/browser/ui/toasts/api/toast_id.h b/chrome/browser/ui/toasts/api/toast_id.h
index 8e8905e..af2e4f6 100644
--- a/chrome/browser/ui/toasts/api/toast_id.h
+++ b/chrome/browser/ui/toasts/api/toast_id.h
@@ -35,7 +35,8 @@
   kTabGroupSyncUserJoined = 13,
   kTabGroupSyncRemovedFromGroup = 14,
   kVideoFrameCopied = 15,
-  kMaxValue = kVideoFrameCopied
+  kClosePinnedTab = 16,
+  kMaxValue = kClosePinnedTab
 };
 // LINT.ThenChange(/tools/metrics/histograms/metadata/toasts/enums.xml:ToastId)
 
diff --git a/chrome/browser/ui/toasts/api/toast_specification.cc b/chrome/browser/ui/toasts/api/toast_specification.cc
index dd7ff67..fc59f5a 100644
--- a/chrome/browser/ui/toasts/api/toast_specification.cc
+++ b/chrome/browser/ui/toasts/api/toast_specification.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/toasts/api/toast_specification.h"
 
 #include <memory>
+#include <string>
 
 #include "base/check.h"
 #include "base/functional/callback.h"
@@ -52,6 +53,15 @@
   return *this;
 }
 
+ToastSpecification::Builder& ToastSpecification::Builder::AddAccelerator(
+    ui::Accelerator accelerator,
+    base::RepeatingClosure callback) {
+  CHECK(!callback.is_null());
+  toast_specification_->AddAccelerator(std::move(accelerator),
+                                       std::move(callback));
+  return *this;
+}
+
 std::unique_ptr<ToastSpecification> ToastSpecification::Builder::Build() {
   ValidateSpecification();
   return std::move(toast_specification_);
@@ -102,3 +112,9 @@
 void ToastSpecification::AddGlobalScope() {
   is_global_scope_ = true;
 }
+
+void ToastSpecification::AddAccelerator(ui::Accelerator accelerator,
+                                        base::RepeatingClosure callback) {
+  accelerator_ = std::move(accelerator);
+  accelerator_callback_ = std::move(callback);
+}
diff --git a/chrome/browser/ui/toasts/api/toast_specification.h b/chrome/browser/ui/toasts/api/toast_specification.h
index 813f30b..3e2a34e6a 100644
--- a/chrome/browser/ui/toasts/api/toast_specification.h
+++ b/chrome/browser/ui/toasts/api/toast_specification.h
@@ -12,9 +12,10 @@
 #include "base/functional/callback_forward.h"
 #include "base/memory/raw_ref.h"
 #include "base/types/pass_key.h"
+#include "ui/base/accelerators/accelerator.h"
 #include "ui/gfx/vector_icon_types.h"
 
-// ToastSpecification details what the toast should contain when shown.
+// ToastSpecification details the supported actions and elements it contains.
 class ToastSpecification {
  public:
   class Builder final {
@@ -48,6 +49,11 @@
     // dismiss.
     Builder& AddGlobalScoped();
 
+    // Adds the accelerator that is used to trigger an action when this toast is
+    // displayed. The accelerator is handled by the view.
+    Builder& AddAccelerator(ui::Accelerator accelerator,
+                            base::RepeatingClosure callback);
+
     std::unique_ptr<ToastSpecification> Build();
 
    private:
@@ -72,13 +78,21 @@
   base::RepeatingClosure action_button_callback() const {
     return action_button_closure_;
   }
+  const ui::Accelerator& accelerator() const { return accelerator_; }
+  base::RepeatingClosure accelerator_callback() const {
+    return accelerator_callback_;
+  }
+
   bool has_menu() const { return has_menu_; }
   bool is_global_scope() const { return is_global_scope_; }
+  bool has_accelerator() const { return !accelerator_.IsEmpty(); }
 
   void AddCloseButton();
   void AddActionButton(int string_id, base::RepeatingClosure closure);
   void AddMenu();
   void AddGlobalScope();
+  void AddAccelerator(ui::Accelerator accelerator,
+                      base::RepeatingClosure callback);
 
  private:
   const base::raw_ref<const gfx::VectorIcon> icon_;
@@ -88,6 +102,8 @@
   std::optional<int> action_button_string_id_;
   base::RepeatingClosure action_button_closure_;
   bool is_global_scope_ = false;
+  ui::Accelerator accelerator_;
+  base::RepeatingClosure accelerator_callback_;
 };
 
 #endif  // CHROME_BROWSER_UI_TOASTS_API_TOAST_SPECIFICATION_H_
diff --git a/chrome/browser/ui/toasts/toast_controller.cc b/chrome/browser/ui/toasts/toast_controller.cc
index a174ef2a..f29acde 100644
--- a/chrome/browser/ui/toasts/toast_controller.cc
+++ b/chrome/browser/ui/toasts/toast_controller.cc
@@ -267,6 +267,12 @@
   const ui::ImageModel* image_override = params.image_override.has_value()
                                              ? &params.image_override.value()
                                              : nullptr;
+
+  if (spec->has_accelerator()) {
+    params.body_string_replacement_params.emplace_back(
+        spec->accelerator().GetShortcutText());
+  }
+
   const std::u16string body_string =
       params.body_string_override.has_value()
           ? params.body_string_override.value()
@@ -296,6 +302,11 @@
     toast_view->AddMenu(std::move(params.menu_model));
   }
 
+  if (spec->has_accelerator()) {
+    toast_view->AddAcceleratorCallback(spec->accelerator(),
+                                       spec->accelerator_callback());
+  }
+
   toast_view_ = toast_view.get();
   toast_widget_ =
       views::BubbleDialogDelegateView::CreateBubble(std::move(toast_view));
@@ -320,7 +331,12 @@
   toast_widget_->SetFocusTraversableParentView(anchor_view);
 
   if (!is_omnibox_popup_showing_) {
-    toast_widget_->ShowInactive();
+    if (spec->has_accelerator()) {
+      toast_widget_->Show();
+    } else {
+      toast_widget_->ShowInactive();
+    }
+
     toast_view_->AnimateIn();
   } else {
     toast_widget_->Hide();
diff --git a/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc b/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
index 93a4479..cd79b237 100644
--- a/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
+++ b/chrome/browser/ui/toasts/toast_controller_interactive_ui_test.cc
@@ -121,6 +121,7 @@
         {toast_features::kToastFramework, toast_features::kToastRefinements,
          toast_features::kLinkCopiedToast, toast_features::kImageCopiedToast,
          toast_features::kReadingListToast,
+         toast_features::kPinnedTabToastOnClose,
          plus_addresses::features::kPlusAddressesEnabled,
          plus_addresses::features::kPlusAddressFullFormFill},
         {});
@@ -556,3 +557,58 @@
         toast_controller->MaybeShowToast(ToastParams(ToastId::kLinkCopied)));
   }
 }
+
+#if BUILDFLAG(IS_MAC)
+// TODO(crbug.com/416316768): Fix disabled tests on mac.
+#define MAYBE_DISABLED(name) DISABLED_##name
+#else
+#define MAYBE_DISABLED(name) name
+#endif  // BUILDFLAG(IS_MAC)
+
+IN_PROC_BROWSER_TEST_F(
+    ToastControllerInteractiveTest,
+    MAYBE_DISABLED(ShowPinnedTabToastOnTabCloseViaKeyboardShortcut)) {
+  ui::Accelerator close_tab_accelerator;
+  ASSERT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())->GetAccelerator(
+      IDC_CLOSE_TAB, &close_tab_accelerator));
+
+  RunTestSequence(
+      // Add a pinned tab.
+      InstrumentTab(kFirstTab), WaitForShow(kFirstTab),
+      AddInstrumentedTab(kSecondTab, GetURL()),
+      SelectTab(kTabStripElementId, 0),
+      Do([&]() { browser()->tab_strip_model()->SetTabPinned(0, true); }),
+      // Expect that closing the tab with an accelerator will show a toast.
+      SendAccelerator(kBrowserViewElementId, close_tab_accelerator),
+      WaitForShow(toasts::ToastView::kToastViewId),
+      ActivateSurface(toasts::ToastView::kToastViewId),
+      CheckResult([&]() { return browser()->tab_strip_model()->count(); }, 2),
+      // Expect that we can close the tab by pressing the accelerator again.
+      SendAccelerator(toasts::ToastView::kToastViewId, close_tab_accelerator),
+      WaitForHide(toasts::ToastView::kToastViewId),
+      CheckResult([&]() { return browser()->tab_strip_model()->count(); }, 1));
+}
+
+IN_PROC_BROWSER_TEST_F(ToastControllerInteractiveTest,
+                       DismissToastWithEscapeKey) {
+  ui::Accelerator escape_accelerator(ui::VKEY_ESCAPE, ui::EF_NONE);
+  ui::Accelerator close_tab_accelerator;
+  ASSERT_TRUE(BrowserView::GetBrowserViewForBrowser(browser())->GetAccelerator(
+      IDC_CLOSE_TAB, &close_tab_accelerator));
+
+  RunTestSequence(
+      // Add a pinned tab.
+      InstrumentTab(kFirstTab), WaitForShow(kFirstTab),
+      AddInstrumentedTab(kSecondTab, GetURL()),
+      SelectTab(kTabStripElementId, 0),
+      Do([&]() { browser()->tab_strip_model()->SetTabPinned(0, true); }),
+      // Expect that closing the tab with an accelerator will show a toast.
+      SendAccelerator(kBrowserViewElementId, close_tab_accelerator),
+      WaitForShow(toasts::ToastView::kToastViewId),
+      ActivateSurface(toasts::ToastView::kToastViewId),
+      CheckResult([&]() { return browser()->tab_strip_model()->count(); }, 2),
+      // Expect that we can dismiss the toast by pressing the escape key.
+      SendAccelerator(toasts::ToastView::kToastViewId, escape_accelerator),
+      WaitForHide(toasts::ToastView::kToastViewId),
+      CheckResult([&]() { return browser()->tab_strip_model()->count(); }, 2));
+}
diff --git a/chrome/browser/ui/toasts/toast_features.cc b/chrome/browser/ui/toasts/toast_features.cc
index a6e6b345..da35feb 100644
--- a/chrome/browser/ui/toasts/toast_features.cc
+++ b/chrome/browser/ui/toasts/toast_features.cc
@@ -66,6 +66,11 @@
              "ClearBrowsingDataToast",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// Enables the pinned tab closing notification toast.
+BASE_FEATURE(kPinnedTabToastOnClose,
+             "PinnedTabToastOnClose",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // static
 bool IsEnabled(const base::Feature& feature) {
   return kToastDemoMode.Get() || base::FeatureList::IsEnabled(feature);
diff --git a/chrome/browser/ui/toasts/toast_features.h b/chrome/browser/ui/toasts/toast_features.h
index a2ba1d5..99824d99 100644
--- a/chrome/browser/ui/toasts/toast_features.h
+++ b/chrome/browser/ui/toasts/toast_features.h
@@ -36,6 +36,7 @@
 BASE_DECLARE_FEATURE(kReadingListToast);
 BASE_DECLARE_FEATURE(kLensOverlayToast);
 BASE_DECLARE_FEATURE(kClearBrowsingDataToast);
+BASE_DECLARE_FEATURE(kPinnedTabToastOnClose);
 
 // Wrapper function used to check if a specific toast feature is enabled. Must
 // be used for toasts that are part of demo mode.
diff --git a/chrome/browser/ui/toasts/toast_service.cc b/chrome/browser/ui/toasts/toast_service.cc
index 7018bad..2622469 100644
--- a/chrome/browser/ui/toasts/toast_service.cc
+++ b/chrome/browser/ui/toasts/toast_service.cc
@@ -8,8 +8,10 @@
 
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
+#include "chrome/app/chrome_command_ids.h"
 #include "chrome/app/vector_icons/vector_icons.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_features.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/chrome_pages.h"
@@ -21,6 +23,8 @@
 #include "chrome/browser/ui/toasts/api/toast_registry.h"
 #include "chrome/browser/ui/toasts/api/toast_specification.h"
 #include "chrome/browser/ui/toasts/toast_controller.h"
+#include "chrome/browser/ui/toasts/toast_features.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_entry_id.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_enums.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_ui.h"
@@ -37,6 +41,7 @@
 #include "components/strings/grit/components_strings.h"
 #include "components/tabs/public/tab_interface.h"
 #include "components/vector_icons/vector_icons.h"
+#include "ui/base/accelerators/accelerator.h"
 #include "ui/menus/simple_menu_model.h"
 
 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
@@ -252,4 +257,23 @@
             .AddGlobalScoped()
             .Build());
   }
-}
+
+  if (toast_features::IsEnabled(toast_features::kPinnedTabToastOnClose)) {
+    BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(
+        browser_window_interface->GetBrowserForMigrationOnly());
+
+    auto callback = base::BindRepeating(
+        [](TabStripModel* model) { model->CloseSelectedTabs(); },
+        browser_window_interface->GetTabStripModel());
+
+    ui::Accelerator accelerator;
+    CHECK(
+        browser_view->GetAcceleratorForCommandId(IDC_CLOSE_TAB, &accelerator));
+    toast_registry_->RegisterToast(
+        ToastId::kClosePinnedTab,
+        ToastSpecification::Builder(kKeepIcon, IDS_CLOSE_PINNED_TAB_TOAST_BODY)
+            .AddAccelerator(accelerator, std::move(callback))
+            .Build());
+  }
+
+}  // RegisterToasts() end.
diff --git a/chrome/browser/ui/toasts/toast_service_browsertest.cc b/chrome/browser/ui/toasts/toast_service_browsertest.cc
index ab3540a..4efa08c0 100644
--- a/chrome/browser/ui/toasts/toast_service_browsertest.cc
+++ b/chrome/browser/ui/toasts/toast_service_browsertest.cc
@@ -45,7 +45,8 @@
          plus_addresses::features::kPlusAddressesEnabled,
          plus_addresses::features::kPlusAddressFullFormFill,
          safe_browsing::kEsbAsASyncedSetting,
-         data_sharing::features::kDataSharingFeature},
+         data_sharing::features::kDataSharingFeature,
+         toast_features::kPinnedTabToastOnClose},
         /*disabled_features*/ {});
     InProcessBrowserTest::SetUp();
   }
diff --git a/chrome/browser/ui/toasts/toast_view.cc b/chrome/browser/ui/toasts/toast_view.cc
index 4c559e3..634d4f3 100644
--- a/chrome/browser/ui/toasts/toast_view.cc
+++ b/chrome/browser/ui/toasts/toast_view.cc
@@ -18,6 +18,7 @@
 #include "chrome/grit/generated_resources.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/vector_icons/vector_icons.h"
+#include "ui/base/accelerators/accelerator.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -115,7 +116,7 @@
       image_override_(image_override),
       render_toast_over_web_contents_(render_toast_over_web_contents),
       toast_close_callback_(std::move(toast_close_callback)) {
-  set_background_color(ui::kColorToastBackgroundProminent);
+  SetBackgroundColor(ui::kColorToastBackgroundProminent);
   SetPaintClientToLayer(true);
   SetShowCloseButton(false);
   DialogDelegate::SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone));
@@ -156,6 +157,15 @@
                           ToastCloseReason::kMenuItemClick));
 }
 
+void ToastView::AddAcceleratorCallback(ui::Accelerator accelerator,
+                                       base::RepeatingClosure callback) {
+  has_accelerator_ = true;
+  accelerator_ = accelerator;
+  accelerator_callback_ = std::move(callback);
+
+  AddAccelerator(accelerator);
+}
+
 int ToastView::GetIconSize() {
   const ChromeLayoutProvider* lp = ChromeLayoutProvider::Get();
   return lp->GetDistanceMetric(DISTANCE_TOAST_BUBBLE_ICON_SIZE);
@@ -306,7 +316,8 @@
       top_margin, lp->GetDistanceMetric(DISTANCE_TOAST_BUBBLE_MARGIN_LEFT),
       total_vertical_margins - top_margin, right_margin));
 
-  if (has_action_button_ || has_close_button_ || menu_model_) {
+  if (has_action_button_ || has_close_button_ || menu_model_ ||
+      has_accelerator_) {
     SetFocusTraversesOut(true);
   } else {
     set_focus_traversable_from_anchor_view(false);
@@ -433,6 +444,15 @@
   }
 }
 
+bool ToastView::AcceleratorPressed(const ui::Accelerator& accelerator) {
+  if (accelerator == accelerator_) {
+    accelerator_callback_.Run();
+    return true;
+  }
+
+  return false;
+}
+
 void ToastView::AnimateOut(base::OnceClosure callback,
                            bool show_height_animation) {
   if (!gfx::Animation::ShouldRenderRichAnimation()) {
diff --git a/chrome/browser/ui/toasts/toast_view.h b/chrome/browser/ui/toasts/toast_view.h
index 269f0a3..ec4f380 100644
--- a/chrome/browser/ui/toasts/toast_view.h
+++ b/chrome/browser/ui/toasts/toast_view.h
@@ -12,6 +12,7 @@
 #include "base/functional/callback_forward.h"
 #include "base/functional/callback_helpers.h"
 #include "base/memory/raw_ptr.h"
+#include "ui/base/accelerators/accelerator.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/widget/widget.h"
@@ -79,6 +80,13 @@
   // views::BubbleDialogDelegateView::CreateBubble).
   void AddMenu(std::unique_ptr<ui::MenuModel> model);
 
+  // Adds the accelerator that runs `callback` when triggered. The accelerator
+  // is handled in this view when it has focus.
+  // Must be called prior to Init
+  // (which is called from views::BubbleDialogDelegateView::CreateBubble).
+  void AddAcceleratorCallback(ui::Accelerator accelerator,
+                              base::RepeatingClosure callback);
+
   // views::BubbleDialogDelegateView:
   void Init() override;
 
@@ -104,6 +112,7 @@
   // views::BubbleDialogDelegateView:
   gfx::Rect GetBubbleBounds() override;
   void OnThemeChanged() override;
+  bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
 
  private:
   void AnimateOut(base::OnceClosure callback, bool show_height_animation);
@@ -126,10 +135,13 @@
   bool render_toast_over_web_contents_;
   bool has_close_button_ = false;
   bool has_action_button_ = false;
+  bool has_accelerator_ = false;
   std::u16string action_button_text_;
   base::RepeatingClosure action_button_callback_;
   base::RepeatingClosure close_button_callback_;
   base::RepeatingCallback<void(ToastCloseReason)> toast_close_callback_;
+  ui::Accelerator accelerator_;
+  base::RepeatingClosure accelerator_callback_;
   std::unique_ptr<ui::MenuModel> menu_model_;
   // Wraps `menu_model_` and triggers closing the toast after executing menu
   // commands.
diff --git a/chrome/browser/ui/views/autofill/payments/payments_view_util.cc b/chrome/browser/ui/views/autofill/payments/payments_view_util.cc
index 0324e06d..a8113344 100644
--- a/chrome/browser/ui/views/autofill/payments/payments_view_util.cc
+++ b/chrome/browser/ui/views/autofill/payments/payments_view_util.cc
@@ -38,6 +38,7 @@
 #include "ui/views/controls/label.h"
 #include "ui/views/controls/separator.h"
 #include "ui/views/controls/styled_label.h"
+#include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/controls/theme_tracking_image_view.h"
 #include "ui/views/controls/throbber.h"
 #include "ui/views/layout/box_layout.h"
@@ -345,4 +346,25 @@
 }
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 
+std::unique_ptr<views::View> CreateLabelAndTextfieldView(
+    const std::u16string& text) {
+  auto view_builder =
+      views::Builder<views::BoxLayoutView>()
+          .SetOrientation(views::BoxLayout::Orientation::kVertical)
+          .SetBetweenChildSpacing(
+              ChromeLayoutProvider::Get()->GetDistanceMetric(
+                  DISTANCE_RELATED_CONTROL_VERTICAL_SMALL));
+
+  view_builder.AddChild(views::Builder<views::Label>()
+                            .SetText(text)
+                            .SetTextContext(views::style::CONTEXT_LABEL)
+                            .SetTextStyle(views::style::STYLE_PRIMARY)
+                            .SetHorizontalAlignment(gfx::ALIGN_TO_HEAD));
+
+  view_builder.AddChild(
+      views::Builder<views::Textfield>().SetAccessibleName(text));
+
+  return std::move(view_builder).Build();
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/payments/payments_view_util.h b/chrome/browser/ui/views/autofill/payments/payments_view_util.h
index 83a82ac9..4a27f86 100644
--- a/chrome/browser/ui/views/autofill/payments/payments_view_util.h
+++ b/chrome/browser/ui/views/autofill/payments/payments_view_util.h
@@ -110,6 +110,11 @@
                                         const ui::ColorProvider* provider);
 #endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)
 
+// Creates a view containing a label and a textfield. The view is arranged
+// vertically with the label positioned above the textfield.
+std::unique_ptr<views::View> CreateLabelAndTextfieldView(
+    const std::u16string& text);
+
 }  // namespace autofill
 
 #endif  // CHROME_BROWSER_UI_VIEWS_AUTOFILL_PAYMENTS_PAYMENTS_VIEW_UTIL_H_
diff --git a/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.cc b/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.cc
index 15a31cf..cfe7880 100644
--- a/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.cc
+++ b/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.cc
@@ -6,8 +6,16 @@
 
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/ui/autofill/payments/payments_view_factory.h"
+#include "chrome/browser/ui/views/autofill/payments/payments_view_util.h"
+#include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller.h"
 #include "components/constrained_window/constrained_window_views.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/mojom/dialog_button.mojom.h"
+#include "ui/views/accessibility/view_accessibility.h"
+#include "ui/views/bubble/bubble_frame_view.h"
+#include "ui/views/controls/textfield/textfield.h"
 
 namespace autofill {
 
@@ -15,6 +23,14 @@
     base::WeakPtr<SaveAndFillDialogController> controller)
     : controller_(controller) {
   SetModalType(ui::mojom::ModalType::kChild);
+  set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
+      views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH));
+  SetButtons(static_cast<int>(ui::mojom::DialogButton::kOk) |
+             static_cast<int>(ui::mojom::DialogButton::kCancel));
+  SetButtonLabel(ui::mojom::DialogButton::kOk,
+                 controller_->GetAcceptButtonText());
+  SetShowCloseButton(false);
+  InitViews();
 }
 
 SaveAndFillDialogViews::~SaveAndFillDialogViews() = default;
@@ -31,4 +47,44 @@
   return dialog_view->GetWeakPtr();
 }
 
+void SaveAndFillDialogViews::AddedToWidget() {
+  if (controller_->IsUploadSaveAndFill()) {
+    GetBubbleFrameView()->SetTitleView(
+        std::make_unique<TitleWithIconAfterLabelView>(
+            GetWindowTitle(), TitleWithIconAfterLabelView::Icon::GOOGLE_PAY));
+  } else {
+    auto title_view = std::make_unique<views::Label>(
+        GetWindowTitle(), views::style::CONTEXT_DIALOG_TITLE);
+    title_view->SetHorizontalAlignment(gfx::ALIGN_TO_HEAD);
+    title_view->SetMultiLine(true);
+    GetBubbleFrameView()->SetTitleView(std::move(title_view));
+  }
+}
+
+std::u16string SaveAndFillDialogViews::GetWindowTitle() const {
+  return controller_ ? controller_->GetWindowTitle() : std::u16string();
+}
+
+void SaveAndFillDialogViews::InitViews() {
+  auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::Orientation::kVertical, gfx::Insets(),
+      ChromeLayoutProvider::Get()->GetDistanceMetric(
+          views::DISTANCE_UNRELATED_CONTROL_VERTICAL)));
+  layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kCenter);
+  set_margins(ChromeLayoutProvider::Get()->GetDialogInsetsForContentType(
+      views::DialogContentType::kControl, views::DialogContentType::kControl));
+
+  AddChildView(views::Builder<views::Label>()
+                   .SetText(controller_->GetExplanatoryMessage())
+                   .SetTextContext(views::style::CONTEXT_DIALOG_BODY_TEXT)
+                   .SetTextStyle(views::style::STYLE_SECONDARY)
+                   .SetMultiLine(true)
+                   .SetHorizontalAlignment(gfx::ALIGN_TO_HEAD)
+                   .Build());
+  // Create a container for the card number label and textfield.
+  AddChildView(CreateLabelAndTextfieldView(controller_->GetCardNumberLabel()));
+  // Create a container for the cardholder name label and textfield.
+  AddChildView(CreateLabelAndTextfieldView(controller_->GetNameOnCardLabel()));
+}
+
 }  // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.h b/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.h
index 28e4df9..630ab9b 100644
--- a/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.h
+++ b/chrome/browser/ui/views/autofill/payments/save_and_fill_dialog_views.h
@@ -24,7 +24,14 @@
   // SaveAndFillDialogView:
   base::WeakPtr<SaveAndFillDialogView> GetWeakPtr() override;
 
+  // DialogDelegateView:
+  void AddedToWidget() override;
+  std::u16string GetWindowTitle() const override;
+
  private:
+  // Initialize the dialog's contents.
+  void InitViews();
+
   base::WeakPtr<SaveAndFillDialogController> controller_;
 
   base::WeakPtrFactory<SaveAndFillDialogViews> weak_ptr_factory_{this};
diff --git a/chrome/browser/ui/views/autofill/popup/popup_view_utils.cc b/chrome/browser/ui/views/autofill/popup/popup_view_utils.cc
index 24a4f8e..5e907e3 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_view_utils.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_view_utils.cc
@@ -459,6 +459,7 @@
     case SuggestionType::kCreditCardEntry:
     case SuggestionType::kDevtoolsTestAddresses:
     case SuggestionType::kFillAutofillAi:
+    case SuggestionType::kLoyaltyCardEntry:
     case SuggestionType::kPasswordEntry:
       return true;
     case SuggestionType::kAccountStoragePasswordEntry:
@@ -480,7 +481,6 @@
     case SuggestionType::kFillPassword:
     case SuggestionType::kGeneratePasswordEntry:
     case SuggestionType::kIbanEntry:
-    case SuggestionType::kLoyaltyCardEntry:
     case SuggestionType::kInsecureContextPaymentDisabledMessage:
     case SuggestionType::kManageAddress:
     case SuggestionType::kManageAutofillAi:
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
index f59cb45..33fee6e 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar.cc
@@ -274,8 +274,8 @@
 
 void SavedTabGroupBar::OnInitialized() {
   RemoveAllChildViews();
-  LoadAllButtonsFromModel();
   overflow_button_ = AddChildView(CreateOverflowButton());
+  LoadAllButtonsFromModel();
   InvalidateLayout();
 }
 
diff --git a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc
index 1385c9a..1f70cd6 100644
--- a/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc
+++ b/chrome/browser/ui/views/bookmarks/saved_tab_groups/saved_tab_group_bar_unittest.cc
@@ -586,4 +586,23 @@
   EXPECT_EQ(1u, saved_tab_group_bar()->children().size());
 }
 
+TEST_F(SavedTabGroupBarUnitTest, GroupLoadFromModelInOrder) {
+  base::Uuid uuid1 = AddGroupFromLocal();
+  base::Uuid uuid2 = AddGroupFromLocal();
+  base::Uuid uuid3 = AddGroupFromLocal();
+
+  auto saved_tab_group_bar =
+      std::make_unique<SavedTabGroupBar>(browser(), false);
+  auto children = saved_tab_group_bar->children();
+
+  // Verify groups are shown in reverse order(last added groups show first).
+  EXPECT_EQ(4u, children.size());
+  EXPECT_EQ(uuid3,
+            views::AsViewClass<SavedTabGroupButton>(children[0])->guid());
+  EXPECT_EQ(uuid2,
+            views::AsViewClass<SavedTabGroupButton>(children[1])->guid());
+  EXPECT_EQ(uuid1,
+            views::AsViewClass<SavedTabGroupButton>(children[2])->guid());
+}
+
 }  // namespace tab_groups
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 535891e..559a149 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -90,8 +90,6 @@
 #include "chrome/browser/ui/sync/one_click_signin_links_delegate_impl.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/collaboration_messaging_tab_data.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/shared_tab_group_feedback_controller.h"
-#include "chrome/browser/ui/tabs/split_tab_data.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_enums.h"
 #include "chrome/browser/ui/tabs/tab_menu_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -219,6 +217,8 @@
 #include "components/sessions/core/tab_restore_service.h"
 #include "components/startup_metric_utils/browser/startup_metric_utils.h"
 #include "components/sync/service/sync_service.h"
+#include "components/tabs/public/split_tab_data.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "components/tabs/public/tab_interface.h"
 #include "components/translate/core/browser/language_state.h"
 #include "components/translate/core/browser/translate_manager.h"
diff --git a/chrome/browser/ui/views/frame/browser_view_browsertest.cc b/chrome/browser/ui/views/frame/browser_view_browsertest.cc
index 933a264..b2d0d089 100644
--- a/chrome/browser/ui/views/frame/browser_view_browsertest.cc
+++ b/chrome/browser/ui/views/frame/browser_view_browsertest.cc
@@ -26,8 +26,6 @@
 #include "chrome/browser/ui/tab_modal_confirm_dialog.h"
 #include "chrome/browser/ui/tab_ui_helper.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
-#include "chrome/browser/ui/tabs/split_tab_collection.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h"
 #include "chrome/browser/ui/test/test_browser_ui.h"
@@ -55,6 +53,8 @@
 #include "components/policy/core/common/policy_types.h"
 #include "components/prefs/pref_service.h"
 #include "components/safe_browsing/core/browser/realtime/fake_url_lookup_service.h"
+#include "components/tabs/public/split_tab_collection.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "content/public/browser/invalidate_type.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/browser/web_contents_observer.h"
diff --git a/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc b/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc
index cbba3036..4b6aa7c 100644
--- a/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/frame/multi_contents_view_interactive_uitest.cc
@@ -6,8 +6,6 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
-#include "chrome/browser/ui/tabs/split_tab_collection.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/test/split_tabs_interactive_test_mixin.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -16,6 +14,8 @@
 #include "chrome/common/webui_url_constants.h"
 #include "chrome/test/base/interactive_test_utils.h"
 #include "chrome/test/interaction/interactive_browser_test.h"
+#include "components/tabs/public/split_tab_collection.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
 #include "third_party/blink/public/common/input/web_mouse_event.h"
diff --git a/chrome/browser/ui/views/lens/lens_region_search_instructions_view.cc b/chrome/browser/ui/views/lens/lens_region_search_instructions_view.cc
index fea6c9e6..aca802b 100644
--- a/chrome/browser/ui/views/lens/lens_region_search_instructions_view.cc
+++ b/chrome/browser/ui/views/lens/lens_region_search_instructions_view.cc
@@ -75,7 +75,7 @@
   SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone));
   set_close_on_deactivate(false);
   set_corner_radius(kCornerRadius);
-  set_background_color(kColorFeatureLensPromoBubbleBackground);
+  SetBackgroundColor(kColorFeatureLensPromoBubbleBackground);
 
   // Add the leading drag selection icon.
   auto selection_icon_view =
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
index ff8696b..d4c52b491 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
@@ -14,6 +14,7 @@
 #include "base/scoped_observation.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/bind.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/with_feature_override.h"
 #include "base/time/time.h"
@@ -1465,6 +1466,7 @@
 #endif
 IN_PROC_BROWSER_TEST_P(AvatarToolbarButtonHistorySyncOptinClickBrowserTest,
                        MAYBE_CollapsesOnClickAndTriggersProfileMenuStartup) {
+  base::HistogramTester histogram_tester;
   AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser());
   // Normal state.
   ASSERT_TRUE(avatar->GetText().empty());
@@ -1479,9 +1481,25 @@
   EXPECT_EQ(
       avatar->GetText(),
       l10n_util::GetStringUTF16(GetParam().expected_history_sync_message_id));
+  // `Signin.SyncOptIn.IdentityPill.Shown` should be recorded with the correct
+  // access point.
+  histogram_tester.ExpectBucketCount(
+      "Signin.SyncOptIn.IdentityPill.Shown",
+      signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnStartup,
+      /*expected_count=*/1);
+  histogram_tester.ExpectBucketCount(
+      "Signin.SyncOptIn.IdentityPill.Shown",
+      signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnInactivity,
+      /*expected_count=*/0);
   // The button action should be overridden.
   EXPECT_TRUE(avatar->HasExplicitButtonAction());
+  histogram_tester.ExpectTotalCount(
+      "Signin.SyncOptIn.IdentityPill.DurationBeforeClick",
+      /*expected_count=*/0);
   Click(avatar);
+  histogram_tester.ExpectTotalCount(
+      "Signin.SyncOptIn.IdentityPill.DurationBeforeClick",
+      /*expected_count=*/1);
   auto* coordinator = ProfileMenuCoordinator::FromBrowser(browser());
   ASSERT_NE(coordinator, nullptr);
   EXPECT_TRUE(coordinator->IsShowing());
@@ -1516,6 +1534,7 @@
 #endif
 IN_PROC_BROWSER_TEST_P(AvatarToolbarButtonHistorySyncOptinClickBrowserTest,
                        MAYBE_CollapsesOnClickAndTriggersProfileMenuInactivity) {
+  base::HistogramTester histogram_tester;
   AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser());
   // Normal state.
   ASSERT_TRUE(avatar->GetText().empty());
@@ -1530,6 +1549,16 @@
   EXPECT_EQ(
       avatar->GetText(),
       l10n_util::GetStringUTF16(GetParam().expected_history_sync_message_id));
+  // `Signin.SyncOptIn.IdentityPill.Shown` should be recorded with the correct
+  // access point.
+  histogram_tester.ExpectBucketCount(
+      "Signin.SyncOptIn.IdentityPill.Shown",
+      signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnStartup,
+      /*expected_count=*/1);
+  histogram_tester.ExpectBucketCount(
+      "Signin.SyncOptIn.IdentityPill.Shown",
+      signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnInactivity,
+      /*expected_count=*/0);
   EXPECT_TRUE(avatar->HasExplicitButtonAction());
   avatar->TriggerTimeoutForTesting(AvatarDelayType::kHistorySyncOptin);
   // The button comes back to the normal state.
@@ -1543,9 +1572,25 @@
   EXPECT_EQ(
       avatar->GetText(),
       l10n_util::GetStringUTF16(GetParam().expected_history_sync_message_id));
+  // `Signin.SyncOptIn.IdentityPill.Shown` should be recorded with the correct
+  // access point.
+  histogram_tester.ExpectBucketCount(
+      "Signin.SyncOptIn.IdentityPill.Shown",
+      signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnStartup,
+      /*expected_count=*/1);
+  histogram_tester.ExpectBucketCount(
+      "Signin.SyncOptIn.IdentityPill.Shown",
+      signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnInactivity,
+      /*expected_count=*/1);
   // The button action should be overridden.
   EXPECT_TRUE(avatar->HasExplicitButtonAction());
+  histogram_tester.ExpectTotalCount(
+      "Signin.SyncOptIn.IdentityPill.DurationBeforeClick",
+      /*expected_count=*/0);
   Click(avatar);
+  histogram_tester.ExpectTotalCount(
+      "Signin.SyncOptIn.IdentityPill.DurationBeforeClick",
+      /*expected_count=*/1);
   auto* coordinator = ProfileMenuCoordinator::FromBrowser(browser());
   ASSERT_NE(coordinator, nullptr);
   EXPECT_TRUE(coordinator->IsShowing());
@@ -1640,6 +1685,7 @@
 IN_PROC_BROWSER_TEST_P(
     AvatarToolbarButtonHistorySyncOptinClickBrowserTest,
     MAYBE_TriggersAndCollapsesConsistentlyAcrossMultipleBrowsers) {
+  base::HistogramTester histogram_tester;
   Profile* profile = browser()->profile();
   Browser* browser_1 = browser();
   AvatarToolbarButton* avatar_1 = GetAvatarToolbarButton(browser_1);
@@ -1663,6 +1709,16 @@
   EXPECT_EQ(
       avatar_2->GetText(),
       l10n_util::GetStringUTF16(GetParam().expected_history_sync_message_id));
+  // `Signin.SyncOptIn.IdentityPill.Shown` histogram should be recorded only
+  // once.
+  histogram_tester.ExpectBucketCount(
+      "Signin.SyncOptIn.IdentityPill.Shown",
+      signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnStartup,
+      /*expected_count=*/1);
+  histogram_tester.ExpectBucketCount(
+      "Signin.SyncOptIn.IdentityPill.Shown",
+      signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnInactivity,
+      /*expected_count=*/0);
   avatar_1->TriggerTimeoutForTesting(AvatarDelayType::kHistorySyncOptin);
   // The button in both browsers comes back to the normal state.
   EXPECT_TRUE(avatar_1->GetText().empty());
@@ -1686,9 +1742,24 @@
   EXPECT_EQ(
       avatar_3->GetText(),
       l10n_util::GetStringUTF16(GetParam().expected_history_sync_message_id));
+  // `Signin.SyncOptIn.IdentityPill.Shown` histogram should be recorded only
+  // once.
+  histogram_tester.ExpectBucketCount(
+      "Signin.SyncOptIn.IdentityPill.Shown",
+      signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnStartup,
+      /*expected_count=*/1);
+  histogram_tester.ExpectBucketCount(
+      "Signin.SyncOptIn.IdentityPill.Shown",
+      signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnInactivity,
+      /*expected_count=*/1);
   // Clicking the button on any browser should collapse the history sync opt-in
   // in all browsers.
   Click(avatar_2);
+  // `Signin.SyncOptIn.IdentityPill.DurationBeforeClick` histogram should be
+  // recorded only once.
+  histogram_tester.ExpectTotalCount(
+      "Signin.SyncOptIn.IdentityPill.DurationBeforeClick",
+      /*expected_count=*/1);
   EXPECT_TRUE(avatar_1->GetText().empty());
   EXPECT_TRUE(avatar_2->GetText().empty());
   EXPECT_TRUE(avatar_3->GetText().empty());
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
index 90d7a2e..c46d045 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_delegate.cc
@@ -15,6 +15,7 @@
 #include "base/functional/callback_helpers.h"
 #include "base/memory/raw_ref.h"
 #include "base/memory/weak_ptr.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/notreached.h"
 #include "base/scoped_observation.h"
 #include "base/strings/utf_string_conversions.h"
@@ -533,6 +534,10 @@
   }
 
   void PromoUsed() {
+    CHECK(before_promo_used_elapsed_timer_.has_value());
+    base::UmaHistogramMediumTimes(
+        "Signin.SyncOptIn.IdentityPill.DurationBeforeClick",
+        before_promo_used_elapsed_timer_->Elapsed());
     sync_promo_identity_pill_manager_.RecordPromoUsed();
     Collapse();
   }
@@ -629,6 +634,7 @@
       collapse_timer_.Stop();
     }
     triggered_ = false;
+    before_promo_used_elapsed_timer_.reset();
     state_changed_callbacks.Notify();
   }
 
@@ -638,8 +644,11 @@
       // `HistorySyncOptin` in the next browser window(s).
       return;
     }
+    before_promo_used_elapsed_timer_.emplace();
     has_been_shown_since_startup_ = true;
     sync_promo_identity_pill_manager_.RecordPromoShown();
+    base::UmaHistogramEnumeration("Signin.SyncOptIn.IdentityPill.Shown",
+                                  access_point_);
     collapse_timer_.Start(FROM_HERE,
                           g_history_sync_optin_duration_for_testing.value_or(
                               kHistorySyncOptinDuration),
@@ -683,6 +692,9 @@
   bool has_been_shown_since_startup_ = false;
   base::OneShotTimer collapse_timer_;
 
+  // Timer to measure the time between the promo being shown and used (clicked).
+  std::optional<base::ElapsedTimer> before_promo_used_elapsed_timer_;
+
   const raw_ref<Profile> profile_;
 
   signin::SyncPromoIdentityPillManager sync_promo_identity_pill_manager_;
diff --git a/chrome/browser/ui/views/sharing_hub/sharing_hub_bubble_view_impl.cc b/chrome/browser/ui/views/sharing_hub/sharing_hub_bubble_view_impl.cc
index 65cde6f..428d8c7 100644
--- a/chrome/browser/ui/views/sharing_hub/sharing_hub_bubble_view_impl.cc
+++ b/chrome/browser/ui/views/sharing_hub/sharing_hub_bubble_view_impl.cc
@@ -53,7 +53,7 @@
   DCHECK(anchor_view);
   DCHECK(controller);
 
-  set_background_color(ui::kColorMenuBackground);
+  SetBackgroundColor(ui::kColorMenuBackground);
   SetAccessibleTitle(l10n_util::GetStringUTF16(IDS_SHARING_HUB_TOOLTIP));
   SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone));
   set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
diff --git a/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.cc b/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.cc
index 5075ce8..d2d2759 100644
--- a/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.cc
+++ b/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.cc
@@ -15,6 +15,7 @@
 #include "extensions/browser/disable_reason.h"
 #include "extensions/browser/extension_registrar.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/common/constants.h"
 #include "extensions/common/extension.h"
 
 namespace customize_chrome {
@@ -55,4 +56,19 @@
                          {extensions::disable_reason::DISABLE_USER_ACTION});
 }
 
+bool IsExtensionNtp(const GURL& url, Profile* profile) {
+  if (!url.SchemeIs(extensions::kExtensionScheme)) {
+    return false;
+  }
+
+  const extensions::Extension* extension_managing_ntp =
+      extensions::GetExtensionOverridingNewTabPage(profile);
+
+  if (!extension_managing_ntp) {
+    return false;
+  }
+
+  return extension_managing_ntp->id() == url.host();
+}
+
 }  // namespace customize_chrome
diff --git a/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.h b/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.h
index fdb5725..2dcdde48 100644
--- a/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.h
+++ b/chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.h
@@ -5,6 +5,9 @@
 #ifndef CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_CUSTOMIZE_CHROME_CUSTOMIZE_CHROME_UTILS_H_
 #define CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_CUSTOMIZE_CHROME_CUSTOMIZE_CHROME_UTILS_H_
 
+#include "chrome/browser/profiles/profile.h"
+#include "url/gurl.h"
+
 namespace content {
 class BrowserContext;
 }
@@ -19,6 +22,9 @@
 void MaybeDisableExtensionOverridingNtp(
     content::BrowserContext* browser_context);
 
+// Returns whether `url` belongs to an extension NTP.
+bool IsExtensionNtp(const GURL& url, Profile* profile);
+
 }  // namespace customize_chrome
 
 #endif  // CHROME_BROWSER_UI_VIEWS_SIDE_PANEL_CUSTOMIZE_CHROME_CUSTOMIZE_CHROME_UTILS_H_
diff --git a/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.cc b/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.cc
index 5254f69b..d10e30d9 100644
--- a/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.cc
+++ b/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.cc
@@ -15,6 +15,7 @@
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/tabs/public/tab_features.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h"
 #include "chrome/browser/ui/views/side_panel/side_panel_web_ui_view.h"
 #include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h"
@@ -97,26 +98,11 @@
 }
 
 bool SidePanelControllerViews::ShouldEnableEditTheme(const GURL& url) const {
-  return NewTabPageUI::IsNewTabPageOrigin(url) ||
-         (base::FeatureList::IsEnabled(ntp_features::kNtpFooter) &&
-          IsExtensionNtp(url));
-}
-
-bool SidePanelControllerViews::IsExtensionNtp(const GURL& url) const {
-  if (!url.SchemeIs(extensions::kExtensionScheme)) {
-    return false;
-  }
-
   Profile* const profile =
       Profile::FromBrowserContext(tab_->GetContents()->GetBrowserContext());
-  const extensions::Extension* extension_managing_ntp =
-      extensions::GetExtensionOverridingNewTabPage(profile);
-
-  if (!extension_managing_ntp) {
-    return false;
-  }
-
-  return extension_managing_ntp->id() == url.host();
+  return NewTabPageUI::IsNewTabPageOrigin(url) ||
+         (base::FeatureList::IsEnabled(ntp_features::kNtpFooter) &&
+          customize_chrome::IsExtensionNtp(url, profile));
 }
 
 void SidePanelControllerViews::DidFinishNavigation(
@@ -134,8 +120,7 @@
   if (CanShowOnURL(url)) {
     CreateAndRegisterEntry();
     if (customize_chrome_ui_) {
-      customize_chrome_ui_->AttachedTabStateUpdated(
-          NewTabPageUI::IsNewTabPageOrigin(url));
+      customize_chrome_ui_->AttachedTabStateUpdated(url);
       customize_chrome_ui_->UpdateThemeEditable(ShouldEnableEditTheme(url));
     }
   } else {
@@ -241,8 +226,7 @@
     entry = tab_->GetContents()->GetController().GetVisibleEntry();
   }
   const GURL& url = entry->GetURL();
-  customize_chrome_ui_->AttachedTabStateUpdated(
-      NewTabPageUI::IsNewTabPageOrigin(url));
+  customize_chrome_ui_->AttachedTabStateUpdated(url);
   customize_chrome_ui_->UpdateThemeEditable(ShouldEnableEditTheme(url));
 
   return customize_chrome_web_view;
diff --git a/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.h b/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.h
index 7ec206b1..9842ad05 100644
--- a/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.h
+++ b/chrome/browser/ui/views/side_panel/customize_chrome/side_panel_controller_views.h
@@ -66,9 +66,6 @@
   // Returns true for 1P NTP or extension NTP, otherwise returns false.
   bool ShouldEnableEditTheme(const GURL& url) const;
 
-  // Helper function to check if the URL belongs to an extension NTP.
-  bool IsExtensionNtp(const GURL& url) const;
-
   // Generates the view for the SidePanel contents. This is the WebUI for the
   // SidePanel. Used by the SidepanelRegistry to create the view.
   std::unique_ptr<views::View> CreateCustomizeChromeWebView(
diff --git a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
index 0e70124a..cc04e1d 100644
--- a/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
+++ b/chrome/browser/ui/views/tabs/browser_tab_strip_controller.cc
@@ -32,9 +32,7 @@
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/tab_ui_helper.h"
 #include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_utils.h"
-#include "chrome/browser/ui/tabs/split_tab_data.h"
 #include "chrome/browser/ui/tabs/split_tab_util.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_enums.h"
 #include "chrome/browser/ui/tabs/tab_group.h"
 #include "chrome/browser/ui/tabs/tab_group_deletion_dialog_controller.h"
@@ -67,7 +65,9 @@
 #include "components/tab_groups/tab_group_color.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "components/tabs/public/split_tab_data.h"
 #include "components/tabs/public/split_tab_id.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "components/tabs/public/tab_interface.h"
 #include "content/public/browser/navigation_controller.h"
 #include "content/public/browser/navigation_entry.h"
diff --git a/chrome/browser/ui/views/tabs/groups/avatar_container_view.cc b/chrome/browser/ui/views/tabs/groups/avatar_container_view.cc
index be77ce9..57764330 100644
--- a/chrome/browser/ui/views/tabs/groups/avatar_container_view.cc
+++ b/chrome/browser/ui/views/tabs/groups/avatar_container_view.cc
@@ -26,6 +26,7 @@
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/style/typography.h"
+#include "ui/views/style/typography_provider.h"
 #include "ui/views/view_class_properties.h"
 #include "ui/views/widget/widget.h"
 
@@ -78,8 +79,9 @@
                     int number,
                     SkColor text_color,
                     int font_size) {
-  gfx::FontList font_list({"Google Sans", "Roboto"}, gfx::Font::NORMAL,
-                          font_size, gfx::Font::Weight::NORMAL);
+  const auto& font_list = views::TypographyProvider::Get().GetFont(
+      views::style::TextContext::CONTEXT_DIALOG_BODY_TEXT,
+      views::style::TextStyle::STYLE_CAPTION_MEDIUM);
   gfx::Rect text_bounds(0, 0, diameter, diameter);
   canvas.DrawStringRectWithFlags(
       base::UTF8ToUTF16("+" + base::NumberToString(number)), font_list,
diff --git a/chrome/browser/ui/views/tabs/recent_activity_bubble_dialog_view.cc b/chrome/browser/ui/views/tabs/recent_activity_bubble_dialog_view.cc
index 83b2d6f..e7ab4ce 100644
--- a/chrome/browser/ui/views/tabs/recent_activity_bubble_dialog_view.cc
+++ b/chrome/browser/ui/views/tabs/recent_activity_bubble_dialog_view.cc
@@ -107,21 +107,6 @@
   return user;
 }
 
-// Gets the string for the metadata line to describe an event.
-std::u16string GetMetadataText(const ActivityLogItem& item) {
-  if (item.description_text == u"") {
-    // If there is no description, the line simply contains elapsed time
-    // since the action.
-    return item.time_delta_text;
-  } else {
-    // The metadata line contains the item's description, a bullet point,
-    // and the elapsed time since the action, separated by spaces.
-    std::u16string_view separator = u" ";
-    return base::JoinString(
-        {item.description_text, kBulletPoint, item.time_delta_text}, separator);
-  }
-}
-
 // TODO(crbug.com/392150086): Refactor this into utilities.
 std::optional<tab_groups::LocalTabGroupID> UnwrapGroupId(
     const ActivityLogItem& item) {
@@ -389,24 +374,64 @@
   // Let hover button process events.
   label_container->SetCanProcessEventsWithinSubtree(false);
 
-  activity_text_ = item.title_text;
   auto* activity_label =
       label_container->AddChildView(std::make_unique<views::Label>());
-  activity_label->SetText(activity_text_);
+  activity_label->SetText(item.title_text);
   activity_label->SetTextStyle(views::style::TextStyle::STYLE_BODY_4_MEDIUM);
   activity_label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
 
-  metadata_text_ = GetMetadataText(item_);
-  auto* metadata_label =
-      label_container->AddChildView(std::make_unique<views::Label>());
-  metadata_label->SetText(metadata_text_);
-  metadata_label->SetTextStyle(views::style::TextStyle::STYLE_BODY_5);
-  metadata_label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
-  metadata_label->SetEnabledColor(ui::kColorSysOnSurfaceSubtle);
+  auto* metadata_container =
+      label_container->AddChildView(std::make_unique<views::View>());
+  auto* metadata_layout = metadata_container->SetLayoutManager(
+      std::make_unique<views::FlexLayout>());
+  metadata_layout->SetOrientation(views::LayoutOrientation::kHorizontal);
+
+  auto* description_label = metadata_container->AddChildView(
+      std::make_unique<views::Label>(item.description_text));
+  description_label->SetTextStyle(views::style::TextStyle::STYLE_BODY_5);
+  description_label->SetHorizontalAlignment(
+      gfx::HorizontalAlignment::ALIGN_LEFT);
+  description_label->SetEnabledColor(ui::kColorSysOnSurfaceSubtle);
+
+  // The email will be elided by using up all available space in the layout.
+  description_label->SetProperty(
+      views::kFlexBehaviorKey,
+      views::FlexSpecification(views::MinimumFlexSizeRule::kScaleToZero,
+                               views::MaximumFlexSizeRule::kPreferred));
+
+  // The time text is not elided and takes up as much space as possible. It only
+  // has a delimiter if there is a description.
+  std::u16string time_text;
+  if (item.description_text.size() > 0) {
+    time_text += kBulletPoint + u" ";
+  }
+  time_text += item_.time_delta_text;
+
+  auto* time_label = metadata_container->AddChildView(
+      std::make_unique<views::Label>(time_text));
+  time_label->SetTextStyle(views::style::TextStyle::STYLE_BODY_5);
+  time_label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_RIGHT);
+  time_label->SetEnabledColor(ui::kColorSysOnSurfaceSubtle);
+
+  // The time value will be completely shown on the right.
+  time_label->SetProperty(
+      views::kFlexBehaviorKey,
+      views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred,
+                               views::MaximumFlexSizeRule::kPreferred));
+
+  // Add extra padding matching the ImageView on the left.
+  gfx::Insets margins;
+  margins.set_right(ChromeLayoutProvider::Get()
+                        ->GetInsetsMetric(INSETS_RECENT_ACTIVITY_IMAGE_MARGIN)
+                        .left());
+  time_label->SetProperty(views::kMarginsKey, margins);
 
   GetViewAccessibility().SetRole(ax::mojom::Role::kRow);
-  GetViewAccessibility().SetName(activity_text_);
-  GetViewAccessibility().SetDescription(metadata_text_);
+  GetViewAccessibility().SetName(item.title_text);
+  GetViewAccessibility().SetDescription((item_.description_text.size() > 0
+                                             ? item_.description_text + u" "
+                                             : u"") +
+                                        time_text);
   SetFocusBehavior(FocusBehavior::ALWAYS);
   SetFocusBehavior(views::PlatformStyle::kDefaultFocusBehavior);
   SetEnabled(GetActionEnabledForItem(item_));
diff --git a/chrome/browser/ui/views/tabs/recent_activity_bubble_dialog_view.h b/chrome/browser/ui/views/tabs/recent_activity_bubble_dialog_view.h
index a042299..3898e4a 100644
--- a/chrome/browser/ui/views/tabs/recent_activity_bubble_dialog_view.h
+++ b/chrome/browser/ui/views/tabs/recent_activity_bubble_dialog_view.h
@@ -95,8 +95,6 @@
   void ButtonPressed();
 
   RecentActivityRowImageView* image_view() const { return image_view_; }
-  const std::u16string& activity_text() const { return activity_text_; }
-  const std::u16string& metadata_text() const { return metadata_text_; }
 
   // RecentActivityAction handlers.
   // Focuses the open tab in the tab strip.
@@ -109,8 +107,6 @@
   void ManageSharing();
 
  private:
-  std::u16string activity_text_;
-  std::u16string metadata_text_;
   raw_ptr<RecentActivityRowImageView> image_view_ = nullptr;
   ActivityLogItem item_;
   const raw_ptr<Profile> profile_ = nullptr;
diff --git a/chrome/browser/ui/views/tabs/recent_activity_bubble_dialog_view_browsertest.cc b/chrome/browser/ui/views/tabs/recent_activity_bubble_dialog_view_browsertest.cc
index a57368a..eccd3a63 100644
--- a/chrome/browser/ui/views/tabs/recent_activity_bubble_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/tabs/recent_activity_bubble_dialog_view_browsertest.cc
@@ -324,27 +324,29 @@
   EXPECT_EQ(bubble->GetWindowTitle(), u"Recent activity");
 #endif
 
-  EXPECT_EQ(bubble->GetRowForTesting(0)->activity_text(), u"You changed a tab");
-  EXPECT_EQ(bubble->GetRowForTesting(0)->metadata_text(),
+  EXPECT_EQ(bubble->GetRowForTesting(0)->GetAccessibleName(),
+            u"You changed a tab");
+  EXPECT_EQ(bubble->GetRowForTesting(0)->GetAccessibleDescription(),
             u"airbnb.com \u2022 5h ago");
 
-  EXPECT_EQ(bubble->GetRowForTesting(1)->activity_text(),
+  EXPECT_EQ(bubble->GetRowForTesting(1)->GetAccessibleName(),
             u"Shirley changed a tab");
-  EXPECT_EQ(bubble->GetRowForTesting(1)->metadata_text(),
+  EXPECT_EQ(bubble->GetRowForTesting(1)->GetAccessibleDescription(),
             u"hotels.com \u2022 4h ago");
 
-  EXPECT_EQ(bubble->GetRowForTesting(2)->activity_text(),
+  EXPECT_EQ(bubble->GetRowForTesting(2)->GetAccessibleName(),
             u"Elisa removed a tab");
-  EXPECT_EQ(bubble->GetRowForTesting(2)->metadata_text(),
+  EXPECT_EQ(bubble->GetRowForTesting(2)->GetAccessibleDescription(),
             u"expedia.com \u2022 6h ago");
 
-  EXPECT_EQ(bubble->GetRowForTesting(3)->activity_text(),
+  EXPECT_EQ(bubble->GetRowForTesting(3)->GetAccessibleName(),
             u"Shirley joined the group");
-  EXPECT_EQ(bubble->GetRowForTesting(3)->metadata_text(),
+  EXPECT_EQ(bubble->GetRowForTesting(3)->GetAccessibleDescription(),
             u"shirleys-email \u2022 8h ago");
 
-  EXPECT_EQ(bubble->GetRowForTesting(4)->activity_text(), u"Elisa added a tab");
-  EXPECT_EQ(bubble->GetRowForTesting(4)->metadata_text(),
+  EXPECT_EQ(bubble->GetRowForTesting(4)->GetAccessibleName(),
+            u"Elisa added a tab");
+  EXPECT_EQ(bubble->GetRowForTesting(4)->GetAccessibleDescription(),
             u"expedia.com \u2022 2d ago");
 }
 
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 48fb991..c271096 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_action_container.cc
@@ -463,6 +463,7 @@
   }
 
   ExecuteHideTabStripNudge(glic_button_);
+  glic_button_->SetText(std::u16string());
 }
 
 void TabStripActionContainer::OnGlicButtonDismissed() {
diff --git a/chrome/browser/ui/views/toolbar/split_tabs_button.cc b/chrome/browser/ui/views/toolbar/split_tabs_button.cc
index 973d28ad..c81260d 100644
--- a/chrome/browser/ui/views/toolbar/split_tabs_button.cc
+++ b/chrome/browser/ui/views/toolbar/split_tabs_button.cc
@@ -14,15 +14,15 @@
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
-#include "chrome/browser/ui/tabs/split_tab_data.h"
 #include "chrome/browser/ui/tabs/split_tab_menu_model.h"
 #include "chrome/browser/ui/tabs/split_tab_util.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
 #include "components/prefs/pref_service.h"
+#include "components/tabs/public/split_tab_data.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "components/tabs/public/tab_interface.h"
 #include "ui/base/interaction/element_identifier.h"
 #include "ui/base/l10n/l10n_util.h"
diff --git a/chrome/browser/ui/views/toolbar/split_tabs_button_interactive_ui_test.cc b/chrome/browser/ui/views/toolbar/split_tabs_button_interactive_ui_test.cc
index 1c79efd..62a5597 100644
--- a/chrome/browser/ui/views/toolbar/split_tabs_button_interactive_ui_test.cc
+++ b/chrome/browser/ui/views/toolbar/split_tabs_button_interactive_ui_test.cc
@@ -9,9 +9,7 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
-#include "chrome/browser/ui/tabs/split_tab_collection.h"
 #include "chrome/browser/ui/tabs/split_tab_menu_model.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_menu_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
@@ -27,6 +25,8 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "chrome/test/interaction/interactive_browser_test.h"
 #include "components/prefs/pref_service.h"
+#include "components/tabs/public/split_tab_collection.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "content/public/test/browser_test.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
diff --git a/chrome/browser/ui/views/web_apps/web_app_icon_name_and_origin_view.cc b/chrome/browser/ui/views/web_apps/web_app_icon_name_and_origin_view.cc
index c572ec2..a3dd763 100644
--- a/chrome/browser/ui/views/web_apps/web_app_icon_name_and_origin_view.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_icon_name_and_origin_view.cc
@@ -11,13 +11,21 @@
 #include "base/strings/string_util.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
 #include "chrome/browser/ui/views/web_apps/web_app_views_utils.h"
+#include "chrome/browser/ui/web_applications/web_app_dialogs.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
 #include "ui/gfx/image/image_skia.h"
 #include "ui/views/controls/image_view.h"
 #include "ui/views/layout/box_layout.h"
 #include "ui/views/layout/layout_provider.h"
+#include "ui/views/view_class_properties.h"
 #include "url/gurl.h"
 
+namespace web_app {
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kSimpleInstallDialogAppTitle);
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kSimpleInstallDialogIconView);
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kSimpleInstallDialogOriginLabel);
+}  // namespace web_app
+
 std::unique_ptr<WebAppIconNameAndOriginView>
 WebAppIconNameAndOriginView::Create(const gfx::ImageSkia& icon_image,
                                     std::u16string app_title,
@@ -41,6 +49,8 @@
 
   auto icon_view = std::make_unique<views::ImageView>();
   icon_view->SetImage(ui::ImageModel::FromImageSkia(icon_image));
+  icon_view->SetProperty(views::kElementIdentifierKey,
+                         web_app::kSimpleInstallDialogIconView);
   AddChildViewRaw(icon_view.release());
 
   views::View* labels = new views::View();
@@ -48,9 +58,16 @@
   labels->SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kVertical));
 
-  labels->AddChildViewRaw(web_app::CreateNameLabel(app_title).release());
-  labels->AddChildViewRaw(
-      web_app::CreateOriginLabelFromStartUrl(start_url, false).release());
+  auto name_label = web_app::CreateNameLabel(app_title);
+  name_label->SetProperty(views::kElementIdentifierKey,
+                          web_app::kSimpleInstallDialogAppTitle);
+  labels->AddChildView(std::move(name_label));
+
+  auto origin_label = web_app::CreateOriginLabelFromStartUrl(
+      start_url, /*is_primary_text=*/false);
+  origin_label->SetProperty(views::kElementIdentifierKey,
+                            web_app::kSimpleInstallDialogOriginLabel);
+  labels->AddChildView(std::move(origin_label));
 }
 
 BEGIN_METADATA(WebAppIconNameAndOriginView)
diff --git a/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc b/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc
index 46743ab9..91d459f 100644
--- a/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc
+++ b/chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc
@@ -148,8 +148,8 @@
   }
 }
 
-void SetAutoAcceptPWAInstallConfirmationForTesting(bool auto_accept) {
-  g_auto_accept_pwa_for_testing = auto_accept;
+base::AutoReset<bool> SetAutoAcceptPWAInstallConfirmationForTesting() {
+  return base::AutoReset<bool>(&g_auto_accept_pwa_for_testing, true);
 }
 
 base::AutoReset<bool> SetDontCloseOnDeactivateForTesting() {
diff --git a/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc b/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc
index 44e20b2..543b1c30 100644
--- a/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/webid/account_selection_bubble_view_unittest.cc
@@ -697,7 +697,7 @@
   CreateAccountSelectionBubble();
 
   // Set the dialog background color to white.
-  dialog()->set_background_color(SK_ColorWHITE);
+  dialog()->SetBackgroundColor(SK_ColorWHITE);
 
   const std::string kDarkBlue = "#1a73e8";
   SkColor bg_color;
@@ -730,7 +730,7 @@
   CreateAccountSelectionBubble();
 
   // Set the dialog background color to white.
-  dialog()->set_background_color(SK_ColorWHITE);
+  dialog()->SetBackgroundColor(SK_ColorWHITE);
 
   const std::string kWhite = "#fff";
   SkColor bg_color;
diff --git a/chrome/browser/ui/web_applications/web_app_dialog_utils.cc b/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
index 6118577..6ae9a30 100644
--- a/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
+++ b/chrome/browser/ui/web_applications/web_app_dialog_utils.cc
@@ -272,6 +272,24 @@
   return true;
 }
 
+void CreateWebAppForBackgroundInstall(
+    content::WebContents* initiating_web_contents,
+    std::unique_ptr<webapps::MlInstallOperationTracker> tracker,
+    const GURL& install_url,
+    const std::optional<GURL>& manifest_id,
+    WebAppInstalledCallback installed_callback) {
+  auto* provider = WebAppProvider::GetForWebContents(initiating_web_contents);
+  CHECK(provider);
+
+  provider->scheduler().InstallAppFromUrl(
+      install_url, manifest_id, initiating_web_contents->GetWeakPtr(),
+      base::BindOnce(&OnWebAppInstallShowInstallDialog,
+                     WebAppInstallFlow::kInstallSite,
+                     webapps::WebappInstallSource::WEB_INSTALL,
+                     PwaInProductHelpState::kNotShown, std::move(tracker)),
+      std::move(installed_callback));
+}
+
 void ShowPwaInstallDialog(Browser* browser) {
   CHECK(browser);
 
diff --git a/chrome/browser/ui/web_applications/web_app_dialog_utils.h b/chrome/browser/ui/web_applications/web_app_dialog_utils.h
index ee88a61..46b626d 100644
--- a/chrome/browser/ui/web_applications/web_app_dialog_utils.h
+++ b/chrome/browser/ui/web_applications/web_app_dialog_utils.h
@@ -51,6 +51,17 @@
     WebAppInstalledCallback installed_callback,
     PwaInProductHelpState iph_state = PwaInProductHelpState::kNotShown);
 
+// Starts the background install of a WebApp at `install_url`, initiated from a
+// `navigator.install` call from within `initiating_web_contents`. This must be
+// called from a context where `WebAppProvider` exists and is supported.
+// Used for the Web Install API.
+void CreateWebAppForBackgroundInstall(
+    content::WebContents* initiating_web_contents,
+    std::unique_ptr<webapps::MlInstallOperationTracker> tracker,
+    const GURL& install_url,
+    const std::optional<GURL>& manifest_id,
+    WebAppInstalledCallback installed_callback);
+
 // Shows the PWA Install dialog for the active tab in the provided browser.
 // Records PWAInstallIcon user metric and closes the PWA install IPH
 // if it is showing.
diff --git a/chrome/browser/ui/web_applications/web_app_dialogs.h b/chrome/browser/ui/web_applications/web_app_dialogs.h
index e5a8b3cc..2ce0e37d 100644
--- a/chrome/browser/ui/web_applications/web_app_dialogs.h
+++ b/chrome/browser/ui/web_applications/web_app_dialogs.h
@@ -127,6 +127,10 @@
   kNotShown
 };
 
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kSimpleInstallDialogAppTitle);
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kSimpleInstallDialogIconView);
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kSimpleInstallDialogOriginLabel);
+
 // Shows the PWA installation confirmation bubble anchored off the PWA install
 // icon in the omnibox.
 //
@@ -165,7 +169,7 @@
 
 // Sets whether |ShowSimpleInstallDialogForWebApps| should accept immediately
 // without any user interaction.
-void SetAutoAcceptPWAInstallConfirmationForTesting(bool auto_accept);
+base::AutoReset<bool> SetAutoAcceptPWAInstallConfirmationForTesting();
 
 // Sets whether |ShowDiyInstallDialogForWebApps| should accept immediately
 // without any user interaction.
diff --git a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
index 1a567b40..a7e8a8a 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
+++ b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.cc
@@ -53,6 +53,7 @@
 #include "components/user_education/common/feature_promo/feature_promo_result.h"
 #include "components/user_education/common/user_education_data.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/installable/ml_install_operation_tracker.h"
 #include "components/webapps/browser/uninstall_result_code.h"
 #include "components/webapps/common/web_app_id.h"
 #include "content/public/browser/clear_site_data_utils.h"
@@ -424,6 +425,17 @@
   web_app::CreateWebAppFromManifest(web_contents, source, std::move(callback));
 }
 
+void WebAppUiManagerImpl::TriggerInstallDialogForBackgroundInstall(
+    content::WebContents* initiating_web_contents,
+    std::unique_ptr<webapps::MlInstallOperationTracker> tracker,
+    const GURL& install_url,
+    const std::optional<GURL>& manifest_id,
+    InstallCallback callback) {
+  web_app::CreateWebAppForBackgroundInstall(initiating_web_contents,
+                                            std::move(tracker), install_url,
+                                            manifest_id, std::move(callback));
+}
+
 void WebAppUiManagerImpl::PresentUserUninstallDialog(
     const webapps::AppId& app_id,
     webapps::WebappUninstallSource uninstall_source,
diff --git a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.h b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.h
index 5eee8be973..c8155c84 100644
--- a/chrome/browser/ui/web_applications/web_app_ui_manager_impl.h
+++ b/chrome/browser/ui/web_applications/web_app_ui_manager_impl.h
@@ -137,6 +137,12 @@
   void TriggerInstallDialog(content::WebContents* web_contents,
                             webapps::WebappInstallSource source,
                             InstallCallback callback) override;
+  void TriggerInstallDialogForBackgroundInstall(
+      content::WebContents* initiating_web_contents,
+      std::unique_ptr<webapps::MlInstallOperationTracker> tracker,
+      const GURL& install_url,
+      const std::optional<GURL>& manifest_id,
+      InstallCallback callback) override;
 
   void PresentUserUninstallDialog(
       const webapps::AppId& app_id,
diff --git a/chrome/browser/ui/webid/identity_dialog_controller.cc b/chrome/browser/ui/webid/identity_dialog_controller.cc
index 64e1426..1f8262d 100644
--- a/chrome/browser/ui/webid/identity_dialog_controller.cc
+++ b/chrome/browser/ui/webid/identity_dialog_controller.cc
@@ -402,7 +402,7 @@
 }
 
 void IdentityDialogController::CollectTrainingData(UserAction user_action) {
-  if (!training_request_id_ || !segmentation_platform_service_) {
+  if (!training_request_id_.has_value() || !segmentation_platform_service_) {
     return;
   }
 
diff --git a/chrome/browser/ui/webui/ash/settings/pages/about/about_section.cc b/chrome/browser/ui/webui/ash/settings/pages/about/about_section.cc
index 76c6d26a..851e3ef5 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/about/about_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/about/about_section.cc
@@ -255,6 +255,8 @@
       {"aboutDiagnostics", IDS_SETTINGS_ABOUT_PAGE_DIAGNOSTICS},
       {"aboutDiagnosticseDescription", IDS_OS_SETTINGS_DIAGNOSTICS_DESCRIPTION},
       {"aboutFirmwareUpdates", IDS_SETTINGS_ABOUT_PAGE_FIRMWARE_UPDATES},
+      {"aboutFirmwareUpdatesDisabledDescription",
+       IDS_OS_SETTINGS_FIRMWARE_DISABLED_DESCRIPTION},
       {"aboutFirmwareUpToDateDescription",
        IDS_OS_SETTINGS_FIRMWARE_UP_TO_DATE_DESCRIPTION},
       {"aboutFirmwareUpdateAvailableDescription",
diff --git a/chrome/browser/ui/webui/ash/settings/pages/device/inputs_section.cc b/chrome/browser/ui/webui/ash/settings/pages/device/inputs_section.cc
index 42696aa5..0ed54c2 100644
--- a/chrome/browser/ui/webui/ash/settings/pages/device/inputs_section.cc
+++ b/chrome/browser/ui/webui/ash/settings/pages/device/inputs_section.cc
@@ -471,6 +471,14 @@
       {"languagesDictionaryDownloadRetryDescription",
        IDS_OS_SETTINGS_LANGUAGES_DICTIONARY_DOWNLOAD_RETRY_DESCRIPTION},
       {"editDictionaryLabel", IDS_OS_SETTINGS_LANGUAGES_EDIT_DICTIONARY_LABEL},
+      {"japaneseClearPersonalizationData",
+       IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_CLEAR_PERSONALIZATION_DATA},
+      {"japaneseDeleteItems",
+       IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_DELETE_ITEMS},
+      {"japaneseConversationHistory",
+       IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_CONVERSATION_HISTORY},
+      {"japaneseSuggestionHistory",
+       IDS_SETTINGS_INPUT_METHOD_OPTIONS_JAPANESE_SUGGESTION_HISTORY},
       {"japaneseManageUserDictionaryLabel",
        IDS_OS_SETTINGS_LANGUAGES_JAPANESE_MANAGE_USER_DICTIONARY_LABEL},
       {"japaneseDictionary",
diff --git a/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc b/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc
index 40f8a81c..2e71397 100644
--- a/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc
+++ b/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.cc
@@ -34,6 +34,11 @@
 
 #if BUILDFLAG(IS_ANDROID)
 #include "chrome/browser/password_manager/android/password_manager_eviction_util.h"
+#else
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "components/autofill/content/browser/content_autofill_driver.h"
 #endif
 
 using autofill::LogRouter;
@@ -130,6 +135,10 @@
       "resetUpmEviction",
       base::BindRepeating(&InternalsUIHandler::OnResetUpmEviction,
                           base::Unretained(this)));
+#else
+  web_ui()->RegisterMessageCallback(
+      "setDomNodeId", base::BindRepeating(&InternalsUIHandler::SetDomNodeId,
+                                          base::Unretained(this)));
 #endif
 }
 
@@ -253,6 +262,24 @@
   FireWebUIListener("enable-reset-upm-eviction-button",
                     base::Value(!is_user_unenrolled));
 }
+#else
+void InternalsUIHandler::SetDomNodeId(const base::Value::List& args) {
+  for (auto* browser : GetAllBrowserWindowInterfaces()) {
+    if (!browser->GetTabStripModel()) {
+      continue;
+    }
+
+    for (int i = 0; i < browser->GetTabStripModel()->count(); i++) {
+      auto* web_contents = browser->GetTabStripModel()->GetWebContentsAt(i);
+      autofill::AutofillDriver* driver =
+          ContentAutofillDriver::GetForRenderFrameHost(
+              web_contents->GetPrimaryMainFrame());
+      if (driver) {
+        driver->ExposeDomNodeIDs();
+      }
+    }
+  }
+}
 #endif
 
 void InternalsUIHandler::StartSubscription() {
diff --git a/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.h b/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.h
index 1f1abeb9..8f8936cf 100644
--- a/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.h
+++ b/chrome/browser/ui/webui/autofill_and_password_manager_internals/internals_ui_handler.h
@@ -92,7 +92,11 @@
   void OnGetAutofillAiCache(const base::Value::List& args);
   void OnLoaded(const base::Value::List& args);
   void OnResetCache(const base::Value::List& args);
+#if BUILDFLAG(IS_ANDROID)
   void OnResetUpmEviction(const base::Value::List& args);
+#else
+  void SetDomNodeId(const base::Value::List& args);
+#endif
 
   void OnResetCacheDone(const std::string& message);
 
diff --git a/chrome/browser/ui/webui/bookmarks/bookmarks_ui.cc b/chrome/browser/ui/webui/bookmarks/bookmarks_ui.cc
index 469efd3..609936a6 100644
--- a/chrome/browser/ui/webui/bookmarks/bookmarks_ui.cc
+++ b/chrome/browser/ui/webui/bookmarks/bookmarks_ui.cc
@@ -9,7 +9,6 @@
 #include <string>
 #include <utility>
 
-#include "base/feature_list.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.h"
@@ -58,8 +57,6 @@
   source->AddString("undoDescription", l10n_util::GetStringFUTF16(
                                            IDS_UNDO_DESCRIPTION,
                                            undo_accelerator.GetShortcutText()));
-  source->AddBoolean("splitViewEnabled",
-                     base::FeatureList::IsEnabled(features::kSideBySide));
 
   // Localized strings (alphabetical order).
   static constexpr webui::LocalizedString kStrings[] = {
@@ -114,7 +111,6 @@
       {"menuOpenNewTabGroup", IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_NEW_TAB_GROUP},
       {"menuOpenNewWindow", IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_NEW_WINDOW},
       {"menuOpenIncognito", IDS_BOOKMARK_MANAGER_MENU_OPEN_INCOGNITO},
-      {"menuOpenSplitView", IDS_BOOKMARK_MANAGER_MENU_OPEN_IN_SPLIT_VIEW},
       {"menuRename", IDS_BOOKMARK_MANAGER_MENU_RENAME},
       {"menuShowInFolder", IDS_BOOKMARK_MANAGER_MENU_SHOW_IN_FOLDER},
       {"menuSort", IDS_BOOKMARK_MANAGER_MENU_SORT},
diff --git a/chrome/browser/ui/webui/discards/discards_ui.cc b/chrome/browser/ui/webui/discards/discards_ui.cc
index 6d6dd2b2..4cb1277 100644
--- a/chrome/browser/ui/webui/discards/discards_ui.cc
+++ b/chrome/browser/ui/webui/discards/discards_ui.cc
@@ -151,7 +151,7 @@
               page_node, DiscardEligibilityPolicy::DiscardReason::URGENT);
       candidates.emplace_back(page_node->GetWeakPtr(), can_discard_result,
                               page_node->IsVisible(), page_node->IsFocused(),
-                              page_node->GetTimeSinceLastVisibilityChange());
+                              page_node->GetLastVisibilityChangeTime());
     }
 
     // Sorts with ascending importance.
diff --git a/chrome/browser/ui/webui/new_tab_footer/BUILD.gn b/chrome/browser/ui/webui/new_tab_footer/BUILD.gn
index 2270c12..c509cd82 100644
--- a/chrome/browser/ui/webui/new_tab_footer/BUILD.gn
+++ b/chrome/browser/ui/webui/new_tab_footer/BUILD.gn
@@ -23,8 +23,12 @@
     "new_tab_footer_handler.cc",
     "new_tab_footer_ui.cc",
   ]
+
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
+
   deps = [
     ":new_tab_footer",
+    "//chrome/browser:browser_process",
     "//chrome/browser/extensions",
     "//chrome/browser/profiles",
     "//chrome/browser/resources/new_tab_footer:resources_grit",
@@ -40,6 +44,7 @@
   deps = [
     ":new_tab_footer",
     "//base/test:test_support",
+    "//chrome/browser/ui:ui_features",
     "//chrome/test:test_support",
   ]
 }
diff --git a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer.mojom b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer.mojom
index cf9a315..4ea73ef 100644
--- a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer.mojom
+++ b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer.mojom
@@ -14,12 +14,20 @@
   string name;
 };
 
+// Enterprise browser management notice
+struct ManagementNotice {
+  // Human readable string for notice text. This will indicate that the browser
+  // is managed and the entity managing the browser, which is  customizable via
+  // the `EnterpriseCustomLabel` policy.
+  string text;
+};
+
 // Used by the WebUI document to bootstrap bidirectional communication.
 interface NewTabFooterHandlerFactory {
   // The WebUI page's |BrowserProxy| singleton calls this method when the
   // document is first initialized.
   CreateNewTabFooterHandler(pending_remote<NewTabFooterDocument> document,
-                    pending_receiver<NewTabFooterHandler> handler);
+                            pending_receiver<NewTabFooterHandler> handler);
 };
 
 // Browser-side handler for requests from WebUI document.
@@ -27,7 +35,14 @@
   // Gets the attribution for an extension overriding the new tab
   // page, if there is one.
   GetNtpExtensionAttribution() => (ExtensionAttribution? attribution);
+
+  // Requests an update to the enterprise management notice for a managed
+  // browser.
+  UpdateManagementNotice();
 };
 
 // WebUI-side handler for requests from the browser.
-interface NewTabFooterDocument {};
+interface NewTabFooterDocument {
+  // Sets the enterprise management notice for a managed browser.
+  SetManagementNotice(ManagementNotice? notice);
+};
diff --git a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler.cc b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler.cc
index b18d6ed..0f5438b0 100644
--- a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler.cc
+++ b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler.cc
@@ -6,11 +6,24 @@
 
 #include <utility>
 
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/enterprise/util/managed_browser_utils.h"
 #include "chrome/browser/extensions/settings_api_helpers.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/managed_ui.h"
 #include "chrome/browser/ui/webui/new_tab_footer/new_tab_footer.mojom.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/common/webui_url_constants.h"
+#include "chrome/grit/branded_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h"
+#include "components/prefs/pref_service.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/web_contents.h"
 #include "net/base/url_util.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/webui/web_ui_util.h"
 
 NewTabFooterHandler::NewTabFooterHandler(
     mojo::PendingReceiver<new_tab_footer::mojom::NewTabFooterHandler>
@@ -41,3 +54,34 @@
   attribution->name = ntp_extension->name();
   std::move(callback).Run(std::move(attribution));
 }
+
+void NewTabFooterHandler::UpdateManagementNotice() {
+  if (!enterprise_util::CanShowEnterpriseBadgingForNTPFooter(profile_)) {
+    document_->SetManagementNotice(nullptr);
+    return;
+  }
+
+  auto notice = new_tab_footer::mojom::ManagementNotice::New();
+  notice->text = GetManagementNoticeText();
+  document_->SetManagementNotice(std::move(notice));
+}
+
+std::string NewTabFooterHandler::GetManagementNoticeText() {
+  CHECK(enterprise_util::CanShowEnterpriseBadgingForNTPFooter(profile_));
+
+  // Return "Managed by <label>" if custom label is set.
+  std::string custom_label = g_browser_process->local_state()->GetString(
+      prefs::kEnterpriseCustomLabelForBrowser);
+  if (!custom_label.empty()) {
+    return l10n_util::GetStringFUTF8(IDS_MANAGED_BY,
+                                     base::UTF8ToUTF16(custom_label));
+  }
+
+  // Return "Managed by <management domain>" if a cloud manager is known.
+  // Otherwise return the generic "Managed by your organization" message.
+  std::optional<std::string> cloud_policy_manager = GetDeviceManagerIdentity();
+  return cloud_policy_manager && !cloud_policy_manager->empty()
+             ? l10n_util::GetStringFUTF8(
+                   IDS_MANAGED_BY, base::UTF8ToUTF16(*cloud_policy_manager))
+             : l10n_util::GetStringUTF8(IDS_MANAGED);
+}
diff --git a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler.h b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler.h
index d03c112..4409e12 100644
--- a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler.h
+++ b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler.h
@@ -31,8 +31,11 @@
   // new_tab_footer::mojom::NewTabFooterHandler:
   void GetNtpExtensionAttribution(
       GetNtpExtensionAttributionCallback callback) override;
+  void UpdateManagementNotice() override;
 
  private:
+  std::string GetManagementNoticeText();
+
   const raw_ptr<Profile> profile_;
   raw_ptr<content::WebContents> web_contents_;
   mojo::Remote<new_tab_footer::mojom::NewTabFooterDocument> document_;
diff --git a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler_unittest.cc b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler_unittest.cc
index 91c349b..1c93bdb9 100644
--- a/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler_unittest.cc
+++ b/chrome/browser/ui/webui/new_tab_footer/new_tab_footer_handler_unittest.cc
@@ -20,6 +20,40 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/enterprise/browser_management/management_service_factory.h"
+#include "chrome/browser/ui/managed_ui.h"
+#include "chrome/browser/ui/ui_features.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/policy/core/common/management/scoped_management_service_override_for_testing.h"
+#include "content/public/test/browser_task_environment.h"
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+
+using testing::_;
+
+class MockNewTabFooterDocument
+    : public new_tab_footer::mojom::NewTabFooterDocument {
+ public:
+  MockNewTabFooterDocument() = default;
+  ~MockNewTabFooterDocument() override = default;
+
+  mojo::PendingRemote<new_tab_footer::mojom::NewTabFooterDocument>
+  BindAndGetRemote() {
+    DCHECK(!receiver_.is_bound());
+    return receiver_.BindNewPipeAndPassRemote();
+  }
+
+  void FlushForTesting() { receiver_.FlushForTesting(); }
+
+  MOCK_METHOD(void,
+              SetManagementNotice,
+              (new_tab_footer::mojom::ManagementNoticePtr));
+  mojo::Receiver<new_tab_footer::mojom::NewTabFooterDocument> receiver_{this};
+};
+
 class NewTabFooterHandlerExtensionTest
     : public extensions::ExtensionServiceTestBase {
  public:
@@ -37,7 +71,7 @@
 
   NewTabFooterHandler& handler() { return *handler_; }
 
- private:
+ protected:
   std::unique_ptr<content::WebContents> web_contents_;
   std::unique_ptr<NewTabFooterHandler> handler_;
 };
@@ -90,3 +124,104 @@
             net::AppendOrReplaceQueryParameter(
                 GURL(chrome::kChromeUIExtensionsURL), "id", extension->id()));
 }
+
+#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
+class NewTabFooterHandlerEnterpriseTest : public testing::Test {
+ public:
+  void SetUp() override {
+    profile_manager_ = std::make_unique<TestingProfileManager>(
+        TestingBrowserProcess::GetGlobal());
+    ASSERT_TRUE(profile_manager_->SetUp());
+    profile_ = profile_manager_->CreateTestingProfile("Test Profile");
+    web_contents_ = content::WebContents::Create(
+        content::WebContents::CreateParams(profile_));
+    feature_list_.InitAndEnableFeature(
+        features::kEnterpriseBadgingForNtpFooter);
+    handler_ = std::make_unique<NewTabFooterHandler>(
+        mojo::PendingReceiver<new_tab_footer::mojom::NewTabFooterHandler>(),
+        document_.BindAndGetRemote(), web_contents_.get());
+  }
+
+  void TearDown() override {
+    // Ensure that the handler is destroyed before the profile.
+    handler_.reset();
+    web_contents_.reset();
+    profile_ = nullptr;
+    profile_manager_->DeleteAllTestingProfiles();
+    profile_manager_.reset();
+  }
+
+  NewTabFooterHandler& handler() { return *handler_; }
+  Profile* profile() { return profile_; }
+
+ protected:
+  content::BrowserTaskEnvironment task_environment_;
+  std::unique_ptr<content::WebContents> web_contents_;
+  std::unique_ptr<NewTabFooterHandler> handler_;
+  std::unique_ptr<TestingProfileManager> profile_manager_;
+  raw_ptr<TestingProfile> profile_;
+  testing::NiceMock<MockNewTabFooterDocument> document_;
+  base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_F(NewTabFooterHandlerEnterpriseTest, SetManagementNoticeWithDefaultText) {
+  // Simulate browser management.
+  policy::ScopedManagementServiceOverrideForTesting
+      profile_supervised_management(
+          policy::ManagementServiceFactory::GetForProfile(profile()),
+          policy::EnterpriseManagementAuthority::DOMAIN_LOCAL);
+
+  // Browser management is local, so no domain is indicated in the managent
+  // notice text.
+  EXPECT_CALL(document_, SetManagementNotice)
+      .WillOnce([](new_tab_footer::mojom::ManagementNoticePtr notice) {
+        EXPECT_EQ("Managed by your organization", notice->text);
+      });
+  handler().UpdateManagementNotice();
+
+  document_.FlushForTesting();
+  testing::Mock::VerifyAndClearExpectations(&document_);
+}
+
+TEST_F(NewTabFooterHandlerEnterpriseTest, SetManagementNoticeWithCustomtext) {
+  // Simulate browser management.
+  policy::ScopedManagementServiceOverrideForTesting
+      profile_supervised_management(
+          policy::ManagementServiceFactory::GetForProfile(profile()),
+          policy::EnterpriseManagementAuthority::DOMAIN_LOCAL);
+
+  // Set a custom label policy, which will be used in the management notice
+  // text.
+  profile_manager_->local_state()->Get()->SetString(
+      prefs::kEnterpriseCustomLabelForBrowser, "custom label");
+  EXPECT_CALL(document_, SetManagementNotice)
+      .WillOnce([](new_tab_footer::mojom::ManagementNoticePtr notice) {
+        EXPECT_EQ("Managed by custom label", notice->text);
+      });
+  handler().UpdateManagementNotice();
+
+  document_.FlushForTesting();
+  testing::Mock::VerifyAndClearExpectations(&document_);
+}
+
+TEST_F(NewTabFooterHandlerEnterpriseTest, SetManagementNoticeWithDomainText) {
+  // Simulate browser management.
+  const std::string managing_domain = "example.com";
+
+  // Simulate that the browser is managed by a cloud domain. The domain will be
+  // indicated in the management notice text.
+  ScopedDeviceManagerForTesting device_manager_for_testing(
+      managing_domain.c_str());
+  policy::ScopedManagementServiceOverrideForTesting profile_management(
+      policy::ManagementServiceFactory::GetForProfile(profile()),
+      policy::EnterpriseManagementAuthority::DOMAIN_LOCAL);
+  EXPECT_CALL(document_, SetManagementNotice)
+      .WillOnce([](new_tab_footer::mojom::ManagementNoticePtr notice) {
+        EXPECT_EQ("Managed by example.com", notice->text);
+      });
+  handler().UpdateManagementNotice();
+
+  document_.FlushForTesting();
+  testing::Mock::VerifyAndClearExpectations(&document_);
+}
+#endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc
index 3eaa1b39..15bf631 100644
--- a/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -71,6 +71,7 @@
 #include "chrome/browser/ui/webui/ash/extended_updates/extended_updates_dialog.h"
 #include "chrome/browser/ui/webui/help/help_utils_chromeos.h"
 #include "chrome/browser/ui/webui/help/version_updater_chromeos.h"
+#include "chromeos/ash/components/browser_context_helper/browser_context_helper.h"
 #include "chromeos/ash/components/dbus/update_engine/update_engine_client.h"
 #include "chromeos/ash/components/fwupd/firmware_update_manager.h"
 #include "chromeos/ash/components/network/network_state.h"
@@ -125,6 +126,25 @@
               : IDS_UPGRADE_NETWORK_LIST_CELLULAR_ALLOWED);
 }
 
+// Returns true if current user can change firmware, false otherwise.
+bool CanChangeFirmware(Profile* profile) {
+  if (policy::ManagementServiceFactory::GetForPlatform()->IsManaged()) {
+    bool value = false;
+    // On a managed machine we allow firmware changes only if enabled by policy
+    if (!ash::CrosSettings::Get()->GetBoolean(
+            ash::kDeviceUserInitiatedFirmwareUpdatesEnabled, &value)) {
+      // This can occur if the lookup for the policy's value fails,
+      // for example if the policy is not present on the current version.
+      // In this case, default to false.
+      LOG(ERROR) << "Failed to get device setting.";
+      return false;
+    }
+    return value;
+  }
+  return user_manager::UserManager::Get()->IsOwnerUser(
+      ash::BrowserContextHelper::Get()->GetUserByBrowserContext(profile));
+}
+
 // Returns true if current user can change channel, false otherwise.
 bool CanChangeChannel(Profile* profile) {
   if (policy::ManagementServiceFactory::GetForPlatform()->IsManaged()) {
@@ -339,6 +359,10 @@
       "getChannelInfo", base::BindRepeating(&AboutHandler::HandleGetChannelInfo,
                                             base::Unretained(this)));
   web_ui()->RegisterMessageCallback(
+      "canChangeFirmware",
+      base::BindRepeating(&AboutHandler::HandleCanChangeFirmware,
+                          base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
       "canChangeChannel",
       base::BindRepeating(&AboutHandler::HandleCanChangeChannel,
                           base::Unretained(this)));
@@ -618,6 +642,13 @@
                      weak_factory_.GetWeakPtr(), callback_id));
 }
 
+void AboutHandler::HandleCanChangeFirmware(const base::Value::List& args) {
+  CHECK_EQ(1U, args.size());
+  const std::string& callback_id = args[0].GetString();
+  ResolveJavascriptCallback(base::Value(callback_id),
+                            base::Value(CanChangeFirmware(profile_)));
+}
+
 void AboutHandler::HandleCanChangeChannel(const base::Value::List& args) {
   CHECK_EQ(1U, args.size());
   const std::string& callback_id = args[0].GetString();
diff --git a/chrome/browser/ui/webui/settings/about_handler.h b/chrome/browser/ui/webui/settings/about_handler.h
index 5cc5142..57401c61 100644
--- a/chrome/browser/ui/webui/settings/about_handler.h
+++ b/chrome/browser/ui/webui/settings/about_handler.h
@@ -109,6 +109,9 @@
   // Retrieves channel info.
   void HandleGetChannelInfo(const base::Value::List& args);
 
+  // Checks whether we can update the firmware.
+  void HandleCanChangeFirmware(const base::Value::List& args);
+
   // Checks whether we can change the current channel.
   void HandleCanChangeChannel(const base::Value::List& args);
 
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom
index e23bca0..8996d03 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom
@@ -111,6 +111,17 @@
   kShopping,
 };
 
+// Types of New Tabs that CustomizeChromePage can attach to.
+enum NewTabPageType {
+  kFirstPartyWebUI,
+  kThirdPartyWebUI,
+  kThirdPartyRemote,
+  kExtension,
+  kIncognito,
+  kGuestMode,
+  kNone,
+};
+
 // Used by the WebUI page to bootstrap bidirectional communication.
 interface CustomizeChromePageHandlerFactory {
   // The WebUI calls this method when the page is first initialized.
@@ -226,7 +237,7 @@
   // |CustomizeChromePageHandler.UpdateScrollToSection()|.
   ScrollToSection(CustomizeChromeSection section);
   // Sets Information about the tab that is attached to the CustomizeChromePage.
-  AttachedTabStateUpdated(bool is_source_tab_first_party_ntp);
+  AttachedTabStateUpdated(NewTabPageType ntp_type);
   // Sets the name of the system that manages the new tab page if there is one.
   // If not, empty string should be provided.
   NtpManagedByNameUpdated(string ntp_managed_by_name);
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_interactive_uitest.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_interactive_uitest.cc
index 13bc5ed..fa4acd6 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_interactive_uitest.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_interactive_uitest.cc
@@ -5,6 +5,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/browser/extensions/chrome_test_extension_loader.h"
+#include "chrome/browser/extensions/install_verifier.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_element_identifiers.h"
@@ -67,12 +68,12 @@
 
     extensions::ChromeTestExtensionLoader extension_loader(profile);
     extension_loader.set_ignore_manifest_warnings(true);
-    // TODO(temao) Not blocking the test, but note that LoadExtension()
-    // occasionally returns null.
-    extension_loader.LoadExtension(extension_dir.Pack()).get();
+    const extensions::Extension* extension =
+        extension_loader.LoadExtension(extension_dir.Pack()).get();
+    ASSERT_TRUE(extension);
   }
 
-  void OpenExtensionNewTabPage() {
+  void OpenNewTabPage() {
     chrome::NewTab(browser());
     content::WebContents* web_contents =
         browser()->tab_strip_model()->GetActiveWebContents();
@@ -84,6 +85,7 @@
 
  protected:
   base::test::ScopedFeatureList scoped_feature_list_;
+  extensions::ScopedInstallVerifierBypassForTest install_verifier_bypass_;
 };
 }  // namespace
 
@@ -98,8 +100,7 @@
   InstallExtension(browser()->profile());
   RunTestSequence(
       // 2. Open extension new tab page.
-      Do(base::BindLambdaForTesting(
-          [&, this]() { OpenExtensionNewTabPage(); })),
+      Do(base::BindLambdaForTesting([&, this]() { OpenNewTabPage(); })),
       // 3. Open customize chrome side panel.
       OpenCustomizeChromeSidePanel(kLocalCustomizeChromeElementId),
       // 4. Check edit theme is enabled in customize chrome side panel.
@@ -108,3 +109,38 @@
             WaitForElementToRender(kLocalCustomizeChromeElementId,
                                    kEditThemeButton)));
 }
+
+IN_PROC_BROWSER_TEST_F(CustomizeChromeInteractiveTest,
+                       ShowsFooterSectionForExtensionNtp) {
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kLocalCustomizeChromeElementId);
+  const DeepQuery kFooterSection = {"customize-chrome-app", "#footer",
+                                    "customize-chrome-footer",
+                                    "#showToggleContainer"};
+  // 1. Load extension that overrides NTP.
+  InstallExtension(browser()->profile());
+  RunTestSequence(
+      // 2. Open extension new tab page.
+      Do(base::BindLambdaForTesting([&, this]() { OpenNewTabPage(); })),
+      // 3. Open customize chrome side panel.
+      OpenCustomizeChromeSidePanel(kLocalCustomizeChromeElementId),
+      // 4. Check that the footer section exists.
+      Steps(
+          WaitForElementExists(kLocalCustomizeChromeElementId, kFooterSection),
+          WaitForElementToRender(kLocalCustomizeChromeElementId,
+                                 kFooterSection)));
+}
+
+IN_PROC_BROWSER_TEST_F(CustomizeChromeInteractiveTest,
+                       FooterSectionNotShownForNonExtensionNtp) {
+  DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kLocalCustomizeChromeElementId);
+  const DeepQuery kFooterSection = {"customize-chrome-app", "#footer",
+                                    "customize-chrome-footer",
+                                    "#showToggleContainer"};
+  RunTestSequence(
+      // 1. Open non-extension new tab page.
+      Do(base::BindLambdaForTesting([&, this]() { OpenNewTabPage(); })),
+      // 2. Open customize chrome side panel.
+      OpenCustomizeChromeSidePanel(kLocalCustomizeChromeElementId),
+      // 3. Check that the footer section does not exist.
+      EnsureNotPresent(kLocalCustomizeChromeElementId, kFooterSection));
+}
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
index 01fcf2f3..981d88d2 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.cc
@@ -29,7 +29,10 @@
 #include "chrome/browser/ui/color/chrome_color_id.h"
 #include "chrome/browser/ui/search/ntp_user_data_types.h"
 #include "chrome/browser/ui/views/side_panel/customize_chrome/customize_chrome_utils.h"
+#include "chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h"
 #include "chrome/browser/ui/webui/new_tab_page/ntp_pref_names.h"
+#include "chrome/browser/ui/webui/new_tab_page_third_party/new_tab_page_third_party_ui.h"
+#include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
 #include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_section.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/common/url_constants.h"
@@ -183,10 +186,9 @@
   page_->ScrollToSection(mojo_section);
 }
 
-void CustomizeChromePageHandler::AttachedTabStateUpdated(
-    bool is_source_tab_first_party_ntp) {
-  last_is_source_tab_first_party_ntp_ = is_source_tab_first_party_ntp;
-  page_->AttachedTabStateUpdated(is_source_tab_first_party_ntp);
+void CustomizeChromePageHandler::AttachedTabStateUpdated(const GURL& url) {
+  last_source_url_ = url;
+  page_->AttachedTabStateUpdated(GetNewTabPageType(url));
 }
 
 bool CustomizeChromePageHandler::IsNtpManagedByThirdPartySearchEngine() const {
@@ -539,7 +541,7 @@
 }
 
 void CustomizeChromePageHandler::UpdateAttachedTabState() {
-  AttachedTabStateUpdated(last_is_source_tab_first_party_ntp_);
+  AttachedTabStateUpdated(last_source_url_);
 }
 
 void CustomizeChromePageHandler::UpdateNtpManagedByName() {
@@ -726,3 +728,22 @@
   LogEvent(NTP_BACKGROUND_UPLOAD_CANCEL);
   std::move(choose_local_custom_background_callback_).Run(false);
 }
+
+side_panel::mojom::NewTabPageType CustomizeChromePageHandler::GetNewTabPageType(
+    const GURL& url) {
+  if (NewTabPageUI::IsNewTabPageOrigin(url)) {
+    return side_panel::mojom::NewTabPageType::kFirstPartyWebUI;
+  } else if (customize_chrome::IsExtensionNtp(url, profile_)) {
+    return side_panel::mojom::NewTabPageType::kExtension;
+  } else if (NewTabPageThirdPartyUI::IsNewTabPageOrigin(url)) {
+    return side_panel::mojom::NewTabPageType::kThirdPartyWebUI;
+  } else if (IsNtpManagedByThirdPartySearchEngine()) {
+    return side_panel::mojom::NewTabPageType::kThirdPartyRemote;
+  } else if (NewTabUI::IsNewTab(url)) {
+    return profile_->IsGuestSession()
+               ? side_panel::mojom::NewTabPageType::kGuestMode
+               : side_panel::mojom::NewTabPageType::kIncognito;
+  }
+
+  return side_panel::mojom::NewTabPageType::kNone;
+}
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h
index a128141..460c8dd 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler.h
@@ -21,6 +21,7 @@
 #include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome.mojom.h"
 #include "chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_section.h"
 #include "chrome/common/search/ntp_logging_events.h"
+#include "chrome/common/webui_url_constants.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/search_engines/template_url_service_observer.h"
 #include "components/themes/ntp_background_service.h"
@@ -90,7 +91,7 @@
   void ScrollToSection(CustomizeChromeSection section);
 
   // Passes AttachedTabStateUpdated calls to the CustomizeChromePage.
-  void AttachedTabStateUpdated(bool is_source_tab_first_party_ntp);
+  void AttachedTabStateUpdated(const GURL& url);
 
   // Helper method to determine if the search engine is overriding the first
   // party NTP.
@@ -145,6 +146,9 @@
 
   std::u16string GetManagingThirdPartyName() const;
 
+  // Returns the type of New Tab Page the SidePanel is attached to.
+  side_panel::mojom::NewTabPageType GetNewTabPageType(const GURL& url);
+
   // ui::NativeThemeObserver:
   void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
 
@@ -190,7 +194,7 @@
 
   // Caches the attached tab state provided to the handler, in cases where the
   // value needs to be requeried by the page.
-  bool last_is_source_tab_first_party_ntp_ = true;
+  GURL last_source_url_{GURL(chrome::kChromeUINewTabPageURL)};
 
   PrefChangeRegistrar pref_change_registrar_;
   base::ScopedObservation<ui::NativeTheme, ui::NativeThemeObserver>
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
index 43e2f3b..f0b26493 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_page_handler_unittest.cc
@@ -169,7 +169,9 @@
   MOCK_METHOD(void,
               ScrollToSection,
               (side_panel::mojom::CustomizeChromeSection));
-  MOCK_METHOD(void, AttachedTabStateUpdated, (bool));
+  MOCK_METHOD(void,
+              AttachedTabStateUpdated,
+              (side_panel::mojom::NewTabPageType));
   MOCK_METHOD(void, NtpManagedByNameUpdated, (const std::string&));
   MOCK_METHOD(void, SetFooterSettings, (bool visible));
 
@@ -810,18 +812,38 @@
   EXPECT_EQ(side_panel::mojom::CustomizeChromeSection::kAppearance, section);
 }
 
+// Ensures that url's are correctly mapped to their NewTabPage type.
+// Does not include test for `side_panel::mojom::NewTabPageType::kExtension`
+// See `CustomizeChromeInteractiveTest.FooterSectionForExtensionNtp` for
+// confirming Customize Chrome shows the footer section for Extension NTPs.
 TEST_F(CustomizeChromePageHandlerTest, AttachedTabStateUpdated) {
-  bool kIsSourceTabFirstPartyNtpValue = false;
+  std::vector<std::pair<side_panel::mojom::NewTabPageType, GURL>>
+      ntp_types_and_urls = {
+          {side_panel::mojom::NewTabPageType::kNone,
+           GURL("https://www.google.com/")},
+          {side_panel::mojom::NewTabPageType::kFirstPartyWebUI,
+           GURL(chrome::kChromeUINewTabPageURL)},
+          {side_panel::mojom::NewTabPageType::kThirdPartyWebUI,
+           GURL(chrome::kChromeUINewTabPageThirdPartyURL)},
+          {side_panel::mojom::NewTabPageType::kIncognito,
+           GURL(chrome::kChromeUINewTabURL)},
+          {side_panel::mojom::NewTabPageType::kGuestMode,
+           GURL(chrome::kChromeUINewTabURL)}};
 
-  bool isSourceTabFirstPartyNtp;
-  EXPECT_CALL(mock_page_, AttachedTabStateUpdated)
-      .Times(1)
-      .WillOnce(SaveArg<0>(&isSourceTabFirstPartyNtp));
+  for (const auto& ntp_type_and_url : ntp_types_and_urls) {
+    if (ntp_type_and_url.first ==
+        side_panel::mojom::NewTabPageType::kGuestMode) {
+      profile().SetGuestSession(true);
+    }
 
-  handler().AttachedTabStateUpdated(kIsSourceTabFirstPartyNtpValue);
-  mock_page_.FlushForTesting();
-
-  EXPECT_EQ(kIsSourceTabFirstPartyNtpValue, isSourceTabFirstPartyNtp);
+    side_panel::mojom::NewTabPageType source_tab;
+    EXPECT_CALL(mock_page_, AttachedTabStateUpdated)
+        .Times(1)
+        .WillOnce(SaveArg<0>(&source_tab));
+    handler().AttachedTabStateUpdated(ntp_type_and_url.second);
+    mock_page_.FlushForTesting();
+    EXPECT_EQ(ntp_type_and_url.first, source_tab);
+  }
 }
 
 TEST_F(CustomizeChromePageHandlerTest, ScrollToUnspecifiedSection) {
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc
index 1c9e0c1..05502f4 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.cc
@@ -305,13 +305,11 @@
   }
 }
 
-void CustomizeChromeUI::AttachedTabStateUpdated(
-    bool is_source_tab_first_party_ntp) {
+void CustomizeChromeUI::AttachedTabStateUpdated(const GURL& url) {
   if (customize_chrome_page_handler_) {
-    customize_chrome_page_handler_->AttachedTabStateUpdated(
-        is_source_tab_first_party_ntp);
+    customize_chrome_page_handler_->AttachedTabStateUpdated(url);
   } else {
-    is_source_tab_first_party_ntp_ = is_source_tab_first_party_ntp;
+    source_tab_url_ = url;
   }
 }
 
@@ -409,10 +407,8 @@
     customize_chrome_page_handler_->ScrollToSection(*section_);
     section_.reset();
   }
-  if (is_source_tab_first_party_ntp_.has_value()) {
-    customize_chrome_page_handler_->AttachedTabStateUpdated(
-        is_source_tab_first_party_ntp_.value());
-    is_source_tab_first_party_ntp_.reset();
+  if (!source_tab_url_.is_empty()) {
+    customize_chrome_page_handler_->AttachedTabStateUpdated(source_tab_url_);
   }
   if (is_theme_editable_.has_value()) {
     customize_chrome_page_handler_->UpdateThemeEditable(
diff --git a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h
index 8445464..33bc34fe 100644
--- a/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h
+++ b/chrome/browser/ui/webui/side_panel/customize_chrome/customize_chrome_ui.h
@@ -83,7 +83,7 @@
   void ScrollToSection(CustomizeChromeSection section);
 
   // Passthrough that calls the CustomizeChromePage's AttachedTabStateUpdated.
-  void AttachedTabStateUpdated(bool is_attached_tab_first_party_ntp);
+  void AttachedTabStateUpdated(const GURL& url);
 
   // Passthrough that calls to CustomizeChromePage's UpdateThemeEditable.
   void UpdateThemeEditable(bool is_theme_editable);
@@ -187,7 +187,7 @@
   // Caches a request to scroll to a section in case the request happens before
   // the front-end is ready to receive the request.
   std::optional<CustomizeChromeSection> section_;
-  std::optional<bool> is_source_tab_first_party_ntp_;
+  GURL source_tab_url_;
   std::optional<bool> is_theme_editable_;
 
   std::unique_ptr<user_education::HelpBubbleHandler> help_bubble_handler_;
diff --git a/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc b/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc
index 3463616..2c8b260 100644
--- a/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc
+++ b/chrome/browser/ui/webui/tab_search/tab_search_page_handler_unittest.cc
@@ -23,8 +23,6 @@
 #include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
 #include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h"
 #include "chrome/browser/ui/tabs/organization/tab_declutter_controller.h"
-#include "chrome/browser/ui/tabs/split_tab_data.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
 #include "chrome/browser/ui/tabs/tab_enums.h"
 #include "chrome/browser/ui/tabs/tab_utils.h"
 #include "chrome/browser/ui/tabs/test_tab_strip_model_delegate.h"
@@ -43,7 +41,9 @@
 #include "components/tab_groups/tab_group_color.h"
 #include "components/tab_groups/tab_group_id.h"
 #include "components/tab_groups/tab_group_visual_data.h"
+#include "components/tabs/public/split_tab_data.h"
 #include "components/tabs/public/split_tab_id.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "components/tabs/public/tab_interface.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_web_ui.h"
diff --git a/chrome/browser/ui/webui/version/version_ui.cc b/chrome/browser/ui/webui/version/version_ui.cc
index f9c2e60..c10d6937 100644
--- a/chrome/browser/ui/webui/version/version_ui.cc
+++ b/chrome/browser/ui/webui/version/version_ui.cc
@@ -260,8 +260,6 @@
       version_ui::kTargetSdkVersion,
       base::NumberToString(
           base::android::BuildInfo::GetInstance()->target_sdk_version()));
-  html_source->AddString(version_ui::kTargetsU,
-                         AndroidAboutAppInfo::GetTargetsUInfo());
   html_source->AddString(version_ui::kGmsVersion,
                          AndroidAboutAppInfo::GetGmsInfo());
   html_source->AddString(
diff --git a/chrome/browser/ui/webui/webui_allowlist_provider_unittest.cc b/chrome/browser/ui/webui/webui_allowlist_provider_unittest.cc
index 99ff3bb5..fdf7a61 100644
--- a/chrome/browser/ui/webui/webui_allowlist_provider_unittest.cc
+++ b/chrome/browser/ui/webui/webui_allowlist_provider_unittest.cc
@@ -276,31 +276,34 @@
   // cookies.
   EXPECT_TRUE(cookies_settings->IsFullCookieAccessAllowed(
       third_party_url, net::SiteForCookies::FromUrl(top_level_url),
-      url::Origin::Create(top_level_url), net::CookieSettingOverrides()));
+      url::Origin::Create(top_level_url), net::CookieSettingOverrides(),
+      /*cookie_partition_key=*/std::nullopt));
 
   // Allowlisted origin on its own can't use cookies.
   EXPECT_FALSE(cookies_settings->IsFullCookieAccessAllowed(
       third_party_url, net::SiteForCookies::FromUrl(third_party_url),
-      url::Origin::Create(third_party_url), net::CookieSettingOverrides()));
+      url::Origin::Create(third_party_url), net::CookieSettingOverrides(),
+      /*cookie_partition_key=*/std::nullopt));
 
   // Allowlisted origin embedded in Web top-level origin can't use cookies.
   EXPECT_FALSE(cookies_settings->IsFullCookieAccessAllowed(
       GURL("https://example2.com"),
       net::SiteForCookies::FromUrl(third_party_url),
-      url::Origin::Create(third_party_url), net::CookieSettingOverrides()));
+      url::Origin::Create(third_party_url), net::CookieSettingOverrides(),
+      /*cookie_partition_key=*/std::nullopt));
 
   // Allowlisted origin making subresource request (e.g. image) can't use
   // cookies.
   EXPECT_FALSE(cookies_settings->IsFullCookieAccessAllowed(
       third_party_url, net::SiteForCookies(), std::nullopt,
-      net::CookieSettingOverrides()));
+      net::CookieSettingOverrides(), /*cookie_partition_key=*/std::nullopt));
 
   // Allowlisted origin embedded in the wrong WebUI origin can't use cookies.
   const GURL url_no_permission_webui = GURL("chrome-untrusted://no-perm");
   EXPECT_FALSE(cookies_settings->IsFullCookieAccessAllowed(
       third_party_url, net::SiteForCookies::FromUrl(url_no_permission_webui),
       url::Origin::Create(url_no_permission_webui),
-      net::CookieSettingOverrides()));
+      net::CookieSettingOverrides(), /*cookie_partition_key=*/std::nullopt));
 
   // Other permissions aren't affected.
   EXPECT_EQ(CONTENT_SETTING_BLOCK,
@@ -328,31 +331,34 @@
 
   EXPECT_TRUE(cookies_settings->IsFullCookieAccessAllowed(
       third_party_url, net::SiteForCookies::FromUrl(top_level_url),
-      url::Origin::Create(top_level_url), net::CookieSettingOverrides()));
+      url::Origin::Create(top_level_url), net::CookieSettingOverrides(),
+      /*cookie_partition_key=*/std::nullopt));
   // Allowlisted origin on its own can use cookies, because only third-party
   // cookies are blocked.
   EXPECT_TRUE(cookies_settings->IsFullCookieAccessAllowed(
       third_party_url, net::SiteForCookies::FromUrl(third_party_url),
-      url::Origin::Create(third_party_url), net::CookieSettingOverrides()));
+      url::Origin::Create(third_party_url), net::CookieSettingOverrides(),
+      /*cookie_partition_key=*/std::nullopt));
 
   // Allowlisted origin embedded in Web top-level origin can't use cookies.
   EXPECT_FALSE(cookies_settings->IsFullCookieAccessAllowed(
       GURL("https://example2.com"),
       net::SiteForCookies::FromUrl(third_party_url),
-      url::Origin::Create(third_party_url), net::CookieSettingOverrides()));
+      url::Origin::Create(third_party_url), net::CookieSettingOverrides(),
+      /*cookie_partition_key=*/std::nullopt));
 
   // Allowlisted origin embedded in the wrong WebUI origin can't use cookies.
   const GURL url_no_permission_webui = GURL("chrome-untrusted://no-perm");
   EXPECT_FALSE(cookies_settings->IsFullCookieAccessAllowed(
       third_party_url, net::SiteForCookies::FromUrl(url_no_permission_webui),
       url::Origin::Create(url_no_permission_webui),
-      net::CookieSettingOverrides()));
+      net::CookieSettingOverrides(), /*cookie_partition_key=*/std::nullopt));
 
   // Allowlisted origin making subresource request (e.g. image) can't use
   // cookies.
   EXPECT_FALSE(cookies_settings->IsFullCookieAccessAllowed(
       third_party_url, net::SiteForCookies(), std::nullopt,
-      net::CookieSettingOverrides()));
+      net::CookieSettingOverrides(), /*cookie_partition_key=*/std::nullopt));
 
   // Other permissions aren't affected.
   EXPECT_EQ(CONTENT_SETTING_BLOCK,
@@ -424,7 +430,8 @@
   // Check that settings are applied before creating an allowlist entry.
   EXPECT_FALSE(cookie_settings->IsFullCookieAccessAllowed(
       url, net::SiteForCookies::FromUrl(top_level_url),
-      url::Origin::Create(top_level_url), {}));
+      url::Origin::Create(top_level_url), {},
+      /*cookie_partition_key=*/std::nullopt));
   EXPECT_TRUE(cookie_settings->IsCookieSessionOnly(url));
   EXPECT_TRUE(cookie_settings->ShouldDeleteCookieOnExit(
       cookie_settings->GetCookieSettings(), url.host(),
@@ -445,7 +452,8 @@
 
   EXPECT_TRUE(cookie_settings->IsFullCookieAccessAllowed(
       url, net::SiteForCookies::FromUrl(top_level_url),
-      url::Origin::Create(top_level_url), {}));
+      url::Origin::Create(top_level_url), {},
+      /*cookie_partition_key=*/std::nullopt));
   EXPECT_TRUE(cookie_settings->IsCookieSessionOnly(url));
   EXPECT_TRUE(cookie_settings->ShouldDeleteCookieOnExit(
       cookie_settings->GetCookieSettings(), url.host(),
diff --git a/chrome/browser/updates/announcement_notification/announcement_notification_delegate.h b/chrome/browser/updates/announcement_notification/announcement_notification_delegate.h
index 142d6a4..148a41ba 100644
--- a/chrome/browser/updates/announcement_notification/announcement_notification_delegate.h
+++ b/chrome/browser/updates/announcement_notification/announcement_notification_delegate.h
@@ -11,7 +11,8 @@
 class NotificationDisplayService;
 
 // Id of the announcement notification.
-constexpr char kAnnouncementNotificationId[] = "announcement_notification";
+inline constexpr char kAnnouncementNotificationId[] =
+    "announcement_notification";
 
 // Default delegate for AnnouncementNotificationService that works on
 // non-Android platforms.
diff --git a/chrome/browser/updates/announcement_notification/announcement_notification_service.h b/chrome/browser/updates/announcement_notification/announcement_notification_service.h
index 7cb43ea..b764258 100644
--- a/chrome/browser/updates/announcement_notification/announcement_notification_service.h
+++ b/chrome/browser/updates/announcement_notification/announcement_notification_service.h
@@ -24,41 +24,41 @@
 
 // The Finch parameter name for a boolean value that whether to show
 // notification on first run.
-constexpr char kSkipFirstRun[] = "skip_first_run";
+inline constexpr char kSkipFirstRun[] = "skip_first_run";
 
 // The Finch parameter name for a string value that represents a time.
 // If first run happens after this time, notification will not show.
 // The string defined in Finch config should specify the time zone.
 // e.g. 02 Feb 2020 13:00:00 GMT.
-constexpr char kSkipFirstRunAfterTime[] = "skip_first_run_after_time";
+inline constexpr char kSkipFirstRunAfterTime[] = "skip_first_run_after_time";
 
 // The Finch parameter name for a boolean value that whether to show
 // notification for new profile.
-constexpr char kSkipNewProfile[] = "skip_new_profile";
+inline constexpr char kSkipNewProfile[] = "skip_new_profile";
 
 // The Finch parameter to control whether to skip notification display.
-constexpr char kSkipDisplay[] = "skip_display";
+inline constexpr char kSkipDisplay[] = "skip_display";
 
 // The Finch parameter to define the latest version number of the notification.
-constexpr char kVersion[] = "version";
+inline constexpr char kVersion[] = "version";
 
 // The Finch parameter to define whether to show the announcement to users not
 // signed in.
-constexpr char kRequireSignout[] = "require_sign_out";
+inline constexpr char kRequireSignout[] = "require_sign_out";
 
 // The Finch parameter to define whether to show the announcement to users not
 // signed in.
-constexpr char kShowOneAllProfiles[] = "show_one_all_profiles";
+inline constexpr char kShowOneAllProfiles[] = "show_one_all_profiles";
 
 // The Finch parameter to define the announcement URL.
-constexpr char kAnnouncementUrl[] = "announcement_url";
+inline constexpr char kAnnouncementUrl[] = "announcement_url";
 
 // Preference name to persist the current version sent from Finch parameter.
-constexpr char kCurrentVersionPrefName[] =
+inline constexpr char kCurrentVersionPrefName[] =
     "announcement_notification_service_current_version";
 
 // Preference name to persist the time of Chrome first run.
-constexpr char kAnnouncementFirstRunTimePrefName[] =
+inline constexpr char kAnnouncementFirstRunTimePrefName[] =
     "announcement_notification_service_first_run_time";
 
 // Used to show a notification when the version defined in Finch parameter is
diff --git a/chrome/browser/web_applications/commands/web_install_from_url_command.cc b/chrome/browser/web_applications/commands/web_install_from_url_command.cc
index 69cc24b..991a7d4 100644
--- a/chrome/browser/web_applications/commands/web_install_from_url_command.cc
+++ b/chrome/browser/web_applications/commands/web_install_from_url_command.cc
@@ -21,6 +21,7 @@
 #include "chrome/browser/web_applications/web_app_helpers.h"
 #include "chrome/browser/web_applications/web_app_install_finalizer.h"
 #include "chrome/browser/web_applications/web_app_install_info.h"
+#include "chrome/browser/web_applications/web_app_install_params.h"
 #include "chrome/browser/web_applications/web_app_install_utils.h"
 #include "chrome/browser/web_applications/web_app_logging.h"
 #include "chrome/browser/web_applications/web_app_registrar.h"
@@ -32,6 +33,8 @@
 #include "components/webapps/browser/installable/installable_logging.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "components/webapps/browser/installable/installable_params.h"
+#include "components/webapps/browser/installable/ml_install_operation_tracker.h"
+#include "components/webapps/browser/installable/ml_installability_promoter.h"
 #include "components/webapps/browser/web_contents/web_app_url_loader.h"
 #include "components/webapps/common/web_app_id.h"
 #include "content/public/browser/web_contents.h"
@@ -46,6 +49,8 @@
     Profile& profile,
     const GURL& install_url,
     const std::optional<GURL>& manifest_id,
+    base::WeakPtr<content::WebContents> web_contents,
+    WebAppInstallDialogCallback dialog_callback,
     WebInstallFromUrlCommandCallback installed_callback)
     : WebAppCommand<SharedWebContentsLock,
                     const webapps::AppId&,
@@ -60,6 +65,8 @@
       profile_(profile),
       manifest_id_(manifest_id),
       install_url_(install_url),
+      web_contents_(web_contents),
+      dialog_callback_(std::move(dialog_callback)),
       install_error_log_entry_(/*background_installation=*/false,
                                kInstallSource) {
   if (manifest_id_.has_value()) {
@@ -237,16 +244,27 @@
   install_error_log_entry_.LogDownloadedIconsErrors(
       *web_app_info_, result, icons_map, icons_http_results);
 
-  // TODO(crbug.com/333795265): Show install dialog.
-  OnInstallDialogCompleted(/*user_accepted=*/true);
+  // TODO(crbug.com/415825168): Support detailed install dialog for background
+  // installs. For now, pass `nullptr` to the screenshot_fetcher which will
+  // always show the simple dialog.
+  std::move(dialog_callback_)
+      .Run(
+          /*screenshot_fetcher=*/nullptr, web_contents_.get(),
+          std::move(web_app_info_),
+          base::BindOnce(&WebInstallFromUrlCommand::OnInstallDialogCompleted,
+                         weak_ptr_factory_.GetWeakPtr()));
 }
 
-void WebInstallFromUrlCommand::OnInstallDialogCompleted(bool user_accepted) {
+void WebInstallFromUrlCommand::OnInstallDialogCompleted(
+    bool user_accepted,
+    std::unique_ptr<WebAppInstallInfo> web_app_info) {
   if (!user_accepted) {
     Abort(webapps::InstallResultCode::kUserInstallDeclined);
     return;
   }
 
+  web_app_info_ = std::move(web_app_info);
+
   web_app_info_->user_display_mode =
       web_app::mojom::UserDisplayMode::kStandalone;
   WebAppInstallFinalizer::FinalizeOptions finalize_options(kInstallSource);
diff --git a/chrome/browser/web_applications/commands/web_install_from_url_command.h b/chrome/browser/web_applications/commands/web_install_from_url_command.h
index b83e7690a..83b9c50 100644
--- a/chrome/browser/web_applications/commands/web_install_from_url_command.h
+++ b/chrome/browser/web_applications/commands/web_install_from_url_command.h
@@ -12,6 +12,7 @@
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/web_applications/commands/web_app_command.h"
 #include "chrome/browser/web_applications/locks/shared_web_contents_lock.h"
+#include "chrome/browser/web_applications/web_app_install_params.h"
 #include "chrome/browser/web_applications/web_app_logging.h"
 #include "components/webapps/common/web_app_id.h"
 #include "third_party/blink/public/mojom/manifest/manifest.mojom-forward.h"
@@ -70,6 +71,8 @@
   WebInstallFromUrlCommand(Profile& profile,
                            const GURL& install_url,
                            const std::optional<GURL>& manifest_id,
+                           base::WeakPtr<content::WebContents> web_contents,
+                           WebAppInstallDialogCallback dialog_callback,
                            WebInstallFromUrlCommandCallback installed_callback);
   ~WebInstallFromUrlCommand() override;
 
@@ -90,7 +93,9 @@
       IconsDownloadedResult result,
       IconsMap icons_map,
       DownloadedIconsHttpResults icons_http_results);
-  void OnInstallDialogCompleted(bool user_accepted);
+  void OnInstallDialogCompleted(
+      bool user_accepted,
+      std::unique_ptr<WebAppInstallInfo> web_app_info);
   void InstallApp();
   void OnAppInstalled(const webapps::AppId& app_id,
                       webapps::InstallResultCode code);
@@ -102,6 +107,10 @@
   // Unset if the WebInstall API's 1-parameter signature was called.
   std::optional<GURL> manifest_id_;
   GURL install_url_;
+  // The WebContents that initiated the install. This is used only to show the
+  // install dialog.
+  base::WeakPtr<content::WebContents> web_contents_;
+  WebAppInstallDialogCallback dialog_callback_;
   InstallErrorLogEntry install_error_log_entry_;
 
   std::unique_ptr<SharedWebContentsLock> web_contents_lock_;
diff --git a/chrome/browser/web_applications/commands/web_install_from_url_command_browsertest.cc b/chrome/browser/web_applications/commands/web_install_from_url_command_browsertest.cc
index 3fb12266..3b7d5361 100644
--- a/chrome/browser/web_applications/commands/web_install_from_url_command_browsertest.cc
+++ b/chrome/browser/web_applications/commands/web_install_from_url_command_browsertest.cc
@@ -4,20 +4,27 @@
 
 #include "chrome/browser/web_applications/commands/web_install_from_url_command.h"
 
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/path_service.h"
 #include "base/test/bind.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_future.h"
+#include "base/threading/thread_restrictions.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/web_applications/web_app_browsertest_base.h"
+#include "chrome/browser/ui/web_applications/web_app_dialogs.h"
 #include "chrome/browser/web_applications/test/command_metrics_test_helper.h"
 #include "chrome/browser/web_applications/test/web_app_test_utils.h"
 #include "chrome/browser/web_applications/web_app_command_manager.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "components/permissions/permission_request_manager.h"
+#include "components/url_formatter/elide_url.h"
 #include "components/webapps/browser/install_result_code.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test.h"
@@ -25,9 +32,17 @@
 #include "content/public/test/test_renderer_host.h"
 #include "net/base/url_util.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "skia/ext/image_operations.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features_generated.h"
+#include "ui/base/models/image_model.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/image/image_unittest_util.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/interaction/element_tracker_views.h"
+#include "ui/views/widget/any_widget_observer.h"
 
 namespace {
 constexpr webapps::WebappInstallSource kInstallSource =
@@ -143,6 +158,8 @@
   std::string install_url =
       https_server()->GetURL("/banners/manifest_with_id_test_page.html").spec();
 
+  auto auto_accept_pwa_install_confirmation =
+      SetAutoAcceptPWAInstallConfirmationForTesting();
   SetPermissionResponse(/*permission_granted=*/true);
   base::HistogramTester histograms;
   ASSERT_TRUE(TryInstallApp(install_url));
@@ -177,6 +194,8 @@
   std::string install_url = GetInstallableAppURL().spec();
   std::string manifest_id = install_url;
 
+  auto auto_accept_pwa_install_confirmation =
+      SetAutoAcceptPWAInstallConfirmationForTesting();
   SetPermissionResponse(/*permission_granted=*/true);
   base::HistogramTester histograms;
   ASSERT_TRUE(TryInstallApp(install_url, manifest_id));
@@ -214,6 +233,9 @@
   std::string install_url = GetInstallableAppURL().spec();
   std::string manifest_id = install_url;
   base::HistogramTester histograms;
+
+  auto auto_accept_pwa_install_confirmation =
+      SetAutoAcceptPWAInstallConfirmationForTesting();
   SetPermissionResponse(/*permission_granted=*/true);
   ASSERT_TRUE(TryInstallApp(install_url, manifest_id));
 
@@ -268,8 +290,12 @@
   std::string manifest_id =
       secondary_server_.GetURL("/banners/manifest_test_page.html").spec();
   base::HistogramTester histograms;
+
+  auto auto_accept_pwa_install_confirmation =
+      SetAutoAcceptPWAInstallConfirmationForTesting();
   SetPermissionResponse(/*permission_granted=*/true);
   ASSERT_TRUE(TryInstallApp(install_url, manifest_id));
+
   EXPECT_TRUE(ResultExists());
   EXPECT_EQ(GetManifestIdResult(), manifest_id);
   EXPECT_FALSE(ErrorExists());
@@ -506,4 +532,97 @@
                       webapps::WebappInstallSource::WEB_INSTALL, 1))));
 }
 
+class WebInstallFromUrlCommandDialogTest
+    : public WebInstallFromUrlCommandBrowserTest {
+ public:
+  SkBitmap ReadImageFile(const base::FilePath& file_path) {
+    base::ScopedAllowBlockingForTesting allow_blocking;
+
+    std::optional<std::vector<uint8_t>> file_contents =
+        base::ReadFileToBytes(file_path);
+
+    return gfx::PNGCodec::Decode(file_contents.value());
+  }
+
+  std::u16string GetAppTitle() {
+    return u"Manifest test app with id specified";
+  }
+
+  base::FilePath GetIconPath() {
+    base::FilePath path;
+    base::PathService::Get(chrome::DIR_TEST_DATA, &path);
+    return path.AppendASCII("banners").AppendASCII("launcher-icon-1x.png");
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(WebInstallFromUrlCommandDialogTest,
+                       VerifyInstallDialogContents) {
+  // Go to /simple.html
+  NavigateToValidUrl();
+
+  // Target a different page to install.
+  const GURL install_url =
+      https_server()->GetURL("/banners/manifest_with_id_test_page.html");
+
+  SetPermissionResponse(/*permission_granted=*/true);
+
+  views::NamedWidgetShownWaiter widget_waiter(
+      views::test::AnyWidgetTestPasskey{}, "WebAppSimpleInstallDialog");
+
+  // We don't actually care about the result of the install, and EvalJs blocks
+  // until the promise resolves, which only happens after the dialog is closed.
+  // Execute the install asynchronously so we can actually check the dialog
+  // contents without the promise timing out.
+  ExecuteScriptAsync(web_contents(),
+                     "navigator.install('" + install_url.spec() + "');");
+
+  // Wait for the install dialog to show.
+  views::Widget* widget = widget_waiter.WaitIfNeededAndGet();
+  ASSERT_NE(widget, nullptr);
+
+  views::ElementTrackerViews* tracker_views =
+      views::ElementTrackerViews::GetInstance();
+  ui::ElementContext context =
+      views::ElementTrackerViews::GetContextForWidget(widget);
+
+  // Get the icon from the dialog.
+  views::ImageView* icon_view =
+      tracker_views->GetUniqueViewAs<views::ImageView>(
+          kSimpleInstallDialogIconView, context);
+  ASSERT_NE(icon_view, nullptr);
+
+  // Convert to a bitmap.
+  const ui::ImageModel& icon_view_model = icon_view->GetImageModel();
+  ASSERT_FALSE(icon_view_model.IsEmpty());
+  ASSERT_TRUE(icon_view_model.IsImage());
+  const SkBitmap* dialog_icon_bitmap = icon_view_model.GetImage().ToSkBitmap();
+  CHECK(!dialog_icon_bitmap->isNull());
+
+  // Read the expected bitmap from the test data directory.
+  base::FilePath path = GetIconPath();
+  SkBitmap bitmap_from_png = ReadImageFile(path);
+  CHECK(!bitmap_from_png.isNull());
+  // The dialog resizes the icon. Resize the png to match.
+  bitmap_from_png = skia::ImageOperations::Resize(
+      bitmap_from_png, skia::ImageOperations::RESIZE_BEST,
+      dialog_icon_bitmap->width(), dialog_icon_bitmap->height());
+
+  EXPECT_TRUE(
+      gfx::test::AreBitmapsClose(*dialog_icon_bitmap, bitmap_from_png, 3));
+
+  // Verify the app title label.
+  views::Label* app_title_view = tracker_views->GetUniqueViewAs<views::Label>(
+      kSimpleInstallDialogAppTitle, context);
+  ASSERT_NE(app_title_view, nullptr);
+  EXPECT_EQ(app_title_view->GetText(), GetAppTitle());
+
+  // Verify the origin label.
+  views::Label* start_url_view = tracker_views->GetUniqueViewAs<views::Label>(
+      kSimpleInstallDialogOriginLabel, context);
+  ASSERT_NE(start_url_view, nullptr);
+  EXPECT_EQ(
+      start_url_view->GetText(),
+      url_formatter::FormatUrlForDisplayOmitSchemePathAndTrivialSubdomains(
+          install_url));
+}
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/navigation_capturing_browser_navigator_browsertest.cc b/chrome/browser/web_applications/navigation_capturing_browser_navigator_browsertest.cc
index 4b78809..4cfcee5 100644
--- a/chrome/browser/web_applications/navigation_capturing_browser_navigator_browsertest.cc
+++ b/chrome/browser/web_applications/navigation_capturing_browser_navigator_browsertest.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/ui/web_applications/web_app_browser_controller.h"
 #include "chrome/browser/ui/web_applications/web_app_browsertest_base.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom-shared.h"
+#include "chrome/browser/web_applications/navigation_capturing_metrics.h"
 #include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
 #include "chrome/browser/web_applications/web_app_command_scheduler.h"
 #include "chrome/browser/web_applications/web_app_provider.h"
@@ -193,6 +194,21 @@
     metrics::SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
   }
 
+  std::vector<NavigationCapturingDisplayModeResult>
+  GetNavigationCapturingFinalDisplayMetric(
+      const base::HistogramTester& tester) {
+    std::vector<base::Bucket> display_result_buckets =
+        tester.GetAllSamples("Webapp.NavigationCapturing.FinalDisplay.Result");
+    std::vector<NavigationCapturingDisplayModeResult> bucket_list;
+    for (const base::Bucket& bucket : display_result_buckets) {
+      for (int count = 0; count < bucket.count; count++) {
+        bucket_list.push_back(
+            static_cast<NavigationCapturingDisplayModeResult>(bucket.min));
+      }
+    }
+    return bucket_list;
+  }
+
   base::test::ScopedFeatureList feature_list_;
   std::vector<base::test::FeatureRefAndParams> enabled_features;
 };
@@ -228,6 +244,10 @@
   histograms.ExpectUniqueSample(
       "WebApp.LaunchSource", apps::LaunchSource::kFromNavigationCapturing, 1);
   histograms.ExpectTotalCount(kLaunchParamsEnqueueMetric, 1);
+  EXPECT_THAT(
+      GetNavigationCapturingFinalDisplayMetric(histograms),
+      testing::ElementsAre(
+          NavigationCapturingDisplayModeResult::kAppBrowserTabFinalBrowserTab));
 
   ASSERT_TRUE(new_tab);
 
@@ -285,6 +305,11 @@
   histograms.ExpectUniqueSample(
       "WebApp.LaunchSource", apps::LaunchSource::kFromNavigationCapturing, 1);
 
+  EXPECT_THAT(
+      GetNavigationCapturingFinalDisplayMetric(histograms),
+      testing::ElementsAre(
+          NavigationCapturingDisplayModeResult::kAppStandaloneFinalStandalone));
+
   // This is measured twice, once for each launch param obtained.
   histograms.ExpectTotalCount(kLaunchParamsEnqueueMetric, 2);
 }
@@ -345,6 +370,11 @@
   histograms.ExpectUniqueSample(
       "WebApp.LaunchSource", apps::LaunchSource::kFromNavigationCapturing, 1);
 
+  EXPECT_THAT(
+      GetNavigationCapturingFinalDisplayMetric(histograms),
+      testing::ElementsAre(
+          NavigationCapturingDisplayModeResult::kAppStandaloneFinalStandalone));
+
   // This is measured twice, once for each launch param obtained.
   histograms.ExpectTotalCount(kLaunchParamsEnqueueMetric, 2);
 }
@@ -395,6 +425,10 @@
   histograms.ExpectUniqueSample(
       "WebApp.LaunchSource", apps::LaunchSource::kFromNavigationCapturing, 1);
   histograms.ExpectTotalCount(kLaunchParamsEnqueueMetric, 1);
+  EXPECT_THAT(
+      GetNavigationCapturingFinalDisplayMetric(histograms),
+      testing::ElementsAre(
+          NavigationCapturingDisplayModeResult::kAppStandaloneFinalStandalone));
 }
 
 IN_PROC_BROWSER_TEST_F(NavigationCapturingBrowserNavigatorBrowserTest,
@@ -441,6 +475,10 @@
   histograms.ExpectUniqueSample(
       "WebApp.LaunchSource", apps::LaunchSource::kFromNavigationCapturing, 1);
   histograms.ExpectTotalCount(kLaunchParamsEnqueueMetric, 1);
+  EXPECT_THAT(
+      GetNavigationCapturingFinalDisplayMetric(histograms),
+      testing::ElementsAre(
+          NavigationCapturingDisplayModeResult::kAppStandaloneFinalStandalone));
 }
 
 IN_PROC_BROWSER_TEST_F(NavigationCapturingBrowserNavigatorBrowserTest,
@@ -493,6 +531,10 @@
   histograms.ExpectUniqueSample(
       "WebApp.LaunchSource", apps::LaunchSource::kFromNavigationCapturing, 1);
   histograms.ExpectTotalCount(kLaunchParamsEnqueueMetric, 1);
+  EXPECT_THAT(
+      GetNavigationCapturingFinalDisplayMetric(histograms),
+      testing::ElementsAre(
+          NavigationCapturingDisplayModeResult::kAppBrowserTabFinalBrowserTab));
 
   EXPECT_NE(contents_navigation_happened_in,
             new_browser->tab_strip_model()->GetActiveWebContents());
@@ -548,6 +590,11 @@
   // enqueued, and hence this metric will not be measured.
   histograms.ExpectTotalCount(kLaunchParamsEnqueueMetric, 0);
 
+  EXPECT_THAT(
+      GetNavigationCapturingFinalDisplayMetric(histograms),
+      testing::ElementsAre(
+          NavigationCapturingDisplayModeResult::kAppBrowserTabFinalBrowserTab));
+
   // browser() should still be at the GetAppUrl() page.
   EXPECT_EQ(GetAppNoManifestUrl(), browser()
                                        ->tab_strip_model()
@@ -615,6 +662,11 @@
       "WebApp.LaunchSource", apps::LaunchSource::kFromNavigationCapturing, 1);
   histograms.ExpectTotalCount(kLaunchParamsEnqueueMetric, 1);
 
+  EXPECT_THAT(
+      GetNavigationCapturingFinalDisplayMetric(histograms),
+      testing::ElementsAre(
+          NavigationCapturingDisplayModeResult::kAppBrowserTabFinalStandalone));
+
   EXPECT_EQ(contents_navigation_happened_in,
             app_browser_to_use->tab_strip_model()->GetActiveWebContents());
   EXPECT_EQ(0, app_browser_to_use->tab_strip_model()->GetIndexOfWebContents(
@@ -673,6 +725,10 @@
   histograms.ExpectUniqueSample(
       "WebApp.LaunchSource", apps::LaunchSource::kFromNavigationCapturing, 1);
   histograms.ExpectTotalCount(kLaunchParamsEnqueueMetric, 1);
+  EXPECT_THAT(
+      GetNavigationCapturingFinalDisplayMetric(histograms),
+      testing::ElementsAre(
+          NavigationCapturingDisplayModeResult::kAppBrowserTabFinalBrowserTab));
 
   EXPECT_EQ(GetFocusExistingUrl(), app_browser_to_use->tab_strip_model()
                                        ->GetActiveWebContents()
@@ -729,6 +785,59 @@
               testing::ElementsAre(GetLandingPage()));
 }
 
+using LaunchContainerMetricMeasurementTest =
+    NavigationCapturingBrowserNavigatorBrowserTest;
+
+IN_PROC_BROWSER_TEST_F(LaunchContainerMetricMeasurementTest,
+                       NavigateExistingStandaloneToTab) {
+  // Load 'kNavigateExistingUrl` and `kFocusExistingUrl` in new tabs.
+  chrome::NewTab(browser());
+  ASSERT_TRUE(
+      ui_test_utils::NavigateToURL(browser(), GetNavigateExistingUrl()));
+  content::WebContents* target_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  chrome::NewTab(browser());
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GetFocusExistingUrl()));
+
+  // Install both apps.
+  const webapps::AppId& source_app =
+      InstallWebAppFromPageAndCloseAppBrowser(browser(), GetFocusExistingUrl());
+  const webapps::AppId& dest_app = InstallWebAppFromPageAndCloseAppBrowser(
+      browser(), GetNavigateExistingUrl());
+
+#if BUILDFLAG(IS_CHROMEOS)
+  EXPECT_EQ(apps::test::EnableLinkCapturingByUser(profile(), dest_app),
+            base::ok());
+#endif
+
+  // Trigger a navigation to `kNavigateExistingUrl`. This should end up in the
+  // browser tab.
+  base::HistogramTester histograms;
+  {
+    NavigateParams params(profile(), GetNavigateExistingUrl(),
+                          ui::PAGE_TRANSITION_LINK);
+    params.source_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
+    Navigate(&params);
+    LoadURLInContents(target_contents, GetNavigateExistingUrl(), params);
+  }
+
+  content::WaitForLoadStop(target_contents);
+  apps::test::FlushLaunchQueuesForAllBrowserTabs();
+  AwaitMetricsAvailableFromRenderer();
+
+  // Verify that navigation did indeed end up in the browser tab via navigation
+  // capturing.
+  histograms.ExpectUniqueSample(
+      "WebApp.LaunchSource", apps::LaunchSource::kFromNavigationCapturing, 1);
+
+  EXPECT_THAT(
+      GetNavigationCapturingFinalDisplayMetric(histograms),
+      testing::ElementsAre(
+          NavigationCapturingDisplayModeResult::kAppStandaloneFinalBrowserTab));
+}
+
 class NavigationCapturingWithRedirectionBrowserNavigatorTest
     : public NavigationCapturingBrowserNavigatorBrowserTest {
  public:
@@ -799,6 +908,11 @@
   // Make sure that web contents is a tab in `browser()` and not `new_browser`.
   EXPECT_NE(browser()->tab_strip_model()->GetIndexOfWebContents(new_tab),
             TabStripModel::kNoTab);
+
+  EXPECT_THAT(
+      GetNavigationCapturingFinalDisplayMetric(histograms),
+      testing::ElementsAre(
+          NavigationCapturingDisplayModeResult::kAppBrowserTabFinalBrowserTab));
 }
 
 IN_PROC_BROWSER_TEST_F(NavigationCapturingWithRedirectionBrowserNavigatorTest,
@@ -831,10 +945,13 @@
   histograms.ExpectUniqueSample(
       "WebApp.LaunchSource", apps::LaunchSource::kFromNavigationCapturing, 0);
   histograms.ExpectTotalCount(kLaunchParamsEnqueueMetric, 0);
+  EXPECT_THAT(GetNavigationCapturingFinalDisplayMetric(histograms),
+              testing::IsEmpty());
 
   // Make sure that web contents is a tab in `browser()` and not `new_browser`.
   EXPECT_NE(browser()->tab_strip_model()->GetIndexOfWebContents(new_tab),
             TabStripModel::kNoTab);
 }
+
 }  // namespace
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/test/fake_web_app_ui_manager.cc b/chrome/browser/web_applications/test/fake_web_app_ui_manager.cc
index 9ea5829..c3c66d3 100644
--- a/chrome/browser/web_applications/test/fake_web_app_ui_manager.cc
+++ b/chrome/browser/web_applications/test/fake_web_app_ui_manager.cc
@@ -208,6 +208,15 @@
                           webapps::InstallResultCode::kWebAppProviderNotReady);
 }
 
+void FakeWebAppUiManager::TriggerInstallDialogForBackgroundInstall(
+    content::WebContents* initiating_web_contents,
+    std::unique_ptr<webapps::MlInstallOperationTracker> tracker,
+    const GURL& install_url,
+    const std::optional<GURL>& manifest_id,
+    InstallCallback callback) {
+  NOTIMPLEMENTED();
+}
+
 void FakeWebAppUiManager::PresentUserUninstallDialog(
     const webapps::AppId& app_id,
     webapps::WebappUninstallSource uninstall_source,
diff --git a/chrome/browser/web_applications/test/fake_web_app_ui_manager.h b/chrome/browser/web_applications/test/fake_web_app_ui_manager.h
index a30ef867..8072ab0 100644
--- a/chrome/browser/web_applications/test/fake_web_app_ui_manager.h
+++ b/chrome/browser/web_applications/test/fake_web_app_ui_manager.h
@@ -15,6 +15,7 @@
 #include "base/values.h"
 #include "chrome/browser/web_applications/web_app_ui_manager.h"
 #include "components/services/app_service/public/cpp/app_launch_util.h"
+#include "components/webapps/browser/installable/ml_install_operation_tracker.h"
 #include "components/webapps/common/web_app_id.h"
 
 class Browser;
@@ -23,6 +24,9 @@
 class FilePath;
 }  // namespace base
 
+namespace webapps {
+class MlInstallOperationTracker;
+}  // namespace webapps
 namespace web_app {
 
 class FakeWebAppUiManager : public WebAppUiManager {
@@ -113,6 +117,12 @@
   void TriggerInstallDialog(content::WebContents* web_contents,
                             webapps::WebappInstallSource source,
                             InstallCallback callback) override;
+  void TriggerInstallDialogForBackgroundInstall(
+      content::WebContents* initiating_web_contents,
+      std::unique_ptr<webapps::MlInstallOperationTracker> tracker,
+      const GURL& install_url,
+      const std::optional<GURL>& manifest_id,
+      InstallCallback callback) override;
 
   void PresentUserUninstallDialog(
       const webapps::AppId& app_id,
diff --git a/chrome/browser/web_applications/test/web_app_test_utils.cc b/chrome/browser/web_applications/test/web_app_test_utils.cc
index f261c55..067bbc6e 100644
--- a/chrome/browser/web_applications/test/web_app_test_utils.cc
+++ b/chrome/browser/web_applications/test/web_app_test_utils.cc
@@ -1182,13 +1182,13 @@
 webapps::AppId InstallPwaForCurrentUrl(Browser* browser) {
   // Depending on the installability criteria, different dialogs can be used.
   SetAutoAcceptWebAppDialogForTesting(true, true);
-  SetAutoAcceptPWAInstallConfirmationForTesting(true);
+  auto auto_accept_pwa_install_confirmation =
+      SetAutoAcceptPWAInstallConfirmationForTesting();
   SetAutoAcceptDiyAppsInstallDialogForTesting(true);
   WebAppTestInstallWithOsHooksObserver observer(browser->profile());
   observer.BeginListening();
   CHECK(chrome::ExecuteCommand(browser, IDC_INSTALL_PWA));
   webapps::AppId app_id = observer.Wait();
-  SetAutoAcceptPWAInstallConfirmationForTesting(false);
   SetAutoAcceptWebAppDialogForTesting(false, false);
   SetAutoAcceptDiyAppsInstallDialogForTesting(false);
   return app_id;
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.cc b/chrome/browser/web_applications/web_app_command_scheduler.cc
index 4a3cc14..c718919 100644
--- a/chrome/browser/web_applications/web_app_command_scheduler.cc
+++ b/chrome/browser/web_applications/web_app_command_scheduler.cc
@@ -83,6 +83,7 @@
 #include "components/keep_alive_registry/keep_alive_types.h"
 #include "components/keep_alive_registry/scoped_keep_alive.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
+#include "components/webapps/browser/installable/ml_install_operation_tracker.h"
 #include "components/webapps/browser/web_contents/web_app_url_loader.h"
 #include "content/public/browser/storage_partition_config.h"
 #include "content/public/browser/web_contents.h"
@@ -689,12 +690,14 @@
 void WebAppCommandScheduler::InstallAppFromUrl(
     const GURL& install_url,
     const std::optional<GURL>& manifest_id,
+    base::WeakPtr<content::WebContents> web_contents,
+    WebAppInstallDialogCallback dialog_callback,
     WebInstallFromUrlCommandCallback installed_callback,
     const base::Location& location) {
   provider_->command_manager().ScheduleCommand(
-      std::make_unique<WebInstallFromUrlCommand>(profile_.get(), install_url,
-                                                 manifest_id,
-                                                 std::move(installed_callback)),
+      std::make_unique<WebInstallFromUrlCommand>(
+          profile_.get(), install_url, manifest_id, web_contents,
+          std::move(dialog_callback), std::move(installed_callback)),
       location);
 }
 
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.h b/chrome/browser/web_applications/web_app_command_scheduler.h
index 0be38c4..2388358 100644
--- a/chrome/browser/web_applications/web_app_command_scheduler.h
+++ b/chrome/browser/web_applications/web_app_command_scheduler.h
@@ -585,11 +585,14 @@
       WebAppIconDiagnosticResultCallback result_callback,
       const base::Location& location = FROM_HERE);
 
-  // Installs the web content at `install_url`, verifying that it has the
-  // given resolved `manifest_id`. Returns the `InstallResultCode` and the
-  // computed manifest id if successful. Used by Web Install API.
+  // User initiated install uses the shared web contents to install the content
+  // at `install_url`, with optional `manifest_id`.
+  // Calls `installed_callback` with the `InstallResultCode` and the computed
+  // manifest id if successful. Used by Web Install API.
   void InstallAppFromUrl(const GURL& install_url,
                          const std::optional<GURL>& manifest_id,
+                         base::WeakPtr<content::WebContents> web_contents,
+                         WebAppInstallDialogCallback dialog_callback,
                          WebInstallFromUrlCommandCallback installed_callback,
                          const base::Location& location = FROM_HERE);
 
diff --git a/chrome/browser/web_applications/web_app_ui_manager.h b/chrome/browser/web_applications/web_app_ui_manager.h
index 7ae5ccf8..0b6da649 100644
--- a/chrome/browser/web_applications/web_app_ui_manager.h
+++ b/chrome/browser/web_applications/web_app_ui_manager.h
@@ -37,6 +37,9 @@
 class NavigationHandle;
 }  // namespace content
 
+namespace webapps {
+class MlInstallOperationTracker;
+}
 namespace web_app {
 
 class WithAppResources;
@@ -253,6 +256,17 @@
                                     webapps::WebappInstallSource source,
                                     InstallCallback callback) = 0;
 
+  // Triggers the web app install dialog for a background install of the
+  // contents at `install_url`, with the optional `manifest_id`. The dialog will
+  // be anchored to `initiating_web_contents`. This assumes the app is not
+  // already installed. Used for the Web Install API.
+  virtual void TriggerInstallDialogForBackgroundInstall(
+      content::WebContents* initiating_web_contents,
+      std::unique_ptr<webapps::MlInstallOperationTracker> tracker,
+      const GURL& install_url,
+      const std::optional<GURL>& manifest_id,
+      InstallCallback callback) = 0;
+
   // The uninstall dialog will be modal to |parent_window|, or a non-modal if
   // |parent_window| is nullptr. Use this API if a Browser window needs to be
   // passed in along with an UninstallCompleteCallback.
diff --git a/chrome/browser/web_applications/web_app_utils.h b/chrome/browser/web_applications/web_app_utils.h
index 2dcd328d..f982a05 100644
--- a/chrome/browser/web_applications/web_app_utils.h
+++ b/chrome/browser/web_applications/web_app_utils.h
@@ -42,10 +42,10 @@
 namespace error_page {
 // |alternative_error_page_params| dictionary key values in the
 // |AlternativeErrorPageOverrideInfo| mojom struct.
-const char kMessage[] = "web_app_error_page_message";
-const char kAppShortName[] = "app_short_name";
-const char kIconUrl[] = "icon_url";
-const char kSupplementaryIcon[] = "supplementary_icon";
+inline constexpr char kMessage[] = "web_app_error_page_message";
+inline constexpr char kAppShortName[] = "app_short_name";
+inline constexpr char kIconUrl[] = "icon_url";
+inline constexpr char kSupplementaryIcon[] = "supplementary_icon";
 
 // This must match the HTML element id of the svg to show as a supplementary
 // icon on the default offline error page.
diff --git a/chrome/browser/web_applications/web_install_browsertest.cc b/chrome/browser/web_applications/web_install_browsertest.cc
index 8a1a7c4..b8c7bbf 100644
--- a/chrome/browser/web_applications/web_install_browsertest.cc
+++ b/chrome/browser/web_applications/web_install_browsertest.cc
@@ -118,8 +118,6 @@
         webapps::TestAppBannerManagerDesktop::FromWebContents(web_contents());
     ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), current_doc_url));
     manager->WaitForInstallableCheck();
-
-    SetAutoAcceptPWAInstallConfirmationForTesting(true);
   }
 
   content::WebContents* web_contents() {
@@ -171,6 +169,8 @@
       GenerateManifestId("some_id", current_doc_url).spec();
 
   NavigateAndConfigureCurrentDocumentForInstall(current_doc_url);
+  auto auto_accept_pwa_install_confirmation =
+      SetAutoAcceptPWAInstallConfirmationForTesting();
 
   base::test::TestFuture<const webapps::AppId&, webapps::InstallResultCode>
       install_future;
diff --git a/chrome/browser/web_applications/web_install_service_impl.cc b/chrome/browser/web_applications/web_install_service_impl.cc
index 6ec68017..58a24d5 100644
--- a/chrome/browser/web_applications/web_install_service_impl.cc
+++ b/chrome/browser/web_applications/web_install_service_impl.cc
@@ -27,6 +27,8 @@
 #include "components/webapps/browser/installable/installable_logging.h"
 #include "components/webapps/browser/installable/installable_metrics.h"
 #include "components/webapps/browser/installable/installable_params.h"
+#include "components/webapps/browser/installable/ml_install_operation_tracker.h"
+#include "components/webapps/browser/installable/ml_installability_promoter.h"
 #include "components/webapps/common/web_app_id.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
@@ -295,12 +297,38 @@
                             GURL());
     return;
   }
-  auto* profile =
-      Profile::FromBrowserContext(render_frame_host().GetBrowserContext());
 
-  auto* provider = WebAppProvider::GetForWebApps(profile);
-  provider->scheduler().InstallAppFromUrl(
-      install_target, manifest_id,
+  auto* web_contents =
+      content::WebContents::FromRenderFrameHost(&render_frame_host());
+
+  webapps::MLInstallabilityPromoter* promoter =
+      webapps::MLInstallabilityPromoter::FromWebContents(web_contents);
+  CHECK(promoter);
+  if (promoter->HasCurrentInstall()) {
+    // The current web contents is being installed via another method. Cancel
+    // this background install.
+    std::move(callback).Run(blink::mojom::WebInstallServiceResult::kAbortError,
+                            GURL());
+    return;
+  }
+
+  auto* provider = WebAppProvider::GetForWebContents(web_contents);
+  CHECK(provider);
+  if (provider->command_manager().IsInstallingForWebContents(web_contents)) {
+    // Another install is already scheduled on the current web contents. Cancel
+    // this background install.
+    std::move(callback).Run(blink::mojom::WebInstallServiceResult::kAbortError,
+                            GURL());
+    return;
+  }
+
+  // Register the background install on the current web contents.
+  std::unique_ptr<webapps::MlInstallOperationTracker> install_tracker =
+      promoter->RegisterCurrentInstallForWebContents(
+          webapps::WebappInstallSource::WEB_INSTALL);
+
+  provider->ui_manager().TriggerInstallDialogForBackgroundInstall(
+      web_contents, std::move(install_tracker), install_target, manifest_id,
       base::BindOnce(&WebInstallServiceImpl::OnAppInstalled,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt
index c6a3ea8..a588019 100644
--- a/chrome/build/android-arm32.pgo.txt
+++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@
-chrome-android32-main-1746619131-b5f119ad34729c39bb8db97e934147dbaff50605-413ccd12abe49ad79521af08e38c1208cc19f44a.profdata
+chrome-android32-main-1746683835-1595faf5cbb93723f571c4d409ff6ca1d7e84907-1bd71c758b467c65ac98df0740c3a0146a2462d1.profdata
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt
index a257ad1..abdadaf3 100644
--- a/chrome/build/android-arm64.pgo.txt
+++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@
-chrome-android64-main-1746631188-eb1d0ec979dc5cad49b7f79248f80823f6ca3248-9158358aef7fc7a0f62b5f98b94c2f09b8e14b32.profdata
+chrome-android64-main-1746695505-489c4ca563f78bed9bbe122adc7b46e036021e1d-4e8b235a3243badf4f0e55260cd385b68f720a0b.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 8bb18b5..8c20069 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1746619131-0479134592bfcf16abb87046cc181dafa4dca288-413ccd12abe49ad79521af08e38c1208cc19f44a.profdata
+chrome-linux-main-1746662394-55f9fee1514d0a324f448645c27e2709e6d6f894-fbacdb92137088dea8f53b8d1cbbe155a867b9d0.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 09b1233..2f9cbb1e 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1746633553-c187962b7e6e4b34c494431fc424b4ae91d00bf2-27ce3c2b779fe727f5ed1e7982fcc356a9b10b7f.profdata
+chrome-mac-arm-main-1746697152-b484f34e46967d38e537a3f5c484b6f1dd833dda-524850a07d7849a97763c3b848063d3dfdbd653d.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 97d963af..2a58ba0 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1746619131-f0689b7c453bf66428fd1cec9c04c8fc387974a9-413ccd12abe49ad79521af08e38c1208cc19f44a.profdata
+chrome-mac-main-1746683835-a60c7c5de757ff87d50998c953633c88d249b2df-1bd71c758b467c65ac98df0740c3a0146a2462d1.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt
index 8bdecb5..e0f8de7 100644
--- a/chrome/build/win-arm64.pgo.txt
+++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@
-chrome-win-arm64-main-1746619131-846a6c3eac1af9e8d77dfc02e863a0f37f6238c6-413ccd12abe49ad79521af08e38c1208cc19f44a.profdata
+chrome-win-arm64-main-1746683835-669257521af98d168da18a11972df7f1fc316149-1bd71c758b467c65ac98df0740c3a0146a2462d1.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index ff835751..23ebb43 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1746608342-d334b0886e62903e6e2e566b2cb4cd02de0e6fdf-deb8d871c2591efece685fe2695903c61259ee06.profdata
+chrome-win32-main-1746683835-c06420b66e2ecd8b25074e50e13f6986a1354986-1bd71c758b467c65ac98df0740c3a0146a2462d1.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 4f6f07a..648488a8 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1746586632-f8dce42004d63c7802f1074010b3c7c9b792793d-f351d8b201937d9402701c01bb3ba07eb2001ae0.profdata
+chrome-win64-main-1746662394-37830bc044293a0fdcf12190c695810885ce0102-fbacdb92137088dea8f53b8d1cbbe155a867b9d0.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 8dde0f01..644991d 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -415,17 +415,17 @@
 BASE_FEATURE_PARAM(std::string,
                    kGlicLauncherToggleLearnMoreURL,
                    &kGlicLearnMoreURLConfig,
-                   "glic-shortcuts-launcher-toggle-learn-more-url",
+                   "glic-launcher-toggle-learn-more-url",
                    "");
 BASE_FEATURE_PARAM(std::string,
                    kGlicLocationToggleLearnMoreURL,
                    &kGlicLearnMoreURLConfig,
-                   "glic-shortcuts-location-toggle-learn-more-url",
+                   "glic-location-toggle-learn-more-url",
                    "");
 BASE_FEATURE_PARAM(std::string,
                    kGlicTabAccessToggleLearnMoreURL,
                    &kGlicLearnMoreURLConfig,
-                   "glic-shortcuts-tab-access-toggle-learn-more-url",
+                   "glic-tab-access-toggle-learn-more-url",
                    "");
 BASE_FEATURE_PARAM(std::string,
                    kGlicSettingsPageLearnMoreURL,
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index fbb8f4c..6a635fc 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -250,8 +250,6 @@
 extern const base::FeatureParam<std::string> kGlicLocationToggleLearnMoreURL;
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::FeatureParam<std::string> kGlicTabAccessToggleLearnMoreURL;
-COMPONENT_EXPORT(CHROME_FEATURES)
-extern const base::FeatureParam<std::string> kGlicSettingsPageLearnMoreURL;
 
 COMPONENT_EXPORT(CHROME_FEATURES) BASE_DECLARE_FEATURE(kGlicCSPConfig);
 COMPONENT_EXPORT(CHROME_FEATURES)
diff --git a/chrome/common/extensions/api/api_sources.gni b/chrome/common/extensions/api/api_sources.gni
index e032ce6..8f1d5db 100644
--- a/chrome/common/extensions/api/api_sources.gni
+++ b/chrome/common/extensions/api/api_sources.gni
@@ -13,6 +13,7 @@
 # TODO(https://crbug.com/356905053): Continue moving more here.
 schema_sources_ = [
   "activity_log_private.json",
+  "bookmarks.json",
   "cookies.json",
   "developer_private.idl",
   "identity.idl",
@@ -47,7 +48,6 @@
     "autofill_private.idl",
     "autotest_private.idl",
     "bookmark_manager_private.json",
-    "bookmarks.json",
     "braille_display_private.idl",
     "chrome_web_view_internal.json",
     "command_line_private.json",
diff --git a/chrome/common/extensions/api/bookmark_manager_private.json b/chrome/common/extensions/api/bookmark_manager_private.json
index df1ec62..812f3729 100644
--- a/chrome/common/extensions/api/bookmark_manager_private.json
+++ b/chrome/common/extensions/api/bookmark_manager_private.json
@@ -43,15 +43,6 @@
             "items": {"$ref": "BookmarkNodeDataElement"}
           }
         }
-      },
-      {
-        "id": "OpenInNewTabParams",
-        "type": "object",
-        "description": "Parameters for the Open In New Tab method",
-        "properties": {
-          "active": {"type": "boolean", "description": "Whether this tab should be active."},
-          "split": {"type": "boolean", "description": "Whether this tab should enter into a split view alongside the active tab."}
-        }
       }
     ],
     "functions": [
@@ -280,10 +271,9 @@
             "type": "string"
           },
           {
-            "name": "params",
-            "optional": true,
-            "$ref": "OpenInNewTabParams",
-            "description": "Parameters for the new tab the given bookmark is opened into."
+            "name": "active",
+            "description": "Whether this tab should be active.",
+            "type": "boolean"
           }
         ]
       },
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index ab5e819..3682d550 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -1260,7 +1260,7 @@
 // the user grants consent again, we will not record their metric in the
 // histogram
 // "ChromeOS.Settings.NumUniqueSettingsChanged.DeviceLifetime2.{Time}".
-const char kHasEverRevokedMetricsConsent[] =
+inline constexpr char kHasEverRevokedMetricsConsent[] =
     "settings.has_ever_revoked_metrics_consent";
 
 // A boolean to store that an admin user accessed the host device remotely when
diff --git a/chrome/elevation_service/elevator.h b/chrome/elevation_service/elevator.h
index 5b9bc81..44266ed 100644
--- a/chrome/elevation_service/elevator.h
+++ b/chrome/elevation_service/elevator.h
@@ -32,8 +32,9 @@
      0x57}};  // Elevator Test CLSID. {416C51AC-4DEF-43CA-96E8-E735210AB257}
 
 namespace switches {
-constexpr char kElevatorClsIdForTestingSwitch[] = "elevator-clsid-for-testing";
-constexpr char kFakeReencryptForTestingSwitch[] =
+inline constexpr char kElevatorClsIdForTestingSwitch[] =
+    "elevator-clsid-for-testing";
+inline constexpr char kFakeReencryptForTestingSwitch[] =
     "elevator-fake-reencrypt-for-testing";
 }  // namespace switches
 
diff --git a/chrome/renderer/accessibility/read_anything/read_aloud_app_model.cc b/chrome/renderer/accessibility/read_anything/read_aloud_app_model.cc
index fe7b9fe..e0203e6 100644
--- a/chrome/renderer/accessibility/read_anything/read_aloud_app_model.cc
+++ b/chrome/renderer/accessibility/read_anything/read_aloud_app_model.cc
@@ -589,7 +589,9 @@
 }
 
 int ReadAloudAppModel::GetCurrentTextStartIndex(const ui::AXNodeID& node_id) {
-  if (processed_granularities_on_current_page_.size() < 1) {
+  if (processed_granularities_on_current_page_.size() < 1 ||
+      processed_granularity_index_ >=
+          processed_granularities_on_current_page_.size()) {
     return -1;
   }
 
@@ -604,7 +606,9 @@
 }
 
 int ReadAloudAppModel::GetCurrentTextEndIndex(const ui::AXNodeID& node_id) {
-  if (processed_granularities_on_current_page_.size() < 1) {
+  if (processed_granularities_on_current_page_.size() < 1 ||
+      processed_granularity_index_ >=
+          processed_granularities_on_current_page_.size()) {
     return -1;
   }
 
diff --git a/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc b/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc
index 68bc30d..2fdea11 100644
--- a/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc
+++ b/chrome/renderer/accessibility/read_anything/read_anything_app_model.cc
@@ -208,10 +208,20 @@
     return need_to_draw;
   }
 
-  // The main panel selection contains content outside of the distilled content.
-  // Find the selected nodes to display instead of the distilled content.
-  if (const ui::AXNode *node = GetAXNode(start_.id), *end = GetAXNode(end_.id);
-      !node->IsInvisibleOrIgnored() && !end->IsInvisibleOrIgnored()) {
+  const ui::AXNode* node = GetAXNode(start_.id);
+  const ui::AXNode* end = GetAXNode(end_.id);
+  DUMP_WILL_BE_CHECK(node && end);
+  if (!node || !end) {
+    // Fail gracefully if the returned nodes are ever missing.
+    // This should never happen given that the AXSelection object is retrieved
+    // from the active tree.
+    return false;
+  }
+
+  // The main panel selection contains content outside of the distilled
+  // content. Find the selected nodes to display instead of the distilled
+  // content.
+  if (!node->IsInvisibleOrIgnored() && !end->IsInvisibleOrIgnored()) {
     // Add all ancestor ids of start node, including the start node itself.
     for (base::queue<ui::AXNode*> ancestors =
              node->GetAncestorsCrossingTreeBoundaryAsQueue();
diff --git a/chrome/renderer/actor/type_tool.cc b/chrome/renderer/actor/type_tool.cc
index 6e167d6..fa5dd00 100644
--- a/chrome/renderer/actor/type_tool.cc
+++ b/chrome/renderer/actor/type_tool.cc
@@ -32,6 +32,15 @@
 
 namespace actor {
 
+using ::blink::WebCoalescedInputEvent;
+using ::blink::WebElement;
+using ::blink::WebInputEvent;
+using ::blink::WebInputEventResult;
+using ::blink::WebKeyboardEvent;
+using ::blink::WebLocalFrame;
+using ::blink::WebNode;
+using ::blink::WebString;
+
 namespace {
 
 // Structure to hold the mapping
@@ -90,6 +99,13 @@
   return *key_info_map;
 }
 
+bool PrepareTargetForMode(WebLocalFrame& frame, mojom::TypeAction::Mode mode) {
+  // TODO(crbug.com/409570203): Use DELETE_EXISTING regardless of `mode` but
+  // we'll have to implement the different insertion modes.
+  frame.ExecuteCommand(WebString::FromUTF8("SelectAll"));
+  return true;
+}
+
 }  // namespace
 
 TypeTool::TypeTool(mojom::TypeActionPtr action, content::RenderFrame& frame)
@@ -132,7 +148,7 @@
     // dom_key is already set correctly (it's the uppercase char)
     // Unmodified is lowercase
     params.unmodified_text = base::ToLowerASCII(c);
-    params.modifiers = blink::WebInputEvent::kShiftKey;
+    params.modifiers = WebInputEvent::kShiftKey;
   } else if (c >= '0' && c <= '9') {
     // ASCII Digits
     params.windows_key_code = ui::VKEY_0 + (c - '0');
@@ -153,7 +169,7 @@
 
     // Check if this character requires shift
     if (info.unmodified_char != 0) {
-      params.modifiers = blink::WebInputEvent::kShiftKey;
+      params.modifiers = WebInputEvent::kShiftKey;
       params.unmodified_text = info.unmodified_char;
     }
   }
@@ -165,11 +181,10 @@
   return params;
 }
 
-blink::WebInputEventResult TypeTool::CreateAndDispatchKeyEvent(
-    blink::WebInputEvent::Type type,
+WebInputEventResult TypeTool::CreateAndDispatchKeyEvent(
+    WebInputEvent::Type type,
     KeyParams key_params) {
-  blink::WebKeyboardEvent key_event(type, key_params.modifiers,
-                                    ui::EventTimeForNow());
+  WebKeyboardEvent key_event(type, key_params.modifiers, ui::EventTimeForNow());
   key_event.windows_key_code = key_params.windows_key_code;
   key_event.native_key_code = key_params.native_key_code;
   key_event.dom_code = static_cast<int>(
@@ -179,17 +194,17 @@
   key_event.text[0] = key_params.text;
   key_event.unmodified_text[0] = key_params.unmodified_text;
 
-  blink::WebInputEventResult result =
+  WebInputEventResult result =
       frame_->GetWebFrame()->FrameWidget()->HandleInputEvent(
-          blink::WebCoalescedInputEvent(key_event, ui::LatencyInfo()));
+          WebCoalescedInputEvent(key_event, ui::LatencyInfo()));
 
   return result;
 }
 
 bool TypeTool::SimulateKeyPress(TypeTool::KeyParams params) {
   // TODO(crbug.com/402082693): Maybe add slight delay between events?
-  blink::WebInputEventResult down_result = CreateAndDispatchKeyEvent(
-      blink::WebInputEvent::Type::kRawKeyDown, params);
+  WebInputEventResult down_result =
+      CreateAndDispatchKeyEvent(WebInputEvent::Type::kRawKeyDown, params);
 
   // Only the KeyDown event will check for and report failure. The reason the
   // other events don't is that if the KeyDown event was dispatched to the page,
@@ -199,33 +214,27 @@
   // successful in terms that the tool has acted on the page. In particular, a
   // preventDefault()'ed KeyDown event will force suppressing the following Char
   // event but this is expected and common.
-  if (down_result == blink::WebInputEventResult::kHandledSuppressed) {
+  if (down_result == WebInputEventResult::kHandledSuppressed) {
     ACTOR_LOG() << "KeyDown event for key " << params.dom_key << " suppressed.";
     return false;
   }
 
-  blink::WebInputEventResult char_result =
-      CreateAndDispatchKeyEvent(blink::WebInputEvent::Type::kChar, params);
-  if (char_result == blink::WebInputEventResult::kHandledSuppressed) {
+  WebInputEventResult char_result =
+      CreateAndDispatchKeyEvent(WebInputEvent::Type::kChar, params);
+  if (char_result == WebInputEventResult::kHandledSuppressed) {
     ACTOR_LOG() << "Warning: Char event for key " << params.dom_key
                 << " suppressed.";
   }
 
-  blink::WebInputEventResult up_result =
-      CreateAndDispatchKeyEvent(blink::WebInputEvent::Type::kKeyUp, params);
-  if (up_result == blink::WebInputEventResult::kHandledSuppressed) {
+  WebInputEventResult up_result =
+      CreateAndDispatchKeyEvent(WebInputEvent::Type::kKeyUp, params);
+  if (up_result == WebInputEventResult::kHandledSuppressed) {
     ACTOR_LOG() << "Warning: KeyUp event for key " << params.dom_key
                 << " suppressed.";
   }
   return true;
 }
 
-bool TypeTool::PrepareTargetForMode(const blink::WebNode& node,
-                                    mojom::TypeAction::Mode mode) {
-  // TODO(crbug.com/409570203): Implement.
-  return true;
-}
-
 void TypeTool::Execute(ToolFinishedCallback callback) {
   if (!frame_->GetWebFrame() || !frame_->GetWebFrame()->FrameWidget()) {
     ACTOR_LOG() << "RenderFrame or FrameWidget is invalid.";
@@ -243,7 +252,7 @@
   }
   int32_t dom_node_id = target->get_dom_node_id();
 
-  blink::WebNode node = GetNodeFromId(frame_.get(), dom_node_id);
+  WebNode node = GetNodeFromId(frame_.get(), dom_node_id);
   if (node.IsNull()) {
     ACTOR_LOG() << "Cannot find dom node with id " << dom_node_id;
     std::move(callback).Run(false);
@@ -251,12 +260,13 @@
   }
 
   // Validate Node is an editable element
+  // TODO(crbug.com/414398425): This seems too restrictive for non-input cases.
   if (!node.IsElementNode()) {
     ACTOR_LOG() << "Target node " << node << " is not an element.";
     std::move(callback).Run(false);
     return;
   }
-  blink::WebElement element = node.To<blink::WebElement>();
+  WebElement element = node.To<WebElement>();
   if (!element.IsEditable()) {
     ACTOR_LOG() << "Target element " << element << " is not editable.";
     std::move(callback).Run(false);
@@ -275,13 +285,19 @@
     }
   }
 
-  if (!PrepareTargetForMode(node, action_->mode)) {
+  if (!PrepareTargetForMode(*frame_->GetWebFrame(), action_->mode)) {
     ACTOR_LOG() << "Failed to prepare target element based on mode: "
                 << action_->mode;
     std::move(callback).Run(false);
     return;
   }
 
+  // Note: Focus and preparing the target performs actions which lead to script
+  // execution so `node` may no longer be focused (it or its frame could be
+  // disconnected). However, sites sometimes do unexpected things to work around
+  // issues so to keep those working we proceed to key dispatch without checking
+  // this.
+
   if (!base::IsStringASCII(action_->text)) {
     // TODO(crbug.com/409032824): Add support beyond ASCII.
     ACTOR_LOG() << "Characters beyond ASCII not supported" << action_->text;
diff --git a/chrome/renderer/actor/type_tool.h b/chrome/renderer/actor/type_tool.h
index 77e0aff..68095b2 100644
--- a/chrome/renderer/actor/type_tool.h
+++ b/chrome/renderer/actor/type_tool.h
@@ -14,10 +14,6 @@
 #include "third_party/blink/public/common/input/web_input_event.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
 
-namespace blink {
-class WebNode;
-}  // namespace blink
-
 namespace content {
 class RenderFrame;
 }  // namespace content
@@ -61,11 +57,6 @@
       KeyParams key_params);
   bool SimulateKeyPress(TypeTool::KeyParams params);
 
-  // Attempts to prepare the target element based on the TypeMode.
-  // Returns true on success, false on failure.
-  bool PrepareTargetForMode(const blink::WebNode& node,
-                            mojom::TypeAction::Mode mode);
-
   // Raw ref since this is owned by ToolExecutor whose lifetime is tied to
   // RenderFrame.
   base::raw_ref<content::RenderFrame> frame_;
diff --git a/chrome/services/ipp_parser/public/cpp/ipp_converter.h b/chrome/services/ipp_parser/public/cpp/ipp_converter.h
index 66c4f3b..9ac68b4 100644
--- a/chrome/services/ipp_parser/public/cpp/ipp_converter.h
+++ b/chrome/services/ipp_parser/public/cpp/ipp_converter.h
@@ -29,10 +29,10 @@
 using HttpHeader = std::pair<std::string, std::string>;
 
 // Carriage return; Http header-pair delimiter.
-const char kCarriage[] = "\r\n";
+inline constexpr char kCarriage[] = "\r\n";
 
 // Defined IPP end-of-message sentinel.
-const char kIppSentinel[] = "\x03";
+inline constexpr char kIppSentinel[] = "\x03";
 
 // Request line converters
 // Parses |status_line| into vector of 3, individual terms, returns empty
diff --git a/chrome/services/speech/soda/soda_test_paths.h b/chrome/services/speech/soda/soda_test_paths.h
index 3264a5e4..c83f7d0 100644
--- a/chrome/services/speech/soda/soda_test_paths.h
+++ b/chrome/services/speech/soda/soda_test_paths.h
@@ -12,42 +12,42 @@
 
 #if BUILDFLAG(IS_MAC)
 
-constexpr base::FilePath::CharType kSodaResourcePath[] =
+inline constexpr base::FilePath::CharType kSodaResourcePath[] =
     FILE_PATH_LITERAL("third_party/soda-mac64/resources");
 
-constexpr base::FilePath::CharType kSodaTestBinaryRelativePath[] =
+inline constexpr base::FilePath::CharType kSodaTestBinaryRelativePath[] =
     FILE_PATH_LITERAL("libsoda_for_testing.so");
 
 #elif BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS)
 
-constexpr base::FilePath::CharType kSodaResourcePath[] =
+inline constexpr base::FilePath::CharType kSodaResourcePath[] =
     FILE_PATH_LITERAL("third_party/soda-win64/resources");
 
-constexpr base::FilePath::CharType kSodaTestBinaryRelativePath[] =
+inline constexpr base::FilePath::CharType kSodaTestBinaryRelativePath[] =
     FILE_PATH_LITERAL("SODA_for_testing.dll");
 
 #elif BUILDFLAG(IS_WIN) && defined(ARCH_CPU_32_BITS)
 
-constexpr base::FilePath::CharType kSodaResourcePath[] =
+inline constexpr base::FilePath::CharType kSodaResourcePath[] =
     FILE_PATH_LITERAL("third_party/soda-win32/resources");
 
-constexpr base::FilePath::CharType kSodaTestBinaryRelativePath[] =
+inline constexpr base::FilePath::CharType kSodaTestBinaryRelativePath[] =
     FILE_PATH_LITERAL("SODA_for_testing.dll");
 
 #elif BUILDFLAG(IS_LINUX)
 
-constexpr base::FilePath::CharType kSodaResourcePath[] =
+inline constexpr base::FilePath::CharType kSodaResourcePath[] =
     FILE_PATH_LITERAL("third_party/soda/resources");
 
-constexpr base::FilePath::CharType kSodaTestBinaryRelativePath[] =
+inline constexpr base::FilePath::CharType kSodaTestBinaryRelativePath[] =
     FILE_PATH_LITERAL("libsoda_for_testing.so");
 
 #endif
 
-constexpr base::FilePath::CharType kSodaLanguagePackRelativePath[] =
+inline constexpr base::FilePath::CharType kSodaLanguagePackRelativePath[] =
     FILE_PATH_LITERAL("en_us");
 
-constexpr base::FilePath::CharType kSodaTestAudioRelativePath[] =
+inline constexpr base::FilePath::CharType kSodaTestAudioRelativePath[] =
     FILE_PATH_LITERAL("hey_google.wav");
 
 }  // namespace soda
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 208c9cef..422673a 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -1668,7 +1668,6 @@
       "../browser/password_manager/passwords_navigation_observer.cc",
       "../browser/password_manager/passwords_navigation_observer.h",
       "../browser/policy/test/v8_optimizer_policy_browsertest.cc",
-      "../browser/privacy_sandbox/privacy_sandbox_attestations/privacy_sandbox_attestations_component_installer_android_browsertest.cc",
       "../browser/profiles/profile_browsertest_android.cc",
       "../browser/ssl/chrome_security_state_client_browsertest.cc",
       "../browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_browsertest.cc",
@@ -2941,6 +2940,7 @@
     data += metric_integration_jsdeps
 
     sources = [
+      "../../components/live_caption/views/caption_bubble_browsertest.cc",
       "../browser/accessibility/accessibility_labels_service_browsertest.cc",
       "../browser/accessibility/ax_main_node_annotator_controller_browsertest.cc",
       "../browser/accessibility/image_annotation_browsertest.cc",
@@ -6391,8 +6391,6 @@
       "../browser/site_protection/site_protection_metrics_observer_unittest.cc",
       "../browser/ssl/generated_https_first_mode_pref_unittest.cc",
       "../browser/ui/passwords/password_cross_domain_confirmation_popup_controller_impl_unittest.cc",
-      "../browser/ui/plus_addresses/plus_address_creation_controller_desktop_unittest.cc",
-      "../browser/ui/plus_addresses/plus_address_menu_model_unittest.cc",
       "../browser/ui/task_manager/task_manager_table_model_unittest.cc",
       "../browser/ui/user_education/recent_session_policy_unittest.cc",
       "../browser/ui/webui/profile_internals/profile_internals_handler_unittest.cc",
@@ -6680,6 +6678,7 @@
     "//chrome/browser/ui/hats",
     "//chrome/browser/ui/login:unit_tests",
     "//chrome/browser/ui/page_info:unit_tests",
+    "//chrome/browser/ui/plus_addresses:unit_tests",
     "//chrome/browser/ui/safety_hub:test_support",
     "//chrome/browser/ui/serial:test_support",
     "//chrome/browser/ui/webui",
@@ -7749,6 +7748,7 @@
       "../browser/password_manager/password_change/change_form_submission_verifier_unittest.cc",
       "../browser/password_manager/password_change/change_password_form_finder_unittest.cc",
       "../browser/password_manager/password_change/change_password_form_waiter_unittest.cc",
+      "../browser/password_manager/password_change/model_quality_logs_uploader_unittest.cc",
       "../browser/password_manager/password_change_delegate_impl_unittest.cc",
       "../browser/performance_manager/execution_context_priority/side_panel_loading_voter_unittest.cc",
       "../browser/platform_util_unittest.cc",
@@ -8914,6 +8914,7 @@
   # Unit tests that run on Win/Mac/Linux and Android desktop.
   if (enable_extensions_core) {
     sources += [
+      "../browser/extensions/api/bookmarks/bookmarks_api_unittest.cc",
       "../browser/extensions/api/declarative_net_request/action_tracker_unittest.cc",
       "../browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc",
       "../browser/extensions/api/declarative_net_request/dnr_test_base.cc",
@@ -8924,6 +8925,8 @@
       "../browser/extensions/api/storage/policy_value_store_unittest.cc",
       "../browser/extensions/api/storage/settings_sync_unittest.cc",
       "../browser/extensions/api/storage/storage_session_unittest.cc",
+      "../browser/extensions/api/web_request/web_request_api_unittest.cc",
+      "../browser/extensions/api/web_request/web_request_event_details_unittest.cc",
       "../browser/extensions/api/webstore_private/extension_install_status_unittest.cc",
       "../browser/extensions/api/webstore_private/webstore_private_unittest.cc",
       "../browser/extensions/chrome_content_verifier_unittest.cc",
@@ -9022,7 +9025,6 @@
       "../browser/extensions/activity_log/fullstream_ui_policy_unittest.cc",
       "../browser/extensions/api/activity_log_private/activity_log_private_api_unittest.cc",
       "../browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc",
-      "../browser/extensions/api/bookmarks/bookmarks_api_unittest.cc",
       "../browser/extensions/api/browsing_data/browsing_data_unittest.cc",
       "../browser/extensions/api/chrome_extensions_api_client_unittest.cc",
       "../browser/extensions/api/cookies/cookies_helpers_unittest.cc",
@@ -9092,8 +9094,6 @@
       "../browser/extensions/api/tabs/tabs_api_unittest.cc",
       "../browser/extensions/api/tabs/windows_util_unittest.cc",
       "../browser/extensions/api/web_navigation/frame_navigation_state_unittest.cc",
-      "../browser/extensions/api/web_request/web_request_api_unittest.cc",
-      "../browser/extensions/api/web_request/web_request_event_details_unittest.cc",
       "../browser/extensions/api/web_request/web_request_permissions_unittest.cc",
       "../browser/extensions/blocklist_check_unittest.cc",
       "../browser/extensions/blocklist_extension_prefs_unittest.cc",
@@ -10888,8 +10888,6 @@
       "../browser/ui/omnibox/omnibox_metrics_browsertest.cc",
       "../browser/ui/omnibox/omnibox_view_browsertest.cc",
       "../browser/ui/performance_controls/tab_resource_usage_tab_helper_interactive_uitest.cc",
-      "../browser/ui/plus_addresses/plus_address_error_dialog_interactive_uitest.cc",
-      "../browser/ui/plus_addresses/views/plus_address_creation_dialog_interactive_uitest.cc",
       "../browser/ui/search/instant_extended_interactive_uitest.cc",
       "../browser/ui/search/instant_test_base.cc",
       "../browser/ui/search/instant_test_base.h",
@@ -11050,6 +11048,7 @@
       "//chrome/browser/ui/lens:interactive_ui_tests",
       "//chrome/browser/ui/omnibox",
       "//chrome/browser/ui/page_action:icon_type",
+      "//chrome/browser/ui/plus_addresses:interactive_ui_tests",
       "//chrome/browser/ui/privacy_sandbox",
       "//chrome/browser/ui/startup:startup_tab",
       "//chrome/browser/ui/tab_contents",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/Journeys.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/Journeys.java
index 7c7250a..bc8882c6 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/Journeys.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/Journeys.java
@@ -214,12 +214,17 @@
             PageStation previousPage = currentPage;
             Tab previousTab = previousPage.loadedTabElement.get();
             if (i == 0 && startingPage.isIncognito() && !isIncognito) {
-                currentPage = currentPage.openNewTabFast().loadWebPageProgrammatically(url);
+                currentPage =
+                        currentPage
+                                .openNewTabFast()
+                                .loadPageProgrammatically(url, pageStationFactory.get());
             } else if (i == 0 && !startingPage.isIncognito() && isIncognito) {
                 currentPage =
-                        currentPage.openNewIncognitoTabFast().loadWebPageProgrammatically(url);
+                        currentPage
+                                .openNewIncognitoTabFast()
+                                .loadPageProgrammatically(url, pageStationFactory.get());
             } else {
-                currentPage = currentPage.openFakeLinkToWebPage(url);
+                currentPage = currentPage.openFakeLink(url, pageStationFactory.get());
             }
 
             if (!captureThumbnails) {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HubBaseStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HubBaseStation.java
index 75444a2..91fc281 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HubBaseStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/HubBaseStation.java
@@ -32,6 +32,7 @@
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.base.test.transit.ViewSpec;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
+import org.chromium.chrome.browser.hub.HubToolbarView;
 import org.chromium.chrome.browser.hub.PaneId;
 import org.chromium.chrome.browser.hub.R;
 import org.chromium.chrome.browser.layouts.LayoutType;
@@ -42,15 +43,14 @@
 
 /** The base station for Hub, with several panes and a toolbar. */
 public abstract class HubBaseStation extends Station<ChromeTabbedActivity> {
-    public static final ViewSpec<View> HUB_TOOLBAR = viewSpec(withId(R.id.hub_toolbar));
+    public static final ViewSpec<HubToolbarView> HUB_TOOLBAR =
+            viewSpec(HubToolbarView.class, withId(R.id.hub_toolbar));
     public static final ViewSpec<View> HUB_PANE_HOST = viewSpec(withId(R.id.hub_pane_host));
-    public static final ViewSpec<View> HUB_MENU_BUTTON =
-            HUB_TOOLBAR.descendant(withId(org.chromium.chrome.R.id.menu_button));
     public static final ViewSpec<TabLayout> HUB_PANE_SWITCHER =
             HUB_TOOLBAR.descendant(TabLayout.class, withId(R.id.pane_switcher));
 
     public final Element<TabModelSelector> tabModelSelectorElement;
-    public final ViewElement<View> toolbarElement;
+    public final ViewElement<HubToolbarView> toolbarElement;
     public final ViewElement<View> paneHostElement;
     public final ViewElement<View> menuButtonElement;
     public final ViewElement<TabLayout> paneSwitcherElement;
@@ -70,7 +70,10 @@
 
         toolbarElement = declareView(HUB_TOOLBAR);
         paneHostElement = declareView(HUB_PANE_HOST);
-        menuButtonElement = hasMenuButton ? declareView(HUB_MENU_BUTTON) : null;
+        menuButtonElement =
+                hasMenuButton
+                        ? declareView(toolbarElement.descendant(withId(R.id.menu_button)))
+                        : null;
 
         paneSwitcherElement = declareView(HUB_PANE_SWITCHER);
 
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupDialogFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupDialogFacility.java
index c2a3e8c..90e2b33d 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupDialogFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabGroupDialogFacility.java
@@ -44,18 +44,16 @@
  */
 public class TabGroupDialogFacility<HostStationT extends Station<ChromeTabbedActivity>>
         extends Facility<HostStationT> {
-    public static final ViewSpec<View> TOOLBAR = viewSpec(withId(R.id.tab_group_toolbar));
-
     public static final ViewSpec<View> TABS_LIST =
             viewSpec(
                     withId(R.id.tab_list_recycler_view),
                     withParent(withId(R.id.tab_grid_dialog_recycler_view_container)));
-    public static final ViewSpec SHARE_BUTTON = TOOLBAR.descendant(withId(R.id.share_button));
 
     private final List<Integer> mTabIdsInGroup;
     private final String mTitle;
     private final boolean mIsIncognito;
     private final @Nullable @TabGroupColorId Integer mSelectedColor;
+    public ViewElement<View> toolbarElement;
     public ViewElement<View> shareButtonElement;
     public ViewElement<View> tabsListElement;
     public ViewElement<View> colorIconElement;
@@ -86,6 +84,41 @@
         mTitle = title;
         mSelectedColor = selectedColor;
         mIsIncognito = isIncognito;
+
+        toolbarElement = declareView(viewSpec(withId(R.id.tab_group_toolbar)));
+        tabsListElement = declareView(TABS_LIST);
+        colorIconElement =
+                declareView(toolbarElement.descendant(withId(R.id.tab_group_color_icon_container)));
+        titleInputElement =
+                declareView(
+                        toolbarElement.descendant(
+                                withId(R.id.title),
+                                isAssignableFrom(EditText.class),
+                                withText(mTitle)));
+        newTabButtonElement =
+                declareView(toolbarElement.descendant(withId(R.id.toolbar_new_tab_button)));
+        backButtonElement =
+                declareView(toolbarElement.descendant(withId(R.id.toolbar_back_button)));
+    }
+
+    @Override
+    public void declareElements(Elements.Builder elements) {
+        if (ChromeFeatureList.isEnabled(ChromeFeatureList.DATA_SHARING)) {
+            // TODO(ckitagawa): Add handling for an already shared group.
+            if (isAllowedToShare()) {
+                shareButtonElement =
+                        declareView(toolbarElement.descendant(withId(R.id.share_button)));
+            }
+
+            // Data sharing layout causes the menu button to be hidden due to the rounded corner.
+            listMenuButtonElement =
+                    declareView(
+                            toolbarElement.descendant(withId(R.id.toolbar_menu_button)),
+                            ViewElement.displayingAtLeastOption(51));
+        } else {
+            listMenuButtonElement =
+                    declareView(toolbarElement.descendant(withId(R.id.toolbar_menu_button)));
+        }
     }
 
     private boolean isAllowedToShare() {
@@ -104,40 +137,6 @@
                                 .isAllowedToCreate());
     }
 
-    @Override
-    public void declareElements(Elements.Builder elements) {
-        tabsListElement = elements.declareView(TABS_LIST);
-        colorIconElement =
-                elements.declareView(
-                        TOOLBAR.descendant(withId(R.id.tab_group_color_icon_container)));
-        titleInputElement =
-                elements.declareView(
-                        TOOLBAR.descendant(
-                                withId(R.id.title),
-                                isAssignableFrom(EditText.class),
-                                withText(mTitle)));
-        newTabButtonElement =
-                elements.declareView(TOOLBAR.descendant(withId(R.id.toolbar_new_tab_button)));
-        backButtonElement =
-                elements.declareView(TOOLBAR.descendant(withId(R.id.toolbar_back_button)));
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.DATA_SHARING)) {
-            // TODO(ckitagawa): Add handling for an already shared group.
-            if (isAllowedToShare()) {
-                shareButtonElement =
-                        elements.declareView(TOOLBAR.descendant(withId(R.id.share_button)));
-            }
-
-            // Data sharing layout causes the menu button to be hidden due to the rounded corner.
-            listMenuButtonElement =
-                    elements.declareView(
-                            TOOLBAR.descendant(withId(R.id.toolbar_menu_button)),
-                            ViewElement.displayingAtLeastOption(51));
-        } else {
-            listMenuButtonElement =
-                    elements.declareView(TOOLBAR.descendant(withId(R.id.toolbar_menu_button)));
-        }
-    }
-
     /** Input a new group name. */
     public TabGroupDialogFacility<HostStationT> inputName(String newTabGroupName) {
         return mHostStation.swapFacilitySync(
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherCardFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherCardFacility.java
index bce8ae3..497e6bff 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherCardFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherCardFacility.java
@@ -63,7 +63,6 @@
     }
 
     protected ViewElement<View> declareActionButton(Elements.Builder elements) {
-        return elements.declareView(
-                cardViewElement.getViewSpec().descendant(withId(R.id.action_button)));
+        return elements.declareView(cardViewElement.descendant(withId(R.id.action_button)));
     }
 }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherStation.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherStation.java
index addd7a7c..5e676a51 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherStation.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/hub/TabSwitcherStation.java
@@ -24,13 +24,11 @@
 import org.hamcrest.Matcher;
 
 import org.chromium.base.test.transit.Condition;
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Transition;
 import org.chromium.base.test.transit.ViewElement;
 import org.chromium.base.test.transit.ViewSpec;
 import org.chromium.base.test.util.ViewActionOnDescendant;
 import org.chromium.chrome.browser.hub.HubToolbarMediator;
-import org.chromium.chrome.browser.hub.HubToolbarView;
 import org.chromium.chrome.browser.hub.PaneId;
 import org.chromium.chrome.browser.tabmodel.TabModel;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -47,8 +45,6 @@
 public abstract class TabSwitcherStation extends HubBaseStation {
     public static final ViewSpec<RecyclerView> TAB_LIST_RECYCLER_VIEW =
             HUB_PANE_HOST.descendant(RecyclerView.class, withId(R.id.tab_list_recycler_view));
-
-    public static final ViewSpec<View> TOOLBAR = viewSpec(instanceOf(HubToolbarView.class));
     public static final ViewSpec<View> TAB_GROUP_COLOR_ICON_VIEW =
             viewSpec(
                     allOf(
@@ -81,20 +77,16 @@
             boolean isIncognito, boolean regularTabsExist, boolean incognitoTabsExist) {
         super(regularTabsExist, incognitoTabsExist, /* hasMenuButton= */ true);
         mIsIncognito = isIncognito;
-    }
-
-    @Override
-    public void declareElements(Elements.Builder elements) {
-        super.declareElements(elements);
 
         newTabButtonElement =
-                elements.declareView(TOOLBAR.descendant(withId(R.id.toolbar_action_button)));
+                declareView(toolbarElement.descendant(withId(R.id.toolbar_action_button)));
         if (OmniboxFeatures.sAndroidHubSearch.isEnabled()) {
-            elements.declareElementFactory(
+            declareElementFactory(
                     mActivityElement,
                     delayedElements -> {
                         ViewSpec<View> searchBox = viewSpec(withId(R.id.search_box));
-                        ViewSpec<View> searchLoupe = TOOLBAR.descendant(withId(R.id.search_loupe));
+                        ViewSpec<View> searchLoupe =
+                                toolbarElement.descendant(withId(R.id.search_loupe));
                         if (shouldHubSearchBoxBeVisible()) {
                             searchElement = delayedElements.declareView(searchLoupe);
                             delayedElements.declareNoView(searchBox);
@@ -104,7 +96,7 @@
                         }
                     });
         }
-        recyclerViewElement = elements.declareView(TAB_LIST_RECYCLER_VIEW);
+        recyclerViewElement = declareView(TAB_LIST_RECYCLER_VIEW);
     }
 
     public boolean isIncognito() {
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/TabSwitcherActionMenuFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/TabSwitcherActionMenuFacility.java
index a11fdb4..6c5d624f 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/TabSwitcherActionMenuFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/page/TabSwitcherActionMenuFacility.java
@@ -21,7 +21,6 @@
 import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.Transition;
 import org.chromium.base.test.transit.ViewElement;
-import org.chromium.base.test.transit.ViewSpec;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
@@ -42,30 +41,30 @@
 
     @Override
     public void declareElements(Elements.Builder elements) {
-        ViewSpec<View> menuList = viewSpec(withId(R.id.app_menu_list));
-        appMenuListElement = elements.declareView(menuList);
+        appMenuListElement = declareView(viewSpec(withId(R.id.app_menu_list)));
         closeTabMenuItemElement =
-                elements.declareView(menuList.descendant(withText(R.string.close_tab)));
+                declareView(appMenuListElement.descendant(withText(R.string.close_tab)));
         newTabMenuItemElement =
-                elements.declareView(menuList.descendant(withText(R.string.menu_new_tab)));
+                declareView(appMenuListElement.descendant(withText(R.string.menu_new_tab)));
         newIncognitoTabMenuItemElement =
-                elements.declareView(
-                        menuList.descendant(withText(R.string.menu_new_incognito_tab)));
+                declareView(
+                        appMenuListElement.descendant(withText(R.string.menu_new_incognito_tab)));
 
         if (ChromeFeatureList.sTabStripIncognitoMigration.isEnabled()) {
             if (mHostStation.isIncognito()
                     && mHostStation.getActivity().getTabModelSelector().getModel(false).getCount()
                             > 0) {
                 switchOutOfIncognitoMenuItemElement =
-                        elements.declareView(
-                                menuList.descendant(
+                        declareView(
+                                appMenuListElement.descendant(
                                         withText(R.string.menu_switch_out_of_incognito)));
             } else if (!mHostStation.isIncognito()
                     && mHostStation.getActivity().getTabModelSelector().getModel(true).getCount()
                             > 0) {
                 switchToIncognitoMenuItemElement =
-                        elements.declareView(
-                                menuList.descendant(withText(R.string.menu_switch_to_incognito)));
+                        declareView(
+                                appMenuListElement.descendant(
+                                        withText(R.string.menu_switch_to_incognito)));
             }
         }
     }
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/quick_delete/QuickDeleteDialogFacility.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/quick_delete/QuickDeleteDialogFacility.java
index 444b7c1..24c8545 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/quick_delete/QuickDeleteDialogFacility.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/quick_delete/QuickDeleteDialogFacility.java
@@ -18,7 +18,6 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.transit.ConditionStatus;
-import org.chromium.base.test.transit.Elements;
 import org.chromium.base.test.transit.Facility;
 import org.chromium.base.test.transit.Station;
 import org.chromium.base.test.transit.UiThreadCondition;
@@ -54,41 +53,39 @@
 
     public QuickDeleteDialogFacility(@TimePeriod int timePeriod) {
         mTimePeriod = timePeriod;
-    }
 
-    @Override
-    public void declareElements(Elements.Builder elements) {
-        ViewSpec<ModalDialogView> dialog =
-                viewSpec(ModalDialogView.class, withId(R.id.modal_dialog_view));
-        dialogElement = elements.declareView(dialog);
+        dialogElement =
+                declareView(viewSpec(ModalDialogView.class, withId(R.id.modal_dialog_view)));
         customViewElement =
-                elements.declareView(dialog.descendant(withId(R.id.custom_view_not_in_scrollable)));
-        elements.declareView(dialog.descendant(withText(R.string.quick_delete_dialog_title)));
+                declareView(dialogElement.descendant(withId(R.id.custom_view_not_in_scrollable)));
+        declareView(dialogElement.descendant(withText(R.string.quick_delete_dialog_title)));
         spinnerElement =
-                elements.declareView(
-                        dialog.descendant(Spinner.class, withId(R.id.quick_delete_spinner)));
+                declareView(
+                        dialogElement.descendant(Spinner.class, withId(R.id.quick_delete_spinner)));
         historyInfoElement =
-                elements.declareView(
-                        dialog.descendant(
+                declareView(
+                        dialogElement.descendant(
                                 TextView.class, withId(R.id.quick_delete_history_row_title)));
         tabsInfoElement =
-                elements.declareView(
-                        dialog.descendant(TextView.class, withId(R.id.quick_delete_tabs_row_title)),
+                declareView(
+                        dialogElement.descendant(
+                                TextView.class, withId(R.id.quick_delete_tabs_row_title)),
                         ViewElement.allowDisabledOption());
-        elements.declareView(
-                dialog.descendant(
+        declareView(
+                dialogElement.descendant(
                         withText(
                                 R.string
                                         .quick_delete_dialog_cookies_cache_and_other_site_data_text)));
         moreOptionsElement =
-                elements.declareView(dialog.descendant(withId(R.id.quick_delete_more_options)));
+                declareView(dialogElement.descendant(withId(R.id.quick_delete_more_options)));
         cancelButtonElement =
-                elements.declareView(
-                        dialog.descendant(withId(R.id.negative_button), withText("Cancel")));
+                declareView(
+                        dialogElement.descendant(withId(R.id.negative_button), withText("Cancel")));
         deleteButtonElement =
-                elements.declareView(
-                        dialog.descendant(withId(R.id.positive_button), withText("Delete data")));
-        elements.declareEnterCondition(new TimePeriodSelectedCondition());
+                declareView(
+                        dialogElement.descendant(
+                                withId(R.id.positive_button), withText("Delete data")));
+        declareEnterCondition(new TimePeriodSelectedCondition());
     }
 
     /** Click Cancel to close the dialog with no action. */
@@ -183,43 +180,25 @@
 
     public class SearchHistoryDisambiguiationFacility
             extends Facility<Station<ChromeTabbedActivity>> {
-        private final boolean mExpectPresent;
-
         public SearchHistoryDisambiguiationFacility(boolean expectPresent) {
-            mExpectPresent = expectPresent;
-        }
-
-        @Override
-        public void declareElements(Elements.Builder elements) {
             ViewSpec<View> spec =
-                    dialogElement
-                            .getViewSpec()
-                            .descendant(withId(R.id.search_history_disambiguation));
-            if (mExpectPresent) {
-                elements.declareView(spec);
+                    dialogElement.descendant(withId(R.id.search_history_disambiguation));
+            if (expectPresent) {
+                declareView(spec);
             } else {
-                elements.declareNoView(spec);
+                declareNoView(spec);
             }
         }
     }
 
     public class SitesSubtitleFacility extends Facility<Station<ChromeTabbedActivity>> {
-        private final boolean mExpectPresent;
-
         public SitesSubtitleFacility(boolean expectPresent) {
-            mExpectPresent = expectPresent;
-        }
-
-        @Override
-        public void declareElements(Elements.Builder elements) {
             ViewSpec spec =
-                    dialogElement
-                            .getViewSpec()
-                            .descendant(withId(R.id.quick_delete_history_row_subtitle));
-            if (mExpectPresent) {
-                elements.declareView(spec);
+                    dialogElement.descendant(withId(R.id.quick_delete_history_row_subtitle));
+            if (expectPresent) {
+                declareView(spec);
             } else {
-                elements.declareNoView(spec);
+                declareNoView(spec);
             }
         }
     }
diff --git a/chrome/test/chromedriver/test/run_py_tests.pydeps b/chrome/test/chromedriver/test/run_py_tests.pydeps
index a3d6407b..af399c7c 100644
--- a/chrome/test/chromedriver/test/run_py_tests.pydeps
+++ b/chrome/test/chromedriver/test/run_py_tests.pydeps
@@ -53,7 +53,6 @@
 ../../../../third_party/catapult/devil/devil/devil_env.py
 ../../../../third_party/catapult/devil/devil/utils/__init__.py
 ../../../../third_party/catapult/devil/devil/utils/cmd_helper.py
-../../../../third_party/catapult/devil/devil/utils/host_utils.py
 ../../../../third_party/catapult/devil/devil/utils/lazy/__init__.py
 ../../../../third_party/catapult/devil/devil/utils/lazy/weak_constant.py
 ../../../../third_party/catapult/devil/devil/utils/logging_common.py
diff --git a/chrome/test/data/actor/input.html b/chrome/test/data/actor/input.html
index 47d8ea7..8562cd5a 100644
--- a/chrome/test/data/actor/input.html
+++ b/chrome/test/data/actor/input.html
@@ -13,6 +13,7 @@
     <!-- method=dialog prevents the form from navigating the page -->
     <form id="form" method="dialog">
       <input type="text" name="input" id="input">
+      <input type="text" name="input2" id="input2">
       <input type="submit" id="submit">
     </form>
     <script>
diff --git a/chrome/test/data/pdf/ink2_annotation_text_mixin_test.ts b/chrome/test/data/pdf/ink2_annotation_text_mixin_test.ts
index 577afa3..72ba65f6 100644
--- a/chrome/test/data/pdf/ink2_annotation_text_mixin_test.ts
+++ b/chrome/test/data/pdf/ink2_annotation_text_mixin_test.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {hexToColor, Ink2Manager, InkAnnotationTextMixin, TEXT_COLORS, TEXT_SIZES, TextAlignment, TextStyle} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {hexToColor, Ink2Manager, InkAnnotationTextMixin, TEXT_COLORS, TEXT_SIZES, TextAlignment, TextStyle, TextTypeface} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import {CrLitElement, html} from 'chrome://resources/lit/v3_0/lit.rollup.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
@@ -21,8 +21,8 @@
   override render() {
     return html`
       <select @change="${this.onTypefaceSelected}">
-        <option value="Roboto"></option>
-        <option value="Serif"></option>
+        <option value="${TextTypeface.SANS_SERIF}"></option>
+        <option value="${TextTypeface.SERIF}"></option>
       </select>
       <select @change="${this.onSizeSelected}">
         <option value="${TEXT_SIZES[0]}"></option>
@@ -32,24 +32,22 @@
   }
 }
 
-const initializationPromise = eventToPromise('attributes-changed', manager);
 customElements.define(TestElement.is, TestElement);
 const testElement = document.createElement('test-element') as TestElement;
 document.body.appendChild(testElement);
 
 chrome.test.runTests([
-  async function testInitialization() {
-    // The mixin should request the fonts from the backend initially, and
-    // update its text parameters based on the values in the manager. This
-    // is asynchronous (eventually, the fonts will be requested from the
-    // plugin).
-    const initEvent = await initializationPromise;
-    const expectedFonts = ['Roboto', 'Serif', 'Sans', 'Monospace'];
+  function testInitialization() {
+    // Test that the mixin initializes the fonts and other properties correctly.
+    const expectedFonts = [
+      TextTypeface.SANS_SERIF,
+      TextTypeface.SERIF,
+      TextTypeface.MONOSPACE,
+    ];
     assertDeepEquals(expectedFonts, testElement.fontNames);
-    assertDeepEquals(testElement.currentColor, initEvent.detail.color);
-    chrome.test.assertEq(
-        testElement.currentTypeface, initEvent.detail.typeface);
-    chrome.test.assertEq(testElement.currentSize, initEvent.detail.size);
+    assertDeepEquals({r: 0, b: 0, g: 0}, testElement.currentColor);
+    chrome.test.assertEq(TextTypeface.SANS_SERIF, testElement.currentTypeface);
+    chrome.test.assertEq(TEXT_SIZES[3]!, testElement.currentSize);
 
     chrome.test.succeed();
   },
@@ -71,11 +69,11 @@
     chrome.test.assertEq(2, selects.length);
     whenChanged = eventToPromise('attributes-changed', manager);
     const fontSelect = selects[0]!;
-    fontSelect.value = 'Serif';
+    fontSelect.value = TextTypeface.SERIF;
     fontSelect.dispatchEvent(
         new CustomEvent('change', {bubbles: true, composed: true}));
     changedEvent = await whenChanged;
-    chrome.test.assertEq('Serif', changedEvent.detail.typeface);
+    chrome.test.assertEq(TextTypeface.SERIF, changedEvent.detail.typeface);
 
     // Test firing a change event from a <select> with onSizeSelected
     // registered as the listener calls the manager and results in an event.
@@ -94,13 +92,12 @@
     // Initial state
     const initialColor = hexToColor(TEXT_COLORS[0]!.color);
     assertDeepEquals(initialColor, testElement.currentColor);
-    chrome.test.assertEq(12, testElement.currentSize);
-    // First font returned by the current dummy code.
-    chrome.test.assertEq('Roboto', testElement.currentTypeface);
+    chrome.test.assertEq(TEXT_SIZES[3]!, testElement.currentSize);
+    chrome.test.assertEq(TextTypeface.SANS_SERIF, testElement.currentTypeface);
 
     const newColor = hexToColor(TEXT_COLORS[1]!.color);
     testElement.onTextAttributesChanged({
-      typeface: 'Serif',
+      typeface: TextTypeface.SERIF,
       size: TEXT_SIZES[1]!,
       color: newColor,
       alignment: TextAlignment.LEFT,
@@ -111,7 +108,7 @@
     });
     assertDeepEquals(newColor, testElement.currentColor);
     chrome.test.assertEq(TEXT_SIZES[1]!, testElement.currentSize);
-    chrome.test.assertEq('Serif', testElement.currentTypeface);
+    chrome.test.assertEq(TextTypeface.SERIF, testElement.currentTypeface);
 
     chrome.test.succeed();
   },
@@ -125,11 +122,13 @@
     chrome.test.assertTrue(testElement.isSelectedSize(TEXT_SIZES[0]!));
 
     // Test that isSelectedTypeface returns the expected value.
-    chrome.test.assertTrue(testElement.isSelectedTypeface('Serif'));
-    chrome.test.assertFalse(testElement.isSelectedTypeface('Roboto'));
-    testElement.currentTypeface = 'Roboto';
-    chrome.test.assertFalse(testElement.isSelectedTypeface('Serif'));
-    chrome.test.assertTrue(testElement.isSelectedTypeface('Roboto'));
+    chrome.test.assertTrue(testElement.isSelectedTypeface(TextTypeface.SERIF));
+    chrome.test.assertFalse(
+        testElement.isSelectedTypeface(TextTypeface.SANS_SERIF));
+    testElement.currentTypeface = TextTypeface.SANS_SERIF;
+    chrome.test.assertFalse(testElement.isSelectedTypeface(TextTypeface.SERIF));
+    chrome.test.assertTrue(
+        testElement.isSelectedTypeface(TextTypeface.SANS_SERIF));
 
     chrome.test.succeed();
   },
diff --git a/chrome/test/data/pdf/ink2_manager_test.ts b/chrome/test/data/pdf/ink2_manager_test.ts
index 11cf6a6c..980beb6 100644
--- a/chrome/test/data/pdf/ink2_manager_test.ts
+++ b/chrome/test/data/pdf/ink2_manager_test.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import type {AnnotationBrush, TextAnnotation, TextAttributes, TextBoxInit} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
-import {AnnotationBrushType, DEFAULT_TEXTBOX_HEIGHT, DEFAULT_TEXTBOX_WIDTH, Ink2Manager, PluginController, PluginControllerEventType, TextAlignment} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {AnnotationBrushType, DEFAULT_TEXTBOX_HEIGHT, DEFAULT_TEXTBOX_WIDTH, Ink2Manager, PluginController, PluginControllerEventType, TextAlignment, TextTypeface} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
@@ -15,7 +15,7 @@
 function getTestAnnotation(): TextAnnotation {
   return {
     textAttributes: {
-      typeface: 'Roboto',
+      typeface: TextTypeface.SANS_SERIF,
       size: 12,
       color: {r: 0, g: 100, b: 0},
       alignment: TextAlignment.LEFT,
@@ -120,33 +120,6 @@
     chrome.test.succeed();
   },
 
-  async function testGetTextAnnotationFontNames() {
-    // Checks that requesting the fonts for the first time retrieves the
-    // fonts from the plugin and fires a attributes-changed event with the font
-    // set to the first font returned.
-    const whenChanged = eventToPromise('attributes-changed', manager);
-    const fontNames = await manager.getTextAnnotationFontNames();
-
-    // For now, these are hardcoded in controller.ts.
-    const expectedFontNames = ['Roboto', 'Serif', 'Sans', 'Monospace'];
-    chrome.test.assertEq(fontNames.length, expectedFontNames.length);
-    for (let i = 0; i < expectedFontNames.length; i++) {
-      chrome.test.assertEq(fontNames[i], expectedFontNames[i]);
-    }
-
-    // Check that the manager requested the fonts.
-    const getTextAnnotFontNamesMessage =
-        mockPlugin.findMessage('getTextAnnotFontNames');
-    chrome.test.assertTrue(getTextAnnotFontNamesMessage !== undefined);
-    chrome.test.assertEq(
-        'getTextAnnotFontNames', getTextAnnotFontNamesMessage.type);
-
-    // Check that an event was fired.
-    const changedEvent = await whenChanged;
-    chrome.test.assertEq('Roboto', changedEvent.detail.typeface);
-    chrome.test.succeed();
-  },
-
   function testSetFontProperties() {
     const fontUpdates: TextAttributes[] = [];
     manager.addEventListener('attributes-changed', e => {
@@ -164,9 +137,9 @@
 
     // Update font. Note the other `expectedAttributes` values come from the
     // defaults set in ink2_manager.ts.
-    manager.setTextTypeface('Serif');
+    manager.setTextTypeface(TextTypeface.SERIF);
     const expectedAttributes = {
-      typeface: 'Serif',
+      typeface: TextTypeface.SERIF,
       size: 12,
       color: {r: 0, g: 0, b: 0},
       alignment: TextAlignment.LEFT,
diff --git a/chrome/test/data/pdf/ink2_text_bottom_toolbar_test.ts b/chrome/test/data/pdf/ink2_text_bottom_toolbar_test.ts
index c1e6ddbd..ba693c3 100644
--- a/chrome/test/data/pdf/ink2_text_bottom_toolbar_test.ts
+++ b/chrome/test/data/pdf/ink2_text_bottom_toolbar_test.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {AnnotationMode, hexToColor, Ink2Manager, TEXT_COLORS, TextAlignment, UserAction} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {AnnotationMode, hexToColor, Ink2Manager, TEXT_COLORS, TextAlignment, TextTypeface, UserAction} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import type {Color} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
@@ -51,7 +51,7 @@
     // Font and size selects
     const selects = toolbar.shadowRoot.querySelectorAll('select');
     chrome.test.assertEq(2, selects.length);
-    chrome.test.assertEq('Roboto', selects[0]!.value);
+    chrome.test.assertEq(TextTypeface.SANS_SERIF, selects[0]!.value);
     chrome.test.assertEq('12', selects[1]!.value);
 
     // Style selector
@@ -86,7 +86,7 @@
 
     const whenChanged =
         eventToPromise('attributes-changed', Ink2Manager.getInstance());
-    const newValue = 'Serif';
+    const newValue = TextTypeface.SERIF;
     fontSelect.focus();
     fontSelect.value = newValue;
     fontSelect.dispatchEvent(new CustomEvent('change'));
diff --git a/chrome/test/data/pdf/ink2_text_box_test.ts b/chrome/test/data/pdf/ink2_text_box_test.ts
index cc8edb8..f52418e 100644
--- a/chrome/test/data/pdf/ink2_text_box_test.ts
+++ b/chrome/test/data/pdf/ink2_text_box_test.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {hexToColor, Ink2Manager, PluginController, PluginControllerEventType, TEXT_COLORS, TextAlignment, TextBoxState, TextStyle} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {hexToColor, Ink2Manager, PluginController, PluginControllerEventType, TEXT_COLORS, TextAlignment, TextBoxState, TextStyle, TextTypeface} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import type {TextAnnotation} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import {isVisible, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
@@ -11,8 +11,6 @@
 // Set up a dummy viewport so that we can get a predictable initial state.
 const {viewport, mockPlugin} = setupTestViewportAndMockPluginForInk();
 const manager = Ink2Manager.getInstance();
-// Initialize a typeface, since this starts out empty.
-manager.setTextTypeface('Roboto');
 const textbox = document.createElement('ink-text-box');
 document.body.appendChild(textbox);
 
@@ -24,7 +22,7 @@
         text: existing ? 'Hello World' : '',
         textAttributes: {
           size: 12,
-          typeface: 'Roboto',
+          typeface: TextTypeface.SANS_SERIF,
           styles: {
             [TextStyle.BOLD]: false,
             [TextStyle.ITALIC]: false,
@@ -112,7 +110,7 @@
     // Initial state
     chrome.test.assertEq('12px', textboxStyles.getPropertyValue('font-size'));
     chrome.test.assertEq(
-        'Roboto', textboxStyles.getPropertyValue('font-family'));
+        'sans-serif', textboxStyles.getPropertyValue('font-family'));
     chrome.test.assertEq('400', textboxStyles.getPropertyValue('font-weight'));
     chrome.test.assertEq(
         'normal', textboxStyles.getPropertyValue('font-style'));
@@ -126,7 +124,7 @@
     // Confirm updating styles in the manager updates the style of the textbox.
     // Each type of update should independently trigger a change.
     // Typeface
-    manager.setTextTypeface('Serif');
+    manager.setTextTypeface(TextTypeface.SERIF);
     await microtasksFinished();
     chrome.test.assertEq(
         'serif', textboxStyles.getPropertyValue('font-family'));
@@ -159,7 +157,7 @@
     chrome.test.assertEq('right', textboxStyles.getPropertyValue('text-align'));
 
     // Reset everything for later tests.
-    manager.setTextTypeface('Roboto');
+    manager.setTextTypeface(TextTypeface.SANS_SERIF);
     manager.setTextSize(12);
     manager.setTextStyles({
       [TextStyle.BOLD]: false,
@@ -403,7 +401,7 @@
       pageNumber: 0,
       textAttributes: {
         size: 12,
-        typeface: 'Roboto',
+        typeface: TextTypeface.SANS_SERIF,
         styles: {
           [TextStyle.BOLD]: false,
           [TextStyle.ITALIC]: false,
@@ -448,13 +446,13 @@
 
     // Any modifications to font are an edit.
     chrome.test.assertTrue(isVisible(textbox));
-    manager.setTextTypeface('Monospace');
+    manager.setTextTypeface(TextTypeface.MONOSPACE);
     await microtasksFinished();
-    testAnnotation.textAttributes.typeface = 'Monospace';
+    testAnnotation.textAttributes.typeface = TextTypeface.MONOSPACE;
     startNewAnnotationAndVerifyMessage();
     await microtasksFinished();
     // Reset expectation.
-    testAnnotation.textAttributes.typeface = 'Roboto';
+    testAnnotation.textAttributes.typeface = TextTypeface.SANS_SERIF;
 
     // If all the text is deleted, there is also no commit message.
     chrome.test.assertTrue(isVisible(textbox));
diff --git a/chrome/test/data/pdf/ink2_text_side_panel_test.ts b/chrome/test/data/pdf/ink2_text_side_panel_test.ts
index bb5c20a..7ad6040 100644
--- a/chrome/test/data/pdf/ink2_text_side_panel_test.ts
+++ b/chrome/test/data/pdf/ink2_text_side_panel_test.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {AnnotationMode, hexToColor, Ink2Manager, TEXT_COLORS, UserAction} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
+import {AnnotationMode, hexToColor, Ink2Manager, TEXT_COLORS, TextTypeface, UserAction} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import type {Color} from 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/pdf_viewer_wrapper.js';
 import {assert} from 'chrome://resources/js/assert.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
@@ -49,7 +49,7 @@
 
     const whenChanged =
         eventToPromise('attributes-changed', Ink2Manager.getInstance());
-    const newValue = 'Serif';
+    const newValue = TextTypeface.SERIF;
     fontSelect.focus();
     fontSelect.value = newValue;
     fontSelect.dispatchEvent(new CustomEvent('change'));
diff --git a/chrome/test/data/web_apps/simple_focus_existing/manifest.json b/chrome/test/data/web_apps/simple_focus_existing/manifest.json
index 58b9cba8..eeef4ed 100644
--- a/chrome/test/data/web_apps/simple_focus_existing/manifest.json
+++ b/chrome/test/data/web_apps/simple_focus_existing/manifest.json
@@ -15,8 +15,7 @@
   "start_url": "index.html",
   "display": "standalone",
   "launch_handler": {
-      "client_mode": "focus-existing",
-      "client_mode_valid_and_specified": true
+      "client_mode": "focus-existing"
    },
   "scope": "."
 }
diff --git a/chrome/test/data/web_apps/simple_navigate_existing/manifest.json b/chrome/test/data/web_apps/simple_navigate_existing/manifest.json
index 58acd07..440e53ea 100644
--- a/chrome/test/data/web_apps/simple_navigate_existing/manifest.json
+++ b/chrome/test/data/web_apps/simple_navigate_existing/manifest.json
@@ -15,8 +15,7 @@
   "start_url": "index.html",
   "display": "standalone",
   "launch_handler": {
-      "client_mode": "navigate-existing",
-      "client_mode_valid_and_specified": true
+      "client_mode": "navigate-existing"
    },
   "scope": "."
 }
diff --git a/chrome/test/data/webui/bookmarks/command_manager_test.ts b/chrome/test/data/webui/bookmarks/command_manager_test.ts
index 812f7a7..8dcffb1 100644
--- a/chrome/test/data/webui/bookmarks/command_manager_test.ts
+++ b/chrome/test/data/webui/bookmarks/command_manager_test.ts
@@ -4,7 +4,6 @@
 
 import type {BookmarksFolderNodeElement, BookmarksItemElement, BookmarksListElement, SelectFolderAction, SelectItemsAction} from 'chrome://bookmarks/bookmarks.js';
 import {BookmarkManagerApiProxyImpl, BookmarksApiProxyImpl, BookmarksCommandManagerElement, Command, createBookmark, DialogFocusManager, getDisplayedList, MenuSource, selectFolder, setDebouncerForTesting} from 'chrome://bookmarks/bookmarks.js';
-import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {isMac} from 'chrome://resources/js/platform.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {pressAndReleaseKeyOn} from 'chrome://webui-test/keyboard_mock_interactions.js';
@@ -24,10 +23,6 @@
   let bookmarkManagerProxy: TestBookmarkManagerApiProxy;
 
   setup(function() {
-    loadTimeData.overrideValues({
-      splitViewEnabled: true,
-    });
-
     const bulkChildren = [];
     for (let i = 1; i <= 20; i++) {
       const id = '3' + i;
@@ -314,21 +309,6 @@
     assertEquals(20, ids.length);
   });
 
-  test('"Open in Split View" passes correct args', async function() {
-    const items = new Set(['141']);
-    assertTrue(commandManager.canExecute(Command.OPEN_SPLIT_VIEW, items));
-
-    commandManager.handle(Command.OPEN_SPLIT_VIEW, items);
-    await microtasksFinished();
-
-    const [id, {active, split}] =
-        await bookmarkManagerProxy.whenCalled('openInNewTab');
-
-    assertEquals('141', id);
-    assertFalse(active);
-    assertTrue(split);
-  });
-
   test(
       'cannot execute "Open in New Tab" on folders with no items', async () => {
         const items = new Set(['2']);
@@ -358,10 +338,6 @@
         assertTrue(commandItem[Command.OPEN_INCOGNITO].disabled);
         assertFalse(commandItem[Command.OPEN_INCOGNITO].hidden);
 
-        assertTrue(!!commandItem[Command.OPEN_SPLIT_VIEW]);
-        assertTrue(commandItem[Command.OPEN_SPLIT_VIEW].disabled);
-        assertFalse(commandItem[Command.OPEN_SPLIT_VIEW].hidden);
-
         assertTrue(!!commandItem[Command.OPEN_NEW_GROUP]);
         assertTrue(commandItem[Command.OPEN_NEW_GROUP].disabled);
         assertFalse(commandItem[Command.OPEN_NEW_GROUP].hidden);
@@ -569,10 +545,10 @@
   test('double click opens items in foreground tab', async function() {
     simulateDoubleClick(items[1]!);
 
-    const [id, params] = await bookmarkManagerProxy.whenCalled('openInNewTab');
+    const [id, active] = await bookmarkManagerProxy.whenCalled('openInNewTab');
 
     assertEquals('12', id);
-    assertEquals(undefined, params);
+    assertTrue(active);
   });
 
   test('shift-double click opens full selection', function() {
@@ -623,12 +599,10 @@
     // Only the middle-clicked item is opened.
     simulateMiddleClick(item2);
 
-    const [id, {active, split}] =
-        await bookmarkManagerProxy.whenCalled('openInNewTab');
+    const [id, active] = await bookmarkManagerProxy.whenCalled('openInNewTab');
 
     assertEquals('13', id);
     assertFalse(active);
-    assertEquals(undefined, split);
   });
 
   test('middle-click does not open folders', function() {
@@ -646,10 +620,10 @@
     assertTrue(!!item);
 
     simulateMiddleClick(item, {shiftKey: true});
-    const [id, params] = await bookmarkManagerProxy.whenCalled('openInNewTab');
+    const [id, active] = await bookmarkManagerProxy.whenCalled('openInNewTab');
 
     assertEquals('12', id);
-    assertEquals(undefined, params);
+    assertTrue(active);
   });
 
   test(
diff --git a/chrome/test/data/webui/bookmarks/test_bookmark_manager_api_proxy.ts b/chrome/test/data/webui/bookmarks/test_bookmark_manager_api_proxy.ts
index 40f438b..be142f0 100644
--- a/chrome/test/data/webui/bookmarks/test_bookmark_manager_api_proxy.ts
+++ b/chrome/test/data/webui/bookmarks/test_bookmark_manager_api_proxy.ts
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import type {BookmarkManagerApiProxy, OpenInNewTabParams} from 'chrome://bookmarks/bookmarks.js';
+import type {BookmarkManagerApiProxy} from 'chrome://bookmarks/bookmarks.js';
 import {FakeChromeEvent} from 'chrome://webui-test/fake_chrome_event.js';
 import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
 
@@ -55,8 +55,8 @@
     this.methodCalled('openInNewWindow', [idList, incognito]);
   }
 
-  openInNewTab(id: string, params?: OpenInNewTabParams) {
-    this.methodCalled('openInNewTab', [id, params]);
+  openInNewTab(id: string, active: boolean) {
+    this.methodCalled('openInNewTab', [id, active]);
   }
 
   openInNewTabGroup(idList: string[]) {
diff --git a/chrome/test/data/webui/chromeos/settings/os_about_page/test_about_page_browser_proxy.ts b/chrome/test/data/webui/chromeos/settings/os_about_page/test_about_page_browser_proxy.ts
index 5861df2..87c3b2dd 100644
--- a/chrome/test/data/webui/chromeos/settings/os_about_page/test_about_page_browser_proxy.ts
+++ b/chrome/test/data/webui/chromeos/settings/os_about_page/test_about_page_browser_proxy.ts
@@ -26,6 +26,7 @@
     isLts: false,
   };
   private canChangeChannel_ = true;
+  private canChangeFirmware_ = true;
   private regulatoryInfo_: RegulatoryInfo|null = null;
   private tpmFirmwareUpdateStatus_: TpmFirmwareUpdateStatusChangedEvent = {
     updateAvailable: false,
@@ -52,6 +53,7 @@
       'openHelpPage',
       'openFeedbackDialog',
       'canChangeChannel',
+      'canChangeFirmware',
       'getChannelInfo',
       'getVersionInfo',
       'getRegulatoryInfo',
@@ -132,6 +134,10 @@
     this.canChangeChannel_ = canChangeChannel;
   }
 
+  setCanChangeFirmware(canChangeFirmware: boolean): void {
+    this.canChangeFirmware_ = canChangeFirmware;
+  }
+
   setChannels(current: BrowserChannel, target: BrowserChannel): void {
     this.channelInfo_.currentChannel = current;
     this.channelInfo_.targetChannel = target;
@@ -175,6 +181,11 @@
     return Promise.resolve(this.canChangeChannel_);
   }
 
+  canChangeFirmware(): Promise<boolean> {
+    this.methodCalled('canChangeFirmware');
+    return Promise.resolve(this.canChangeFirmware_);
+  }
+
   checkInternetConnection(): Promise<boolean> {
     this.methodCalled('checkInternetConnection');
     return Promise.resolve(this.hasInternetConnection_);
diff --git a/chrome/test/data/webui/new_tab_footer/app_test.ts b/chrome/test/data/webui/new_tab_footer/app_test.ts
index c1daccd..76194bb8 100644
--- a/chrome/test/data/webui/new_tab_footer/app_test.ts
+++ b/chrome/test/data/webui/new_tab_footer/app_test.ts
@@ -6,6 +6,7 @@
 
 import type {NewTabFooterAppElement} from 'chrome://newtab-footer/app.js';
 import {NewTabFooterDocumentProxy} from 'chrome://newtab-footer/browser_proxy.js';
+import type {ManagementNotice, NewTabFooterDocumentRemote} from 'chrome://newtab-footer/new_tab_footer.mojom-webui.js';
 import {NewTabFooterDocumentCallbackRouter, NewTabFooterHandlerRemote} from 'chrome://newtab-footer/new_tab_footer.mojom-webui.js';
 import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {TestMock} from 'chrome://webui-test/test_mock.js';
@@ -14,18 +15,22 @@
 suite('NewTabFooterAppTest', () => {
   let element: NewTabFooterAppElement;
   let handler: TestMock<NewTabFooterHandlerRemote>&NewTabFooterHandlerRemote;
+  let callbackRouter: NewTabFooterDocumentRemote;
 
   setup(() => {
     document.body.innerHTML = window.trustedTypes!.emptyHTML;
     handler = TestMock.fromClass(NewTabFooterHandlerRemote);
     NewTabFooterDocumentProxy.setInstance(
         handler, new NewTabFooterDocumentCallbackRouter());
+    callbackRouter = NewTabFooterDocumentProxy.getInstance()
+                         .callbackRouter.$.bindNewPipeAndPassRemote();
   });
 
   async function initializeElement() {
     element = document.createElement('new-tab-footer-app');
     document.body.appendChild(element);
     await microtasksFinished();
+    await handler.whenCalled('updateManagementNotice');
   }
 
   test('Get extension attibution on initialization', async () => {
@@ -43,4 +48,24 @@
     assertEquals(attributionLink!.href, 'chrome://extensions/?id=1234');
     assertEquals(attributionLink!.innerText, 'foo');
   });
+
+  test('Get management notice on initialization', async () => {
+    // Arrange.
+    await initializeElement();
+    const managementNotice:
+        ManagementNotice = {text: 'Managed by your organization'};
+    callbackRouter.setManagementNotice(managementNotice);
+    await callbackRouter.$.flushForTesting();
+
+    // Assert.
+    // const managementNoticeContainer =
+    //     element.shadowRoot.querySelector('#managementNoticeContainer');
+    // assertTrue(!!managementNoticeContainer);
+    const managementNoticeText =
+        element.shadowRoot.querySelector('#managementNoticeText');
+    assertTrue(!!managementNoticeText);
+
+    assertEquals(
+        managementNoticeText.textContent, 'Managed by your organization');
+  });
 });
diff --git a/chrome/test/data/webui/new_tab_page/app_test.ts b/chrome/test/data/webui/new_tab_page/app_test.ts
index 44f7ccda..3f6386f 100644
--- a/chrome/test/data/webui/new_tab_page/app_test.ts
+++ b/chrome/test/data/webui/new_tab_page/app_test.ts
@@ -744,44 +744,21 @@
     });
 
     test('container is hidden', () => {
-      const modules = $$(app, 'ntp-modules-v2')!;
+      const modules = $$(app, 'ntp-modules')!;
       assertTrue(!!modules);
       assertStyle(modules, 'display', 'none');
     });
 
     test(`clicking records click`, () => {
       // Act.
-      $$<HTMLElement>(app, 'ntp-modules-v2')!.click();
+      $$<HTMLElement>(app, 'ntp-modules')!.click();
 
       // Assert.
       assertEquals(1, metrics.count('NewTabPage.Click'));
       assertEquals(1, metrics.count('NewTabPage.Click', NtpElement.MODULE));
     });
 
-    modulesCommonTests('ntp-modules-v2');
-  });
-
-  suite('v2 modules', () => {
-    suiteSetup(() => {
-      loadTimeData.overrideValues({
-        modulesEnabled: true,
-      });
-    });
-
-    test('container is hidden', () => {
-      const modules = $$(app, 'ntp-modules-v2')!;
-      assertTrue(!!modules);
-      assertStyle(modules, 'display', 'none');
-    });
-
-    test(`clicking records click`, () => {
-      // Act.
-      $$<HTMLElement>(app, 'ntp-modules-v2')!.click();
-
-      // Assert.
-      assertEquals(1, metrics.count('NewTabPage.Click'));
-      assertEquals(1, metrics.count('NewTabPage.Click', NtpElement.MODULE));
-    });
+    modulesCommonTests('ntp-modules');
   });
 
   suite('CounterfactualModules', () => {
diff --git a/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.ts b/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.ts
index f40a4ef..87416ae 100644
--- a/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/module_wrapper_test.ts
@@ -3,10 +3,11 @@
 // found in the LICENSE file.
 
 import {ModuleDescriptor, ModuleWrapperElement} from 'chrome://new-tab-page/lazy_load.js';
+import type {ModuleInstance} from 'chrome://new-tab-page/lazy_load.js';
 import {NewTabPageProxy, WindowProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
-import {assertDeepEquals, assertEquals, assertThrows} from 'chrome://webui-test/chai_assert.js';
+import {assertDeepEquals, assertEquals} from 'chrome://webui-test/chai_assert.js';
 import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
 import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
 import type {TestMock} from 'chrome://webui-test/test_mock.js';
@@ -16,7 +17,6 @@
 
 suite('NewTabPageModulesModuleWrapperTest', () => {
   let handler: TestMock<PageHandlerRemote>;
-  let moduleWrapper: ModuleWrapperElement;
   let metrics: MetricsTracker;
   let windowProxy: TestMock<WindowProxy>;
 
@@ -31,56 +31,51 @@
             NewTabPageProxy.setInstance(mock, new PageCallbackRouter()));
     metrics = fakeMetricsPrivate();
     windowProxy = installMock(WindowProxy);
-    moduleWrapper = new ModuleWrapperElement();
-    document.body.appendChild(moduleWrapper);
   });
 
+  function createModuleWrapper(module: ModuleInstance): ModuleWrapperElement {
+    const moduleWrapper: ModuleWrapperElement = new ModuleWrapperElement();
+    moduleWrapper.module = module;
+    return moduleWrapper;
+  }
+
   test('renders module descriptor', async () => {
     // Arrange.
     const moduleElement = createElement();
     moduleElement.style.height = '100px';
+    const moduleWrapper = createModuleWrapper({
+      descriptor: new ModuleDescriptor('foo', initNullModule),
+      element: moduleElement,
+      initialized: false,
+      impressed: false,
+    });
     const detectedImpression =
         eventToPromise('detect-impression', moduleWrapper);
+    document.body.appendChild(moduleWrapper);
     windowProxy.setResultFor('now', 123);
 
     // Act.
-    moduleWrapper.module = {
-      descriptor: new ModuleDescriptor('foo', initNullModule),
-      element: moduleElement,
-    };
     await detectedImpression;
 
     // Assert.
     assertEquals(100, moduleWrapper.$.moduleElement.offsetHeight);
-    assertDeepEquals(moduleElement, moduleWrapper.$.moduleElement.children[0]);
+    assertDeepEquals(moduleElement, moduleWrapper.querySelector('div'));
     assertEquals(1, metrics.count('NewTabPage.Modules.Impression'));
     assertEquals(1, metrics.count('NewTabPage.Modules.Impression.foo'));
     assertEquals(1, metrics.count('NewTabPage.Modules.Impression', 123));
     assertEquals(1, metrics.count('NewTabPage.Modules.Impression.foo', 123));
   });
 
-
-  test('descriptor can only be set once', () => {
-    const moduleElement = createElement();
-    moduleWrapper.module = {
-      descriptor: new ModuleDescriptor('foo', initNullModule),
-      element: moduleElement,
-    };
-    assertThrows(() => {
-      moduleWrapper.module = {
-        descriptor: new ModuleDescriptor('foo', initNullModule),
-        element: moduleElement,
-      };
-    });
-  });
-
   test('receiving usage events records usage', () => {
     // Arrange.
     const moduleElement = createElement();
-    moduleWrapper.module = {
+    const moduleWrapper = createModuleWrapper({
       descriptor: new ModuleDescriptor('foo', initNullModule),
       element: moduleElement,
-    };
+      initialized: false,
+      impressed: false,
+    });
+    document.body.appendChild(moduleWrapper);
 
     // Act.
     moduleElement.dispatchEvent(new Event('usage', {bubbles: true}));
@@ -90,38 +85,38 @@
     assertEquals(1, metrics.count('NewTabPage.Modules.Usage.foo'));
   });
 
-  suite('Redesigned modules', () => {
-    suiteSetup(() => {
-      loadTimeData.overrideValues({modulesRedesignedEnabled: true});
-    });
-
-    ['usage', 'menu-button-click'].forEach((eventName: string) => {
-      test(
-          `module ${eventName} event triggers onModuleUsed
+  ['usage', 'menu-button-click'].forEach((eventName: string) => {
+    test(
+        `module ${eventName} event triggers onModuleUsed
               function`,
-          () => {
-            const moduleId = 'foo';
-            const moduleElement = createElement();
-            moduleWrapper.module = {
-              descriptor: new ModuleDescriptor(moduleId, initNullModule),
-              element: moduleElement,
-            };
-
-            moduleElement.dispatchEvent(new Event(eventName, {bubbles: true}));
-
-            assertEquals(1, handler.getCallCount('onModuleUsed'));
-            assertEquals(moduleId, handler.getArgs('onModuleUsed')[0]);
+        () => {
+          const moduleId = 'foo';
+          const moduleElement = createElement();
+          const moduleWrapper = createModuleWrapper({
+            descriptor: new ModuleDescriptor(moduleId, initNullModule),
+            element: moduleElement,
+            initialized: false,
+            impressed: false,
           });
-    });
+          document.body.appendChild(moduleWrapper);
+
+          moduleElement.dispatchEvent(new Event(eventName, {bubbles: true}));
+
+          assertEquals(1, handler.getCallCount('onModuleUsed'));
+          assertEquals(moduleId, handler.getArgs('onModuleUsed')[0]);
+        });
   });
 
   test('clicking info button records click and module id', () => {
     // Arrange.
     const moduleElement = createElement();
-    moduleWrapper.module = {
+    const moduleWrapper = createModuleWrapper({
       descriptor: new ModuleDescriptor('foo', initNullModule),
       element: moduleElement,
-    };
+      initialized: false,
+      impressed: false,
+    });
+    document.body.appendChild(moduleWrapper);
 
     // Act.
     moduleElement.dispatchEvent(new Event('info-button-click'));
diff --git a/chrome/test/data/webui/new_tab_page/modules/v2/modules_test.ts b/chrome/test/data/webui/new_tab_page/modules/v2/modules_test.ts
index 2ca228d..0b545821 100644
--- a/chrome/test/data/webui/new_tab_page/modules/v2/modules_test.ts
+++ b/chrome/test/data/webui/new_tab_page/modules/v2/modules_test.ts
@@ -3,16 +3,17 @@
 // found in the LICENSE file.
 
 import type {Module, ModuleWrapperElement, NamedWidth} from 'chrome://new-tab-page/lazy_load.js';
-import {ModuleDescriptor, ModuleRegistry, ModulesV2Element, SUPPORTED_MODULE_WIDTHS} from 'chrome://new-tab-page/lazy_load.js';
+import {ModuleDescriptor, ModuleRegistry, ModulesElement, SUPPORTED_MODULE_WIDTHS} from 'chrome://new-tab-page/lazy_load.js';
 import {NewTabPageProxy} from 'chrome://new-tab-page/new_tab_page.js';
 import type {PageRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
 import {PageCallbackRouter, PageHandlerRemote} from 'chrome://new-tab-page/new_tab_page.mojom-webui.js';
+import {assert} from 'chrome://resources/js/assert.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
 import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js';
 import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js';
-import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 import type {TestMock} from 'chrome://webui-test/test_mock.js';
+import {microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import {assertNotStyle, assertStyle, createElement, initNullModule, installMock} from '../../test_support.js';
 
@@ -45,7 +46,7 @@
 
   async function createModulesElement(
       modules: Module[], enabled: boolean, width: number,
-      disabledModuleIds: string[] = []): Promise<ModulesV2Element> {
+      disabledModuleIds: string[] = []): Promise<ModulesElement> {
     if (!enabled) {
       assertTrue(
           modules.length === 0,
@@ -59,10 +60,11 @@
     });
 
     moduleRegistry.setResultFor('initializeModulesHavingIds', modulesPromise);
-    const element = new ModulesV2Element();
+    const element = new ModulesElement();
     document.body.style.width = `${width}px`;
     document.body.appendChild(element);
     await modulesPromise;
+    await microtasksFinished();
     return element;
   }
 
@@ -196,9 +198,8 @@
             },
           ],
           true, scenario.width);
-      await waitAfterNextRender(modulesElement);
 
-      const wrappers = modulesElement.shadowRoot!.querySelectorAll(
+      const wrappers = modulesElement.shadowRoot.querySelectorAll(
           'ntp-module-wrapper:not([hidden])');
       assertEquals(scenario.count, wrappers.length);
 
@@ -206,7 +207,7 @@
       scenario.rows.forEach((expectedRowWidths, i) => {
         expectedRowWidths.forEach((expectedWidth, j) => {
           const wrapper = wrappers[index]! as ModuleWrapperElement;
-          const instance = wrapper.$.moduleElement.lastChild! as HTMLElement;
+          const instance = wrapper.lastChild! as HTMLElement;
           assertEquals(expectedWidth.name, instance.getAttribute('format'));
           assertEquals(
               expectedWidth.value, wrapper.clientWidth,
@@ -226,12 +227,11 @@
         {id: barDescriptor.id, name: barDescriptor.id},
       ],
     });
+
     const modulesElement =
         await createModulesElement([], false, SAMPLE_SCREEN_WIDTH);
-    await waitAfterNextRender(modulesElement);
-
     const moduleWrappers =
-        modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
+        modulesElement.shadowRoot.querySelectorAll('ntp-module-wrapper');
     assertEquals(0, moduleWrappers.length);
     assertEquals(1, metrics.count('NewTabPage.Modules.LoadedModulesCount', 0));
     assertEquals(1, metrics.count('NewTabPage.Modules.InstanceCount', 0));
@@ -264,7 +264,7 @@
             true, SAMPLE_SCREEN_WIDTH);
 
         const moduleWrappers =
-            modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
+            modulesElement.shadowRoot.querySelectorAll('ntp-module-wrapper');
         assertEquals(4, moduleWrappers.length);
         assertEquals(1, metrics.count('NewTabPage.Modules.LoadedModulesCount'));
         assertEquals(1, metrics.count('NewTabPage.Modules.InstanceCount', 4));
@@ -282,53 +282,6 @@
             0, metrics.count('NewTabPage.Modules.LoadedWith.bar', 'bar'));
       });
 
-  test('modules maxium instance count works correctly', async () => {
-    const SAMPLE_MAX_MODULE_INSTANCE_COUNT = 2;
-    loadTimeData.overrideValues({
-      modulesMaxColumnCount: MAX_COLUMN_COUNT,
-      multipleLoadedModulesMaxModuleInstanceCount:
-          SAMPLE_MAX_MODULE_INSTANCE_COUNT,
-    });
-
-    const fooDescriptor = new ModuleDescriptor('foo', initNullModule);
-    const barDescriptor = new ModuleDescriptor('bar', initNullModule);
-    const descriptors = [
-      fooDescriptor,
-      barDescriptor,
-    ];
-    const modulesElement = await createModulesElementFromDescriptors(
-        descriptors, SAMPLE_MAX_MODULE_INSTANCE_COUNT + 1);
-    const moduleWrappers =
-        modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
-    assertEquals(
-        descriptors.length * SAMPLE_MAX_MODULE_INSTANCE_COUNT,
-        moduleWrappers.length);
-  });
-
-  test('modules maxium instance capped to maximum column count', async () => {
-    const SAMPLE_MAX_COLUMN_COUNT = 3;
-    const SAMPLE_MAX_MODULE_INSTANCE_COUNT = 3;
-    loadTimeData.overrideValues({
-      modulesMaxColumnCount: SAMPLE_MAX_COLUMN_COUNT,
-      multipleLoadedModulesMaxModuleInstanceCount:
-          SAMPLE_MAX_MODULE_INSTANCE_COUNT,
-    });
-
-    const fooDescriptor = new ModuleDescriptor('foo', initNullModule);
-    const barDescriptor = new ModuleDescriptor('bar', initNullModule);
-    const bazDescriptor = new ModuleDescriptor('baz', initNullModule);
-    const descriptors = [
-      fooDescriptor,
-      barDescriptor,
-      bazDescriptor,
-    ];
-    const modulesElement = await createModulesElementFromDescriptors(
-        descriptors, SAMPLE_MAX_MODULE_INSTANCE_COUNT);
-    const moduleWrappers =
-        modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
-    assertEquals(SAMPLE_MAX_COLUMN_COUNT, moduleWrappers.length);
-  });
-
   enum UndoStrategy {
     BUTTON_ACTIVATION = 'button activation',
     SHORTCUT_KEY = 'shortcut key',
@@ -356,26 +309,26 @@
                   true, SAMPLE_SCREEN_WIDTH);
 
               // Assert.
-              const moduleWrappers =
-                  modulesElement.shadowRoot!.querySelectorAll(
-                      'ntp-module-wrapper');
+              const moduleWrappers = modulesElement.shadowRoot.querySelectorAll(
+                  'ntp-module-wrapper');
               assertEquals(1, moduleWrappers.length);
               assertNotStyle(moduleWrappers[0]!, 'display', 'none');
               assertFalse(modulesElement.$.undoToast.open);
 
               // Act.
               let restoreCalled = false;
-              moduleWrappers[0]!.dispatchEvent(
-                  new CustomEvent('disable-module', {
-                    bubbles: true,
-                    composed: true,
-                    detail: {
-                      message: 'Foo',
-                      restoreCallback: () => {
-                        restoreCalled = true;
-                      },
-                    },
-                  }));
+              const moduleElement =
+                  moduleWrappers[0]!.lastChild! as HTMLElement;
+              moduleElement.dispatchEvent(new CustomEvent('disable-module', {
+                bubbles: true,
+                composed: true,
+                detail: {
+                  message: 'Foo',
+                  restoreCallback: () => {
+                    restoreCalled = true;
+                  },
+                },
+              }));
 
               // Assert.
               assertDeepEquals(
@@ -399,10 +352,10 @@
               assertFalse(restoreCalled);
 
               // Act.
-              await waitAfterNextRender(modulesElement);
+              await microtasksFinished();
               if (undoStrategy === UndoStrategy.BUTTON_ACTIVATION) {
                 const undoButton =
-                    modulesElement.shadowRoot!.querySelector<HTMLElement>(
+                    modulesElement.shadowRoot.querySelector<HTMLElement>(
                         '#undoButton');
                 assertTrue(!!undoButton);
                 undoButton.click();
@@ -450,7 +403,8 @@
                   }],
                   true, SAMPLE_SCREEN_WIDTH);
 
-              let moduleWrappers = modulesElement.shadowRoot!.querySelectorAll(
+              assert(modulesElement.shadowRoot);
+              let moduleWrappers = modulesElement.shadowRoot.querySelectorAll(
                   'ntp-module-wrapper');
               assertEquals(1, moduleWrappers.length);
               assertFalse(modulesElement.$.undoToast.open);
@@ -467,10 +421,11 @@
                       },
                     },
                   }));
+              await microtasksFinished();
 
               assertEquals(
                   0,
-                  modulesElement.shadowRoot!
+                  modulesElement.shadowRoot
                       .querySelectorAll('ntp-module-wrapper')
                       .length);
               assertTrue(modulesElement.$.undoToast.open);
@@ -478,10 +433,11 @@
               assertEquals(1, handler.getCallCount('onDismissModule'));
               assertEquals(moduleId, handler.getArgs('onDismissModule')[0]);
 
-              await waitAfterNextRender(modulesElement);
+              await microtasksFinished();
               if (undoStrategy === UndoStrategy.BUTTON_ACTIVATION) {
+                assert(modulesElement.shadowRoot);
                 const undoButton =
-                    modulesElement.shadowRoot!.querySelector<HTMLElement>(
+                    modulesElement.shadowRoot.querySelector<HTMLElement>(
                         '#undoButton');
                 assertTrue(!!undoButton);
                 undoButton.click();
@@ -492,7 +448,8 @@
                 }));
               }
 
-              moduleWrappers = modulesElement.shadowRoot!.querySelectorAll(
+              await microtasksFinished();
+              moduleWrappers = modulesElement.shadowRoot.querySelectorAll(
                   'ntp-module-wrapper');
               assertEquals(1, moduleWrappers.length);
               assertFalse(modulesElement.$.undoToast.open);
@@ -512,13 +469,13 @@
         {id: fooDescriptor.id, name: fooDescriptor.id},
       ],
     });
-    const modulesElement = await createModulesElement(
+    await createModulesElement(
         [{
           descriptor: fooDescriptor,
           elements: [createElement()],
         }],
         true, SAMPLE_SCREEN_WIDTH);
-    await waitAfterNextRender(modulesElement);
+    await microtasksFinished();
 
     // Act.
     window.dispatchEvent(new KeyboardEvent('keydown', {
@@ -537,7 +494,7 @@
     scenario.rows.forEach((expectedRowWidths, i) => {
       expectedRowWidths.forEach((expectedWidth, j) => {
         const wrapper = moduleWrappers[index]!;
-        const instance = wrapper.$.moduleElement.lastChild! as HTMLElement;
+        const instance = wrapper.lastChild! as HTMLElement;
         assertEquals(expectedWidth.name, instance.getAttribute('format'));
         assertEquals(
             expectedWidth.value, wrapper.clientWidth,
@@ -612,30 +569,31 @@
           });
           const modulesElement =
               await createModulesElement(modules, true, SAMPLE_SCREEN_WIDTH);
-          await waitAfterNextRender(modulesElement);
+          await microtasksFinished();
 
           const moduleWrappers =
               Array.from(
-                  modulesElement.shadowRoot!.querySelectorAll<HTMLElement>(
+                  modulesElement.shadowRoot.querySelectorAll<HTMLElement>(
                       'ntp-module-wrapper')) as ModuleWrapperElement[];
           assertContainerLayout(moduleWrappers, layoutChangeScenario.before);
 
-          moduleWrappers[0]!.dispatchEvent(new CustomEvent('disable-module', {
-            bubbles: true,
-            composed: true,
-            detail: {
-              message: 'Foo',
-            },
-          }));
+          moduleWrappers[0]!.lastChild!.dispatchEvent(
+              new CustomEvent('disable-module', {
+                bubbles: true,
+                composed: true,
+                detail: {
+                  message: 'Foo',
+                },
+              }));
           assertDeepEquals(
               ['foo', true], handler.getArgs('setModuleDisabled')[0]);
           callbackRouterRemote.setDisabledModules(false, ['foo']);
           await callbackRouterRemote.$.flushForTesting();
-          await waitAfterNextRender(modulesElement);
+          await microtasksFinished();
 
           assertContainerLayout(
               Array.from(
-                  modulesElement.shadowRoot!.querySelectorAll<HTMLElement>(
+                  modulesElement.shadowRoot.querySelectorAll<HTMLElement>(
                       'ntp-module-wrapper:not([hidden])')) as
                   ModuleWrapperElement[],
               layoutChangeScenario.after);
@@ -704,11 +662,12 @@
           });
           const modulesElement =
               await createModulesElement(modules, true, SAMPLE_SCREEN_WIDTH);
-          await waitAfterNextRender(modulesElement);
+          await microtasksFinished();
 
+          assert(modulesElement.shadowRoot);
           const moduleWrappers =
               Array.from(
-                  modulesElement.shadowRoot!.querySelectorAll<HTMLElement>(
+                  modulesElement.shadowRoot.querySelectorAll<HTMLElement>(
                       'ntp-module-wrapper')) as ModuleWrapperElement[];
           assertContainerLayout(moduleWrappers, layoutChangeScenario.before);
 
@@ -725,11 +684,11 @@
                 },
               }));
           assertFalse(restoreCalled);
-          await waitAfterNextRender(modulesElement);
+          await microtasksFinished();
 
           assertContainerLayout(
               Array.from(
-                  modulesElement.shadowRoot!.querySelectorAll<HTMLElement>(
+                  modulesElement.shadowRoot.querySelectorAll<HTMLElement>(
                       'ntp-module-wrapper')) as ModuleWrapperElement[],
               layoutChangeScenario.after);
         });
@@ -773,22 +732,24 @@
         const modulesElement = await createModulesElement(
             [], true, SAMPLE_SCREEN_WIDTH,
             /*disabledModuleIds=*/[fooDescriptor.id]);
-        await waitAfterNextRender(modulesElement);
+        await microtasksFinished();
         let moduleWrappers =
-            modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
+            modulesElement.shadowRoot.querySelectorAll('ntp-module-wrapper');
         assertEquals(0, moduleWrappers.length);
+
         // Mock required data for loading foo module.
         const fooModulePromise = getModulePromise(fooDescriptor);
         moduleRegistry.setResultFor('initializeModuleById', fooModulePromise);
 
-        // Act - Remove foo module from disabled modules list.
+        // Act - Remove foo module from disabled modules list and trigger a
+        // reload operation.
         callbackRouterRemote.setDisabledModules(false, []);
+        callbackRouterRemote.setModulesLoadable();
         await fooModulePromise;
-        await waitAfterNextRender(modulesElement);
+        await microtasksFinished();
 
-        // Assert - Foo module loaded.
         moduleWrappers =
-            modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
+            modulesElement.shadowRoot.querySelectorAll('ntp-module-wrapper');
         assertEquals(1, moduleWrappers.length);
         assertEquals(
             fooDescriptor.id,
@@ -799,46 +760,43 @@
             1, metrics.count('NewTabPage.Modules.ReloadedModulesCount', 1));
       });
 
-      test('enabling module while container is being loaded', async () => {
-        // Arrange.
-        loadTimeData.overrideValues({
-          // Prevent initial module loading to ensure module enabling during
-          // container load is tested.
-          waitToLoadModules: true,
-        });
-        const fooDescriptor = new ModuleDescriptor('foo', initNullModule);
-        const barDescriptor = new ModuleDescriptor('bar', initNullModule);
-        const bazDescriptor = new ModuleDescriptor('baz', initNullModule);
-        // Initial state: foo and bar enabled, baz disabled.
-        const enabledDescriptors = [fooDescriptor, barDescriptor];
-        const modulesElement = await createModulesElementFromDescriptors(
-            enabledDescriptors, /*instanceCount=*/ 1, [bazDescriptor]);
-        const bazReloadPromise = getModulePromise(bazDescriptor);
-        moduleRegistry.setResultFor('initializeModuleById', bazReloadPromise);
+      test(
+          'enabling module after container has loaded with some modules',
+          async () => {
+            // Arrange.
+            loadTimeData.overrideValues({
+              modulesReloadable: true,
+              // Prevent initial module loading to ensure module enabling during
+              // container load is tested.
+              waitToLoadModules: true,
+            });
+            const fooDescriptor = new ModuleDescriptor('foo', initNullModule);
+            const barDescriptor = new ModuleDescriptor('bar', initNullModule);
+            const bazDescriptor = new ModuleDescriptor('baz', initNullModule);
+            // Initial state: foo and bar enabled, baz disabled.
+            const enabledDescriptors = [fooDescriptor, barDescriptor];
+            const modulesElement = await createModulesElementFromDescriptors(
+                enabledDescriptors, /*instanceCount=*/ 1, [bazDescriptor]);
+            callbackRouterRemote.setModulesLoadable();
+            await microtasksFinished();
 
-        // Act - Start module load, then enable baz by clearing the disabled
-        // modules list.
-        callbackRouterRemote.setModulesLoadable();
-        callbackRouterRemote.setDisabledModules(false, []);
-        callbackRouterRemote.$.flushForTesting();
+            const bazReloadPromise = getModulePromise(bazDescriptor);
+            moduleRegistry.setResultFor(
+                'initializeModuleById', bazReloadPromise);
 
-        await bazReloadPromise;
-        await waitAfterNextRender(modulesElement);
+            // Act - Enable baz by clearing the disabled modules list and
+            // trigger a reload operation.
+            callbackRouterRemote.setDisabledModules(false, []);
+            callbackRouterRemote.setModulesLoadable();
+            await bazReloadPromise;
+            await microtasksFinished();
 
-        // Assert.
-        const moduleWrappers =
-            modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
-        assertEquals(3, moduleWrappers.length);
-        assertEquals(
-            fooDescriptor.id,
-            (moduleWrappers[0] as ModuleWrapperElement).module.descriptor.id);
-        assertEquals(
-            barDescriptor.id,
-            (moduleWrappers[1] as ModuleWrapperElement).module.descriptor.id);
-        assertEquals(
-            bazDescriptor.id,
-            (moduleWrappers[2] as ModuleWrapperElement).module.descriptor.id);
-      });
+            // Assert.
+            assert(modulesElement.shadowRoot);
+            const moduleWrappers = modulesElement.shadowRoot.querySelectorAll(
+                'ntp-module-wrapper');
+            assertEquals(3, moduleWrappers.length);
+          });
 
       test('reloads module container after initial load', async () => {
         // Arrange.
@@ -851,8 +809,9 @@
             enabledDescriptors, /*instanceCount=*/ 1,
             [barDescriptor, bazDescriptor]);
         // Ensure only foo module loaded.
+        assert(modulesElement.shadowRoot);
         let moduleWrappers =
-            modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
+            modulesElement.shadowRoot.querySelectorAll('ntp-module-wrapper');
         assertEquals(1, moduleWrappers.length);
         assertEquals(
             fooDescriptor.id,
@@ -872,14 +831,15 @@
         }));
 
         // Act - Enable the bar module by removing it from the disabled modules
-        // list.
+        // list and trigger a reload operation.
         callbackRouterRemote.setDisabledModules(false, [bazDescriptor.id]);
+        callbackRouterRemote.setModulesLoadable();
         await barModulePromise;
-        await waitAfterNextRender(modulesElement);
+        await microtasksFinished();
 
         // Assert.
         moduleWrappers =
-            modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
+            modulesElement.shadowRoot.querySelectorAll('ntp-module-wrapper');
         assertEquals(2, moduleWrappers.length);
         // Ensure the 'foo' module loads last, as it was not included in the
         // module order returned by |getModulesOrder()|.
@@ -910,12 +870,15 @@
             const barDescriptor = new ModuleDescriptor('bar', initNullModule);
             // Initial state: foo enabled, bar disabled.
             const modulesElement = await createModulesElementFromDescriptors(
-                /*enabledDescriptors=*/[fooDescriptor], /*instanceCount=*/ 1,
+                /*enabledDescriptors=*/[fooDescriptor],
+                /*instanceCount=*/ 1,
                 /*disabledDescriptors=*/[barDescriptor]);
             // Ensure foo module shows.
-            let moduleWrappers = modulesElement.shadowRoot!.querySelectorAll(
+            assert(modulesElement.shadowRoot);
+            let moduleWrappers = modulesElement.shadowRoot.querySelectorAll(
                 'ntp-module-wrapper');
             assertEquals(1, moduleWrappers.length);
+
             let fooModule = moduleWrappers[0] as ModuleWrapperElement;
             assertEquals(fooDescriptor.id, fooModule.module.descriptor.id);
             assertNotStyle(fooModule, 'display', 'none');
@@ -935,7 +898,7 @@
             await callbackRouterRemote.$.flushForTesting();
 
             // Assert - Foo module shows and bar module never loaded.
-            moduleWrappers = modulesElement.shadowRoot!.querySelectorAll(
+            moduleWrappers = modulesElement.shadowRoot.querySelectorAll(
                 'ntp-module-wrapper');
             assertEquals(1, moduleWrappers.length);
             fooModule = moduleWrappers[0] as ModuleWrapperElement;
@@ -951,8 +914,9 @@
         const modulesElement = await createModulesElementFromDescriptors(
             [fooDescriptor], /*instanceCount=*/ 1, [barDescriptor]);
         // Ensure only foo module loaded.
+        assert(modulesElement.shadowRoot);
         let moduleWrappers =
-            modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
+            modulesElement.shadowRoot.querySelectorAll('ntp-module-wrapper');
         assertEquals(1, moduleWrappers.length);
         assertEquals(
             fooDescriptor.id,
@@ -962,18 +926,20 @@
         moduleRegistry.setResultFor('initializeModuleById', barModulePromise);
         // Set the module order, to be verified later.
         handler.setResultFor(
-            'getModulesOrder', Promise.resolve({moduleIds: []}));
+            'getModulesOrder',
+            Promise.resolve({moduleIds: [fooDescriptor.id, barDescriptor.id]}));
 
         // Act - Clear the disabled modules list multiple times.
         callbackRouterRemote.setDisabledModules(false, []);
         callbackRouterRemote.setDisabledModules(false, []);
         callbackRouterRemote.setDisabledModules(false, []);
+        callbackRouterRemote.setModulesLoadable();
         await barModulePromise;
-        await flushTasks();
+        await microtasksFinished();
 
         // Assert - Ensure only one instance of the bar module populated.
         moduleWrappers =
-            modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
+            modulesElement.shadowRoot.querySelectorAll('ntp-module-wrapper');
         assertEquals(2, moduleWrappers.length);
         assertEquals(
             fooDescriptor.id,
@@ -1001,7 +967,7 @@
                 }],
                 true, SAMPLE_SCREEN_WIDTH,
                 /*disabledModuleIds=*/[barDescriptor.id]);
-            await waitAfterNextRender(modulesElement);
+            await microtasksFinished();
             const barReloadPromise = getModulePromise(barDescriptor);
             moduleRegistry.setResultFor(
                 'initializeModuleById', barReloadPromise);
@@ -1010,10 +976,10 @@
             // modules list.
             callbackRouterRemote.setDisabledModules(false, []);
             await barReloadPromise;
-            await waitAfterNextRender(modulesElement);
+            await microtasksFinished();
 
             // Assert - Foo module shows and bar module never loaded.
-            const moduleWrappers = modulesElement.shadowRoot!.querySelectorAll(
+            const moduleWrappers = modulesElement.shadowRoot.querySelectorAll(
                 'ntp-module-wrapper');
             assertEquals(1, moduleWrappers.length);
             const fooModule = moduleWrappers[0] as ModuleWrapperElement;
@@ -1033,11 +999,12 @@
           // Act.
           const modulesElement = await createModulesElementFromDescriptors(
               /*enabledDescriptors=*/[fooDescriptor]);
-          await waitAfterNextRender(modulesElement);
+          await microtasksFinished();
 
           // Assert.
+          assert(modulesElement.shadowRoot);
           const moduleWrappers =
-              modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
+              modulesElement.shadowRoot.querySelectorAll('ntp-module-wrapper');
           if (waitToLoadModules) {
             assertEquals(0, moduleWrappers.length);
           } else {
@@ -1057,19 +1024,20 @@
         const fooDescriptor = new ModuleDescriptor('foo', initNullModule);
         const modulesElement = await createModulesElementFromDescriptors(
             /*enabledDescriptors=*/[fooDescriptor]);
-        await waitAfterNextRender(modulesElement);
+        await microtasksFinished();
+        assert(modulesElement.shadowRoot);
         let moduleWrappers =
-            modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
+            modulesElement.shadowRoot.querySelectorAll('ntp-module-wrapper');
         assertEquals(0, moduleWrappers.length);
 
         // Act.
         callbackRouterRemote.setModulesLoadable();
         await callbackRouterRemote.$.flushForTesting();
-        await waitAfterNextRender(modulesElement);
+        await microtasksFinished();
 
         // Assert.
         moduleWrappers =
-            modulesElement.shadowRoot!.querySelectorAll('ntp-module-wrapper');
+            modulesElement.shadowRoot.querySelectorAll('ntp-module-wrapper');
         assertEquals(1, moduleWrappers.length);
         const fooModule = moduleWrappers[0] as ModuleWrapperElement;
         assertEquals(fooDescriptor.id, fooModule.module.descriptor.id);
diff --git a/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.ts b/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.ts
index 05f869c..d325475 100644
--- a/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.ts
+++ b/chrome/test/data/webui/print_preview/destination_dialog_interactive_test.ts
@@ -6,7 +6,6 @@
 import {NativeLayerImpl, State} from 'chrome://print/print_preview.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 import {keyDownOn} from 'chrome://webui-test/keyboard_mock_interactions.js';
-import {fakeDataBind} from 'chrome://webui-test/polymer_test_util.js';
 import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {NativeLayerStub} from './native_layer_stub.js';
@@ -34,10 +33,8 @@
     // Create destination settings, so  that the user manager is created.
     const destinationSettings =
         document.createElement('print-preview-destination-settings');
-    destinationSettings.settings = model.settings;
     destinationSettings.state = State.READY;
     destinationSettings.disabled = false;
-    fakeDataBind(model, destinationSettings, 'settings');
     document.body.appendChild(destinationSettings);
 
     // Initialize
diff --git a/chrome/test/data/webui/print_preview/destination_select_test.ts b/chrome/test/data/webui/print_preview/destination_select_test.ts
index ba507117..c441e2e 100644
--- a/chrome/test/data/webui/print_preview/destination_select_test.ts
+++ b/chrome/test/data/webui/print_preview/destination_select_test.ts
@@ -4,8 +4,8 @@
 
 import type {PrintPreviewDestinationSelectElement} from 'chrome://print/print_preview.js';
 import {Destination, DestinationOrigin, getSelectDropdownBackground, IconsetMap} from 'chrome://print/print_preview.js';
-import {assertEquals} from 'chrome://webui-test/chai_assert.js';
-import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
+import {assertEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks, waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js';
 
 import {selectOption} from './print_preview_test_utils.js';
 
@@ -71,4 +71,28 @@
       compareIcon(selectEl, enterpriseIcon);
     });
   });
+
+  test('ShowsSelectedDestination', async function() {
+    const select = destinationSelect.shadowRoot!.querySelector('select');
+    assertTrue(!!select);
+
+    const destination = recentDestinationList[0]!;
+    destinationSelect.loaded = true;
+    destinationSelect.destination = destination;
+    destinationSelect.updateDestination();
+    await flushTasks();
+
+    assertEquals(destination.key, select.value);
+    assertEquals(destination.key, destinationSelect.selectedValue);
+
+    const newDestination =
+        new Destination('ID2', DestinationOrigin.LOCAL, 'Two');
+    destinationSelect.recentDestinationList = [newDestination];
+    destinationSelect.destination = newDestination;
+    destinationSelect.updateDestination();
+    await flushTasks();
+
+    assertEquals(newDestination.key, select.value);
+    assertEquals(newDestination.key, destinationSelect.selectedValue);
+  });
 });
diff --git a/chrome/test/data/webui/print_preview/destination_settings_test.ts b/chrome/test/data/webui/print_preview/destination_settings_test.ts
index 79acd9c..329cd37 100644
--- a/chrome/test/data/webui/print_preview/destination_settings_test.ts
+++ b/chrome/test/data/webui/print_preview/destination_settings_test.ts
@@ -2,30 +2,24 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'chrome://print/print_preview.js';
+
 import type {LocalDestinationInfo, PrintPreviewDestinationSettingsElement, RecentDestination} from 'chrome://print/print_preview.js';
 import {Destination, DestinationErrorType, DestinationOrigin, DestinationState, DestinationStoreEventType, Error, GooglePromotedDestinationId, makeRecentDestination, NativeLayerImpl, NUM_PERSISTED_DESTINATIONS, State} from 'chrome://print/print_preview.js';
 import {assert} from 'chrome://resources/js/assert.js';
-import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
-import {fakeDataBind, waitBeforeNextRender} from 'chrome://webui-test/polymer_test_util.js';
-import {eventToPromise} from 'chrome://webui-test/test_util.js';
+import {eventToPromise, microtasksFinished} from 'chrome://webui-test/test_util.js';
 
 import {NativeLayerStub} from './native_layer_stub.js';
 import {getDestinations, getSaveAsPdfDestination, setupTestListenerElement} from './print_preview_test_utils.js';
 
 suite('DestinationSettingsTest', function() {
   let destinationSettings: PrintPreviewDestinationSettingsElement;
-
   let nativeLayer: NativeLayerStub;
-
   let recentDestinations: RecentDestination[] = [];
-
   let localDestinations: LocalDestinationInfo[] = [];
-
   let destinations: Destination[] = [];
-
   const extraDestinations: Destination[] = [];
-
   let pdfPrinterDisabled: boolean = false;
 
   suiteSetup(function() {
@@ -54,10 +48,8 @@
 
     destinationSettings =
         document.createElement('print-preview-destination-settings');
-    destinationSettings.settings = model.settings;
     destinationSettings.state = State.NOT_READY;
     destinationSettings.disabled = true;
-    fakeDataBind(model, destinationSettings, 'settings');
     document.body.appendChild(destinationSettings);
   });
 
@@ -81,20 +73,23 @@
                    DestinationStoreEventType
                        .SELECTED_DESTINATION_CAPABILITIES_READY,
                    destinationSettings.getDestinationStoreForTest())
-            .then(() => {
+            .then(async () => {
               // The capabilities ready event results in |destinationState|
               // changing to SELECTED, which enables and shows the dropdown even
               // though |state| has not yet transitioned to READY. This is to
               // prevent brief losses of focus when the destination changes.
+              await microtasksFinished();
               assertFalse(dropdown.disabled);
               assertTrue(dropdown.loaded);
               destinationSettings.state = State.READY;
               destinationSettings.disabled = false;
+              await microtasksFinished();
 
               // Simulate setting a setting to an invalid value. Dropdown is
               // disabled due to validation error on another control.
               destinationSettings.state = State.ERROR;
               destinationSettings.disabled = true;
+              await microtasksFinished();
               assertTrue(dropdown.disabled);
 
               // Simulate the user fixing the validation error, and then
@@ -102,17 +97,19 @@
               // user can fix the error.
               destinationSettings.state = State.READY;
               destinationSettings.disabled = false;
+              await microtasksFinished();
               destinationSettings.getDestinationStoreForTest().dispatchEvent(
                   new CustomEvent(
                       DestinationStoreEventType.ERROR,
                       {detail: DestinationErrorType.INVALID}));
-              flush();
+              await microtasksFinished();
 
               assertEquals(
                   DestinationState.ERROR, destinationSettings.destinationState);
               assertEquals(Error.INVALID_PRINTER, destinationSettings.error);
               destinationSettings.state = State.ERROR;
               destinationSettings.disabled = true;
+              await microtasksFinished();
               assertFalse(dropdown.disabled);
 
               // Simulate the user having no printers.
@@ -120,13 +117,14 @@
                   new CustomEvent(
                       DestinationStoreEventType.ERROR,
                       {detail: DestinationErrorType.NO_DESTINATIONS}));
-              flush();
+              await microtasksFinished();
 
               assertEquals(
                   DestinationState.ERROR, destinationSettings.destinationState);
               assertEquals(Error.NO_DESTINATIONS, destinationSettings.error);
               destinationSettings.state = State.FATAL_ERROR;
               destinationSettings.disabled = true;
+              await microtasksFinished();
               assertTrue(dropdown.disabled);
             });
       });
@@ -139,7 +137,11 @@
    * Initializes the destination store and destination settings using
    * |destinations| and |recentDestinations|.
    */
-  function initialize() {
+  function initialize(): Promise<void> {
+    const whenCapabilitiesSet = eventToPromise(
+        DestinationStoreEventType.SELECTED_DESTINATION_CAPABILITIES_READY,
+        destinationSettings.getDestinationStoreForTest());
+
     // Initialize destination settings.
     destinationSettings.setSetting('recentDestinations', recentDestinations);
     destinationSettings.init(
@@ -147,6 +149,9 @@
         '' /* serializedDefaultDestinationSelectionRulesStr */);
     destinationSettings.state = State.READY;
     destinationSettings.disabled = false;
+
+    // Only resolved when `pdfPrinterDisabled` is false.
+    return whenCapabilitiesSet;
   }
 
   /**
@@ -174,19 +179,17 @@
 
   // Tests that the dropdown contains the appropriate destinations when there
   // are no recent destinations.
-  test(
-      'NoRecentDestinations', function() {
-        initialize();
-        return nativeLayer.whenCalled('getPrinterCapabilities').then(() => {
-          // This will result in the destination store setting the Save as
-          // PDF destination.
-          assertEquals(
-              GooglePromotedDestinationId.SAVE_AS_PDF,
-              destinationSettings.destination.id);
-          assertFalse(destinationSettings.$.destinationSelect.disabled);
-          assertDropdownItems(['Save as PDF/local/']);
-        });
-      });
+  test('NoRecentDestinations', async function() {
+    await initialize();
+    // This will result in the destination store setting the Save as
+    // PDF destination.
+    assertTrue(!!destinationSettings.destination);
+    assertEquals(
+        GooglePromotedDestinationId.SAVE_AS_PDF,
+        destinationSettings.destination.id);
+    assertFalse(destinationSettings.$.destinationSelect.disabled);
+    assertDropdownItems(['Save as PDF/local/']);
+  });
 
   // Tests that the dropdown contains the appropriate destinations when there
   // are 5 recent destinations.
@@ -195,18 +198,12 @@
         recentDestinations = destinations.slice(0, 5).map(
             destination => makeRecentDestination(destination));
 
-        const whenCapabilitiesDone =
-            nativeLayer.whenCalled('getPrinterCapabilities');
-        initialize();
-
         // Wait for the destinations to be inserted into the store.
-        return whenCapabilitiesDone
-            .then(() => {
-              return waitBeforeNextRender(destinationSettings);
-            })
-            .then(() => {
+        return initialize().then(
+            () => {
               // This will result in the destination store setting the most
               // recent destination.
+              assertTrue(!!destinationSettings.destination);
               assertEquals('ID1', destinationSettings.destination.id);
               assertFalse(destinationSettings.$.destinationSelect.disabled);
               const dropdownItems = [
@@ -225,19 +222,13 @@
             destination => makeRecentDestination(destination));
         localDestinations.splice(1, 1);
         nativeLayer.setLocalDestinations(localDestinations);
-        const whenCapabilitiesDone =
-            nativeLayer.whenCalled('getPrinterCapabilities');
-
-        initialize();
 
         // Wait for the destinations to be inserted into the store.
-        return whenCapabilitiesDone
-            .then(() => {
-              return waitBeforeNextRender(destinationSettings);
-            })
-            .then(() => {
+        return initialize().then(
+            () => {
               // This will result in the destination store setting the most
               // recent destination.
+              assertTrue(!!destinationSettings.destination);
               assertEquals('ID1', destinationSettings.destination.id);
               assertFalse(destinationSettings.$.destinationSelect.disabled);
               const dropdownItems = [
@@ -255,17 +246,12 @@
         destination => makeRecentDestination(destination));
     recentDestinations.splice(
         1, 1, makeRecentDestination(getSaveAsPdfDestination()));
-    const whenCapabilitiesDone =
-        nativeLayer.whenCalled('getPrinterCapabilities');
-    initialize();
 
-    return whenCapabilitiesDone
-        .then(() => {
-          return waitBeforeNextRender(destinationSettings);
-        })
-        .then(() => {
+    return initialize().then(
+        () => {
           // This will result in the destination store setting the most recent
           // destination.
+          assertTrue(!!destinationSettings.destination);
           assertEquals('ID1', destinationSettings.destination.id);
           assertFalse(destinationSettings.$.destinationSelect.disabled);
           const dropdownItems = [
@@ -284,19 +270,14 @@
         destination => makeRecentDestination(destination));
     recentDestinations.splice(
         1, 1, makeRecentDestination(getSaveAsPdfDestination()));
-    const whenCapabilitiesDone =
-        nativeLayer.whenCalled('getPrinterCapabilities');
-    initialize();
 
     const dropdown = destinationSettings.$.destinationSelect;
 
-    return whenCapabilitiesDone
-        .then(() => {
-          return waitBeforeNextRender(destinationSettings);
-        })
+    return initialize()
         .then(() => {
           // This will result in the destination store setting the most recent
           // destination.
+          assertTrue(!!destinationSettings.destination);
           assertEquals('ID1', destinationSettings.destination.id);
           assertFalse(dropdown.disabled);
           const dropdownItems = [
@@ -319,6 +300,7 @@
           return whenDestinationSelect;
         })
         .then(() => {
+          assertTrue(!!destinationSettings.destination);
           assertEquals(
               GooglePromotedDestinationId.SAVE_AS_PDF,
               destinationSettings.destination.id);
@@ -332,18 +314,13 @@
       'SelectRecentDestination', function() {
         recentDestinations = destinations.slice(0, 5).map(
             destination => makeRecentDestination(destination));
-        const whenCapabilitiesDone =
-            nativeLayer.whenCalled('getPrinterCapabilities');
-        initialize();
         const dropdown = destinationSettings.$.destinationSelect;
 
-        return whenCapabilitiesDone
-            .then(() => {
-              return waitBeforeNextRender(destinationSettings);
-            })
+        return initialize()
             .then(() => {
               // This will result in the destination store setting the most
               // recent destination.
+              assertTrue(!!destinationSettings.destination);
               assertEquals('ID1', destinationSettings.destination.id);
               assertFalse(dropdown.disabled);
               const dropdownItems = [
@@ -364,6 +341,7 @@
               return whenDestinationSelect;
             })
             .then(() => {
+              assertTrue(!!destinationSettings.destination);
               assertEquals('ID2', destinationSettings.destination.id);
             });
       });
@@ -372,18 +350,13 @@
   test('OpenDialog', function() {
     recentDestinations = destinations.slice(0, 5).map(
         destination => makeRecentDestination(destination));
-    const whenCapabilitiesDone =
-        nativeLayer.whenCalled('getPrinterCapabilities');
-    initialize();
     const dropdown = destinationSettings.$.destinationSelect;
 
-    return whenCapabilitiesDone
-        .then(() => {
-          return waitBeforeNextRender(destinationSettings);
-        })
+    return initialize()
         .then(() => {
           // This will result in the destination store setting the most recent
           // destination.
+          assertTrue(!!destinationSettings.destination);
           assertEquals('ID1', destinationSettings.destination.id);
           assertFalse(dropdown.disabled);
           const dropdownItems = [
@@ -395,7 +368,7 @@
           dropdown.dispatchEvent(new CustomEvent(
               'selected-option-change',
               {bubbles: true, composed: true, detail: 'seeMore'}));
-          return waitBeforeNextRender(destinationSettings);
+          return microtasksFinished();
         })
         .then(() => {
           assertTrue(destinationSettings.$.destinationDialog.get().isOpen());
@@ -416,13 +389,16 @@
   }
 
   function selectDestination(destination: Destination) {
+    const whenCapabilitiesSet = eventToPromise(
+        DestinationStoreEventType.SELECTED_DESTINATION_CAPABILITIES_READY,
+        destinationSettings.getDestinationStoreForTest());
     const storeDestination =
         destinationSettings.getDestinationStoreForTest().destinations().find(
             d => d.key === destination.key);
     assert(storeDestination);
     destinationSettings.getDestinationStoreForTest().selectDestination(
         storeDestination);
-    flush();
+    return whenCapabilitiesSet;
   }
 
   /**
@@ -430,14 +406,12 @@
    * destinations array.
    */
   test(
-      'UpdateRecentDestinations', function() {
+      'UpdateRecentDestinations', async function() {
         // Recent destinations start out empty.
         assertRecentDestinations([]);
         assertEquals(0, nativeLayer.getCallCount('getPrinterCapabilities'));
 
-        initialize();
-
-        return nativeLayer.whenCalled('getPrinterCapabilities')
+        return initialize()
             .then(() => {
               assertRecentDestinations(['Save as PDF']);
               assertEquals(
@@ -450,11 +424,11 @@
               return nativeLayer.whenCalled('getPrinters');
             })
             .then(() => {
+              nativeLayer.resetResolver('getPrinterCapabilities');
               // Simulate setting a destination from the dialog.
-              selectDestination(destinations[0]!);
-              return nativeLayer.whenCalled('getPrinterCapabilities');
+              return selectDestination(destinations[0]!);
             })
-            .then(() => {
+            .then(async () => {
               assertRecentDestinations(['ID1', 'Save as PDF']);
               assertEquals(
                   1, nativeLayer.getCallCount('getPrinterCapabilities'));
@@ -468,7 +442,7 @@
                     composed: true,
                     detail: 'Save as PDF/local/',
                   }));
-              flush();
+              await microtasksFinished();
               assertRecentDestinations(['Save as PDF', 'ID1']);
               // No additional capabilities call, since the destination was
               // previously selected.
@@ -476,8 +450,7 @@
                   0, nativeLayer.getCallCount('getPrinterCapabilities'));
 
               // Select a third destination.
-              selectDestination(destinations[1]!);
-              return nativeLayer.whenCalled('getPrinterCapabilities');
+              return selectDestination(destinations[1]!);
             })
             .then(() => {
               assertRecentDestinations(['ID2', 'Save as PDF', 'ID1']);
@@ -510,24 +483,19 @@
       'DisabledSaveAsPdf', function() {
         // Initialize destination settings with the PDF printer disabled.
         pdfPrinterDisabled = true;
-        initialize();
 
-        return nativeLayer.whenCalled('getPrinterCapabilities')
-            .then(() => {
-              return waitBeforeNextRender(destinationSettings);
-            })
-            .then(() => {
-              // Because the 'Save as PDF' fallback is unavailable, the first
-              // destination is selected.
-              const expectedDestination = makeLocalDestinationKey('ID1');
-              assertDropdownItems([expectedDestination]);
-            });
+        return initialize().then(() => {
+          // Because the 'Save as PDF' fallback is unavailable, the first
+          // destination is selected.
+          const expectedDestination = makeLocalDestinationKey('ID1');
+          assertDropdownItems([expectedDestination]);
+        });
       });
 
   // Tests that disabling the 'Save as PDF' destination and exposing no
   // printers to the native layer results in a 'No destinations' option in the
   // dropdown.
-  test('NoDestinations', function() {
+  test('NoDestinations', async function() {
     nativeLayer.setLocalDestinations([]);
 
     // Initialize destination settings with the PDF printer disabled.
@@ -537,13 +505,8 @@
     // 'getPrinters' will be called because there are no printers known to
     // the destination store and the 'Save as PDF' fallback is
     // unavailable.
-    return Promise
-        .all([
-          nativeLayer.whenCalled('getPrinters'),
-          // TODO (rbpotter): remove this wait once user manager is fully
-          // removed.
-          waitBeforeNextRender(destinationSettings),
-        ])
-        .then(() => assertDropdownItems(['noDestinations']));
+    await nativeLayer.whenCalled('getPrinters');
+    await microtasksFinished();
+    assertDropdownItems(['noDestinations']);
   });
 });
diff --git a/chrome/test/data/webui/print_preview/preview_generation_test.ts b/chrome/test/data/webui/print_preview/preview_generation_test.ts
index 7563ee5..7443e06 100644
--- a/chrome/test/data/webui/print_preview/preview_generation_test.ts
+++ b/chrome/test/data/webui/print_preview/preview_generation_test.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import type {NativeInitialSettings, PreviewTicket, PrintPreviewAppElement, PrintPreviewDestinationSettingsElement, Range, Settings} from 'chrome://print/print_preview.js';
-import {ColorMode, CustomMarginsOrientation, Destination, DestinationOrigin, DestinationState, Margins, MarginsType, NativeLayerImpl, PluginProxyImpl, ScalingType} from 'chrome://print/print_preview.js';
+import {ColorMode, CustomMarginsOrientation, Destination, DestinationOrigin, Margins, MarginsType, NativeLayerImpl, PluginProxyImpl, ScalingType} from 'chrome://print/print_preview.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 import {NativeLayerStub} from './native_layer_stub.js';
@@ -564,6 +564,7 @@
               page.shadowRoot!.querySelector('print-preview-sidebar')!
                   .shadowRoot.querySelector(
                       'print-preview-destination-settings')!;
+          assertTrue(!!destinationSettings.destination);
           assertEquals('FooDevice', destinationSettings.destination.id);
           assertEquals('FooDevice', originalTicket.deviceName);
           const barDestination =
@@ -581,13 +582,12 @@
           };
           barDestination.capabilities = capabilities;
           nativeLayer.resetResolver('getPreview');
-          destinationSettings.destinationState = DestinationState.SET;
           destinationSettings.getDestinationStoreForTest().selectDestination(
               barDestination);
-          destinationSettings.destinationState = DestinationState.UPDATED;
           return nativeLayer.whenCalled('getPreview');
         })
         .then(function(args) {
+          assertTrue(!!destinationSettings.destination);
           assertEquals('BarDevice', destinationSettings.destination.id);
           const ticket: PreviewTicket = JSON.parse(args.printTicket);
           assertEquals('BarDevice', ticket.deviceName);
diff --git a/chrome/test/data/webui/print_preview/system_dialog_test.ts b/chrome/test/data/webui/print_preview/system_dialog_test.ts
index 1110a83..2a0426d8 100644
--- a/chrome/test/data/webui/print_preview/system_dialog_test.ts
+++ b/chrome/test/data/webui/print_preview/system_dialog_test.ts
@@ -60,7 +60,7 @@
               'FooDevice',
               sidebar.shadowRoot
                   .querySelector(
-                      'print-preview-destination-settings')!.destination.id);
+                      'print-preview-destination-settings')!.destination!.id);
           // <if expr="is_win">
           link = linkContainer.$.systemDialogLink;
           // </if>
diff --git a/chrome/test/data/webui/print_preview/test_plugin_proxy.ts b/chrome/test/data/webui/print_preview/test_plugin_proxy.ts
index fbfbd28f..50c718f 100644
--- a/chrome/test/data/webui/print_preview/test_plugin_proxy.ts
+++ b/chrome/test/data/webui/print_preview/test_plugin_proxy.ts
@@ -25,7 +25,7 @@
     this.loadCompleteCallback_ = loadCompleteCallback;
   }
 
-  setPreloadCallback(preloadCallback: (() => void)|null) {
+  setPreloadCallback(preloadCallback: () => void) {
     this.preloadCallback_ = preloadCallback;
   }
 
diff --git a/chrome/test/data/webui/settings/settings_browsertest.cc b/chrome/test/data/webui/settings/settings_browsertest.cc
index 58bb010..122859de 100644
--- a/chrome/test/data/webui/settings/settings_browsertest.cc
+++ b/chrome/test/data/webui/settings/settings_browsertest.cc
@@ -411,8 +411,7 @@
     scoped_feature_list_.InitWithFeaturesAndParameters(
         {{features::kGlicLearnMoreURLConfig,
           {
-              {"glic-shortcuts-launcher-toggle-learn-more-url",
-               "https://google.com/"},
+              {"glic-launcher-toggle-learn-more-url", "https://google.com/"},
           }}},
         /*disabled_features=*/{});
   }
@@ -433,8 +432,7 @@
     scoped_feature_list_.InitWithFeaturesAndParameters(
         {{features::kGlicLearnMoreURLConfig,
           {
-              {"glic-shortcuts-location-toggle-learn-more-url",
-               "https://google.com/"},
+              {"glic-location-toggle-learn-more-url", "https://google.com/"},
           }}},
         /*disabled_features=*/{});
   }
@@ -456,8 +454,7 @@
     scoped_feature_list_.InitWithFeaturesAndParameters(
         {{features::kGlicLearnMoreURLConfig,
           {
-              {"glic-shortcuts-tab-access-toggle-learn-more-url",
-               "https://google.com/"},
+              {"glic-tab-access-toggle-learn-more-url", "https://google.com/"},
           }}},
         /*disabled_features=*/{});
   }
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts
index 6d06f6f..64f2981 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/app_test.ts
@@ -7,7 +7,7 @@
 import type {AppElement} from 'chrome://customize-chrome-side-panel.top-chrome/app.js';
 import {CustomizeChromeImpression} from 'chrome://customize-chrome-side-panel.top-chrome/common.js';
 import type {BackgroundCollection, CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
-import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote, CustomizeChromeSection} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote, CustomizeChromeSection, NewTabPageType} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome_api_proxy.js';
 import {CustomizeToolbarClientCallbackRouter, CustomizeToolbarHandlerRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_toolbar.mojom-webui.js';
 import type {CustomizeToolbarHandlerInterface} from 'chrome://customize-chrome-side-panel.top-chrome/customize_toolbar.mojom-webui.js';
@@ -230,7 +230,7 @@
     });
   });
 
-  test('isSourceTabFirstPartyNtp should update the cards', async () => {
+  test('source tab type should update the cards', async () => {
     const idsControlledByIsSourceTabFirstPartyNtp = [
       '#shortcuts',
       '#modules',
@@ -249,19 +249,29 @@
       '#buttonContainer',
     ];
 
-    const checkIdsVisibility = (isSourceTabFirstPartyNtp: boolean) => {
+    const newTabPageTypes = [
+      NewTabPageType.kFirstPartyWebUI,
+      NewTabPageType.kThirdPartyWebUI,
+      NewTabPageType.kThirdPartyRemote,
+      NewTabPageType.kExtension,
+      NewTabPageType.kIncognito,
+      NewTabPageType.kGuestMode,
+      NewTabPageType.kNone,
+    ];
+
+    const checkIdsVisibility = (sourceTabType: NewTabPageType) => {
       idsControlledByIsSourceTabFirstPartyNtp.forEach(
           id => assertEquals(
-              isSourceTabFirstPartyNtp,
+              sourceTabType === NewTabPageType.kFirstPartyWebUI,
               !!customizeChromeApp.shadowRoot.querySelector(id)));
       idsNotControlledByIsSourceTabFirstPartyNtp.forEach(
           id => assertTrue(!!customizeChromeApp.shadowRoot.querySelector(id)));
     };
 
-    await[true, false].forEach(async b => {
-      callbackRouter.attachedTabStateUpdated(b);
+    await newTabPageTypes.forEach(async t => {
+      callbackRouter.attachedTabStateUpdated(t);
       await microtasksFinished();
-      checkIdsVisibility(b);
+      checkIdsVisibility(t);
     });
   });
 
@@ -301,7 +311,7 @@
               'selected'));
           assertEquals(customizeChromeApp, document.activeElement);
 
-          callbackRouter.attachedTabStateUpdated(false);
+          callbackRouter.attachedTabStateUpdated(NewTabPageType.kExtension);
           callbackRouter.setThemeEditable(false);
           await microtasksFinished();
 
@@ -326,7 +336,7 @@
               customizeChromeApp.shadowRoot.querySelector('#toolbarPage')!
                   .classList.contains('selected'));
 
-          callbackRouter.attachedTabStateUpdated(false);
+          callbackRouter.attachedTabStateUpdated(NewTabPageType.kExtension);
           await microtasksFinished();
 
           // Current page should now be toolbar.
diff --git a/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts b/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts
index ae64285..b2dc801b 100644
--- a/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts
+++ b/chrome/test/data/webui/side_panel/customize_chrome/appearance_test.ts
@@ -7,7 +7,7 @@
 import type {AppearanceElement} from 'chrome://customize-chrome-side-panel.top-chrome/appearance.js';
 import {CustomizeChromeAction} from 'chrome://customize-chrome-side-panel.top-chrome/common.js';
 import type {CustomizeChromePageRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
-import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
+import {CustomizeChromePageCallbackRouter, CustomizeChromePageHandlerRemote, NewTabPageType} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome.mojom-webui.js';
 import {CustomizeChromeApiProxy} from 'chrome://customize-chrome-side-panel.top-chrome/customize_chrome_api_proxy.js';
 import type {ManagedDialogElement} from 'chrome://resources/cr_components/managed_dialog/managed_dialog.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.js';
@@ -619,7 +619,7 @@
     });
   });
 
-  test('isSourceTabFirstPartyNtp should update the content', async () => {
+  test('source tab type should update the content', async () => {
     const idsControlledByIsSourceTabFirstPartyNtp = [
       '#editButtonsContainer',
       '#themeSnapshot',
@@ -637,19 +637,29 @@
       '#editThemeIcon',
     ];
 
-    const checkIdsVisibility = (isSourceTabFirstPartyNtp: boolean) => {
+    const newTabPageTypes = [
+      NewTabPageType.kFirstPartyWebUI,
+      NewTabPageType.kThirdPartyWebUI,
+      NewTabPageType.kThirdPartyRemote,
+      NewTabPageType.kExtension,
+      NewTabPageType.kIncognito,
+      NewTabPageType.kGuestMode,
+      NewTabPageType.kNone,
+    ];
+
+    const checkIdsVisibility = (sourceTabType: NewTabPageType) => {
       idsControlledByIsSourceTabFirstPartyNtp.forEach(
           id => assertEquals(
-              isSourceTabFirstPartyNtp,
+              sourceTabType === NewTabPageType.kFirstPartyWebUI,
               !!appearanceElement.shadowRoot.querySelector(id)));
       idsNotControlledByIsSourceTabFirstPartyNtp.forEach(
           id => assertTrue(!!appearanceElement.shadowRoot.querySelector(id)));
     };
 
-    await[true, false].forEach(async b => {
-      callbackRouterRemote.attachedTabStateUpdated(b);
+    await newTabPageTypes.forEach(async t => {
+      callbackRouterRemote.attachedTabStateUpdated(t);
       await microtasksFinished();
-      checkIdsVisibility(b);
+      checkIdsVisibility(t);
     });
   });
 });
diff --git a/chrome/test/data/webui/side_panel/read_anything/BUILD.gn b/chrome/test/data/webui/side_panel/read_anything/BUILD.gn
index feee120..857e8e3 100644
--- a/chrome/test/data/webui/side_panel/read_anything/BUILD.gn
+++ b/chrome/test/data/webui/side_panel/read_anything/BUILD.gn
@@ -64,7 +64,6 @@
     "voice_pack_controller_test.ts",
     "voice_pack_model_test.ts",
     "voice_selection_menu_test.ts",
-    "voice_selection_test.ts",
     "word_boundaries_speech_test.ts",
     "word_boundaries_test.ts",
     "word_highlighting_test.ts",
diff --git a/chrome/test/data/webui/side_panel/read_anything/highlighter_test.ts b/chrome/test/data/webui/side_panel/read_anything/highlighter_test.ts
index b323d3b..28844881 100644
--- a/chrome/test/data/webui/side_panel/read_anything/highlighter_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/highlighter_test.ts
@@ -52,6 +52,8 @@
   test('isInvalidHighlightForWordHighlighting', () => {
     assertTrue(highlighter.isInvalidHighlightForWordHighlighting());
     assertTrue(highlighter.isInvalidHighlightForWordHighlighting(''));
+    assertTrue(highlighter.isInvalidHighlightForWordHighlighting(' '));
+    assertTrue(highlighter.isInvalidHighlightForWordHighlighting('  '));
     assertTrue(highlighter.isInvalidHighlightForWordHighlighting('!'));
     assertTrue(highlighter.isInvalidHighlightForWordHighlighting('()?!?'));
     assertFalse(highlighter.isInvalidHighlightForWordHighlighting('hello !!!'));
@@ -133,6 +135,42 @@
         id);
   });
 
+  test('word highlight across multiple nodes with engine length', () => {
+    chrome.readingMode.onHighlightGranularityChanged(
+        chrome.readingMode.wordHighlighting);
+    // speechUtteranceLength should extend across multiple nodes.
+    wordBoundaries.updateBoundary(0, 4);
+
+    const bold = document.createElement('b');
+    const text1 = 'I\'m';
+    bold.appendChild(document.createTextNode(text1));
+    const sentence = document.createElement('p');
+    // A space is intentionally inserted into the beginning of this segment.
+    const text2 = ' slipping into the lava.';
+    sentence.appendChild(document.createTextNode(text2));
+    const id1 = 10;
+    const id2 = 12;
+    chrome.readingMode.getHighlightForCurrentSegmentIndex = () =>
+        [{nodeId: id1, start: 0, length: 3},
+         {nodeId: id2, start: 0, length: 1}];
+    nodeStore.setDomNode(bold, id1);
+    nodeStore.setDomNode(sentence, id2);
+    chrome.readingMode.getCurrentTextStartIndex = () => 0;
+    chrome.readingMode.getCurrentTextEndIndex = () =>
+        text1.length + text2.length;
+
+    highlighter.highlightCurrentGranularity(
+        [id1, id2], /*scrollIntoView=*/ false,
+        /*shouldUpdateSentenceHighlight=*/ true);
+
+    assertTrue(highlighter.hasCurrentHighlights());
+
+    // Only "I'm" is highlighted. The rest of the sentence, including the space
+    // after "I'm" remains unhighlighted.
+    assertHtml('<span class="current-read-highlight">I\'m</span>', id1);
+    assertHtml(' slipping into the lava.', id2);
+  });
+
   test('phrase highlight', () => {
     chrome.readingMode.onHighlightGranularityChanged(
         chrome.readingMode.autoHighlighting);
diff --git a/chrome/test/data/webui/side_panel/read_anything/language_change_test.ts b/chrome/test/data/webui/side_panel/read_anything/language_change_test.ts
index 0e69c6a..a2008bd 100644
--- a/chrome/test/data/webui/side_panel/read_anything/language_change_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/language_change_test.ts
@@ -109,7 +109,7 @@
     app.languageChanged();
     await microtasksFinished();
 
-    assertEquals(otherVoice, app.getSpeechSynthesisVoice());
+    assertEquals(otherVoice, voicePackController.getCurrentVoice());
   });
 
   test('enables the stored voice language', async () => {
@@ -124,7 +124,7 @@
     await microtasksFinished();
 
     assertTrue(voicePackController.isLangEnabled(voice.lang));
-    assertEquals(voice, app.getSpeechSynthesisVoice());
+    assertEquals(voice, voicePackController.getCurrentVoice());
   });
 
   suite('when there is no stored voice for this language', () => {
@@ -142,19 +142,20 @@
             app, ToolbarEvent.VOICE, {detail: {selectedVoice: otherVoice}});
         app.languageChanged();
         await microtasksFinished();
-        assertEquals(otherVoice, app.getSpeechSynthesisVoice());
+        assertEquals(otherVoice, voicePackController.getCurrentVoice());
       });
 
       test('to a natural voice if there\'s no current voice', async () => {
         app.languageChanged();
         await microtasksFinished();
-        assertEquals(naturalVoiceWithLang3, app.getSpeechSynthesisVoice());
+        assertEquals(
+            naturalVoiceWithLang3, voicePackController.getCurrentVoice());
       });
 
       test('to the device default if there\'s no natural', () => {
         setVoices(app, speech, voices.filter(v => v !== naturalVoiceWithLang3));
         app.languageChanged();
-        assertEquals(defaultVoice, app.getSpeechSynthesisVoice());
+        assertEquals(defaultVoice, voicePackController.getCurrentVoice());
       });
     });
 
@@ -167,7 +168,7 @@
 
       app.languageChanged();
 
-      assertEquals(voice, app.getSpeechSynthesisVoice());
+      assertEquals(voice, voicePackController.getCurrentVoice());
     });
 
     test('to a voice in the available locale for this base language', () => {
@@ -179,14 +180,15 @@
 
       app.languageChanged();
 
-      assertEquals(voice, app.getSpeechSynthesisVoice());
+      assertEquals(voice, voicePackController.getCurrentVoice());
     });
 
     suite('and this locale is enabled', () => {
       test('to a natural voice for this language', () => {
         chrome.readingMode.baseLanguageForSpeech = lang3;
         app.languageChanged();
-        assertEquals(naturalVoiceWithLang3, app.getSpeechSynthesisVoice());
+        assertEquals(
+            naturalVoiceWithLang3, voicePackController.getCurrentVoice());
       });
 
       test(
@@ -194,7 +196,8 @@
           () => {
             chrome.readingMode.baseLanguageForSpeech = lang1;
             app.languageChanged();
-            assertEquals(defaultVoiceWithLang1, app.getSpeechSynthesisVoice());
+            assertEquals(
+                defaultVoiceWithLang1, voicePackController.getCurrentVoice());
           });
 
       test(
@@ -202,7 +205,8 @@
           () => {
             chrome.readingMode.baseLanguageForSpeech = lang2;
             app.languageChanged();
-            assertEquals(firstVoiceWithLang2, app.getSpeechSynthesisVoice());
+            assertEquals(
+                firstVoiceWithLang2, voicePackController.getCurrentVoice());
           });
     });
 
@@ -213,7 +217,8 @@
         app.languageChanged();
 
         assertTrue(voicePackController.isLangEnabled(lang3));
-        assertEquals(naturalVoiceWithLang3, app.getSpeechSynthesisVoice());
+        assertEquals(
+            naturalVoiceWithLang3, voicePackController.getCurrentVoice());
       });
 
       test(
@@ -224,7 +229,8 @@
             app.languageChanged();
 
             assertTrue(voicePackController.isLangEnabled(lang1));
-            assertEquals(defaultVoiceWithLang1, app.getSpeechSynthesisVoice());
+            assertEquals(
+                defaultVoiceWithLang1, voicePackController.getCurrentVoice());
           });
 
 
@@ -239,7 +245,7 @@
 
         app.languageChanged();
 
-        assertEquals(voice, app.getSpeechSynthesisVoice());
+        assertEquals(voice, voicePackController.getCurrentVoice());
       });
 
       test('to natural enabled voice if no same locale', () => {
@@ -249,7 +255,8 @@
 
         app.languageChanged();
 
-        assertEquals(naturalVoiceWithLang3, app.getSpeechSynthesisVoice());
+        assertEquals(
+            naturalVoiceWithLang3, voicePackController.getCurrentVoice());
       });
 
       test('to default enabled voice if no natural voice', () => {
@@ -259,10 +266,11 @@
 
         app.languageChanged();
 
-        assertEquals(defaultVoiceWithLang1, app.getSpeechSynthesisVoice());
+        assertEquals(
+            defaultVoiceWithLang1, voicePackController.getCurrentVoice());
       });
 
-      test('to undefined if no enabled languages', () => {
+      test('to null if no enabled languages', () => {
         chrome.readingMode.baseLanguageForSpeech = lang2;
         for (const lang of voicePackController.getEnabledLangs()) {
           voicePackController.disableLang(lang);
@@ -270,9 +278,7 @@
 
         app.languageChanged();
 
-        assertEquals(
-            undefined, app.getSpeechSynthesisVoice(),
-            app.getSpeechSynthesisVoice()?.name);
+        assertFalse(!!voicePackController.getCurrentVoice());
       });
     });
   });
@@ -316,7 +322,7 @@
     test('but doesn\'t if the language is already installing', () => {
       const lang = 'bn-bd';
       const voicePackLang = convertLangOrLocaleForVoicePackManager(lang);
-      assertTrue(voicePackLang !== undefined);
+      assertTrue(!!voicePackLang);
 
       app.updateVoicePackStatus(lang, 'kInstalling');
       app.languageChanged();
diff --git a/chrome/test/data/webui/side_panel/read_anything/phrase_highlighting_test.ts b/chrome/test/data/webui/side_panel/read_anything/phrase_highlighting_test.ts
index 0757eb30..0014774 100644
--- a/chrome/test/data/webui/side_panel/read_anything/phrase_highlighting_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/phrase_highlighting_test.ts
@@ -4,7 +4,7 @@
 
 import type {CrIconButtonElement} from '//resources/cr_elements/cr_icon_button/cr_icon_button.js';
 import type {AppElement, ReadAnythingToolbarElement} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
-import {SpeechController, WordBoundaries} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
+import {ReadAloudHighlighter, SpeechController, VoicePackController, WordBoundaries} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
 import {assertEquals, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
 import {microtasksFinished} from 'chrome-untrusted://webui-test/test_util.js';
 
@@ -66,9 +66,12 @@
     chrome.readingMode.onConnected = () => {};
 
     SpeechController.setInstance(new SpeechController());
+    VoicePackController.setInstance(new VoicePackController());
+    wordBoundaries = new WordBoundaries();
+    WordBoundaries.setInstance(wordBoundaries);
+    ReadAloudHighlighter.setInstance(new ReadAloudHighlighter());
     metrics = mockMetrics();
     app = await createApp();
-    wordBoundaries = WordBoundaries.getInstance();
 
     // Use a tree with just one sentence. For the actual implementation of
     // phrase segmentation, a more realistic example would be to use
diff --git a/chrome/test/data/webui/side_panel/read_anything/prefs_test.ts b/chrome/test/data/webui/side_panel/read_anything/prefs_test.ts
index 2f625548..9aba687b7 100644
--- a/chrome/test/data/webui/side_panel/read_anything/prefs_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/prefs_test.ts
@@ -100,7 +100,7 @@
 
         // Set synthesis to have no available voices
         setVoices(app, speech, []);
-        app.resetVoiceForTesting();
+        voicePackController.setCurrentVoice(null);
       });
 
       test('with no settings, voice selected in onVoicesChanged', () => {
@@ -109,41 +109,40 @@
         // When there's no voices available, there shouldn't be a speech
         // synthesis voice selected.
         app.restoreSettingsFromPrefs();
-        assertFalse(!!app.getSpeechSynthesisVoice());
+        assertFalse(!!voicePackController.getCurrentVoice());
 
         // Update the speech synthesis engine with voices.
         setupBasicSpeech(app, speech);
 
         // Once voices are available, settings should be restored.
-        assertTrue(!!app.getSpeechSynthesisVoice());
+        assertTrue(!!voicePackController.getCurrentVoice());
+      });
+
+      test('with no settings, dfferent language voice selected', () => {
+        chrome.readingMode.getStoredVoice = () => '';
+
+        // When there's no voices available, there shouldn't be a speech
+        // synthesis voice selected.
+        app.restoreSettingsFromPrefs();
+        assertFalse(!!voicePackController.getCurrentVoice());
+
+        // Update the speech synthesis engine with voices.
+        setupBasicSpeech(app, speech);
+
+        // Once voices are available, settings should be restored.
+        assertTrue(!!voicePackController.getCurrentVoice());
       });
 
       test(
-          'with no settings, dfferent language voice selected in onVoicesChanged',
-          () => {
-            chrome.readingMode.getStoredVoice = () => '';
-
-            // When there's no voices available, there shouldn't be a speech
-            // synthesis voice selected.
-            app.restoreSettingsFromPrefs();
-            assertFalse(!!app.getSpeechSynthesisVoice());
-
-            // Update the speech synthesis engine with voices.
-            setupBasicSpeech(app, speech);
-
-            // Once voices are available, settings should be restored.
-            assertTrue(!!app.getSpeechSynthesisVoice());
-          });
-
-      test(
-          'with no initial voices and previously selected voice, correct voice selected after onVoicesChanged',
+          'with no initial voices and previously selected voice, correct ' +
+              'voice selected after onVoicesChanged',
           () => {
             chrome.readingMode.getStoredVoice = () => 'Google Kristi';
 
             // When there's no voices available, there shouldn't be a speech
             // synthesis voice selected.
             app.restoreSettingsFromPrefs();
-            assertFalse(!!app.getSpeechSynthesisVoice());
+            assertFalse(!!voicePackController.getCurrentVoice());
 
             // Update the speech synthesis engine with voices.
             createAndSetVoices(app, speech, [
@@ -153,7 +152,7 @@
             ]);
 
             // Once voices are available, settings should be restored.
-            const selectedVoice = app.getSpeechSynthesisVoice();
+            const selectedVoice = voicePackController.getCurrentVoice();
             assertTrue(!!selectedVoice);
             assertEquals('Google Kristi', selectedVoice.name);
           });
@@ -166,7 +165,7 @@
             // When there's no voices available, there shouldn't be a speech
             // synthesis voice selected.
             app.restoreSettingsFromPrefs();
-            assertFalse(!!app.getSpeechSynthesisVoice());
+            assertFalse(!!voicePackController.getCurrentVoice());
 
             const futureSelectedVoice =
                 createSpeechSynthesisVoice({lang: 'en', name: 'Google Kristi'});
@@ -179,14 +178,14 @@
             ]);
 
             // Once voices are available, settings should be restored.
-            let selectedVoice = app.getSpeechSynthesisVoice();
+            let selectedVoice = voicePackController.getCurrentVoice();
             assertTrue(!!selectedVoice);
             assertEquals('Google Shari', selectedVoice.name);
 
             emitEvent(
                 app, ToolbarEvent.VOICE,
                 {detail: {selectedVoice: futureSelectedVoice}});
-            selectedVoice = app.getSpeechSynthesisVoice();
+            selectedVoice = voicePackController.getCurrentVoice();
             assertTrue(!!selectedVoice);
             assertEquals('Google Kristi', selectedVoice.name);
 
@@ -197,7 +196,7 @@
 
             // After onVoicesChanged, the most recently selected voice should
             // be used.
-            selectedVoice = app.getSpeechSynthesisVoice();
+            selectedVoice = voicePackController.getCurrentVoice();
             assertTrue(!!selectedVoice);
             assertEquals('Google Kristi', selectedVoice.name);
           });
@@ -233,98 +232,5 @@
             [langs[1], locales[1]], voicePackController.getEnabledLangs());
       });
     });
-
-    suite('initializes voice', () => {
-      const langForDefaultVoice = 'en';
-      const lang1 = 'zh';
-      const lang2 = 'tr';
-      const langWithNoVoices = 'elvish';
-
-      const defaultVoice = createSpeechSynthesisVoice({
-        lang: langForDefaultVoice,
-        name: 'Google Kristi',
-        default: true,
-      });
-      const firstVoiceWithLang1 =
-          createSpeechSynthesisVoice({lang: lang1, name: 'Google Lauren'});
-      const defaultVoiceWithLang1 = createSpeechSynthesisVoice({
-        lang: lang1,
-        name: 'Google Eitan',
-        default: true,
-      });
-      const firstVoiceWithLang2 =
-          createSpeechSynthesisVoice({lang: lang2, name: 'Google Yu'});
-      const secondVoiceWithLang2 =
-          createSpeechSynthesisVoice({lang: lang2, name: 'Google Xiang'});
-      const otherVoice =
-          createSpeechSynthesisVoice({lang: 'it', name: 'Google Shari'});
-      const voices = [
-        defaultVoice,
-        firstVoiceWithLang1,
-        defaultVoiceWithLang1,
-        otherVoice,
-        firstVoiceWithLang2,
-        secondVoiceWithLang2,
-      ];
-
-      setup(() => {
-        setVoices(app, speech, voices);
-      });
-
-      test('to the stored voice for this language if there is one', () => {
-        chrome.readingMode.getStoredVoice = () => otherVoice.name;
-        app.restoreSettingsFromPrefs();
-        assertEquals(otherVoice, app.getSpeechSynthesisVoice());
-      });
-
-      test('to a default voice if the stored voice is invalid', () => {
-        chrome.readingMode.getStoredVoice = () => 'Matt';
-        voicePackController.enableLang(langForDefaultVoice);
-        app.restoreSettingsFromPrefs();
-        assertEquals(defaultVoice, app.getSpeechSynthesisVoice());
-      });
-
-      suite('when there is no stored voice for this language', () => {
-        setup(() => {
-          chrome.readingMode.getStoredVoice = () => '';
-        });
-
-        test('to the default voice for this language', () => {
-          voicePackController.enableLang(lang1);
-          voicePackController.setCurrentLanguage(lang1);
-          app.restoreSettingsFromPrefs();
-          assertEquals(defaultVoiceWithLang1, app.getSpeechSynthesisVoice());
-        });
-
-        test('uses current voice if there\'s none for this language', () => {
-          voicePackController.setCurrentLanguage(langWithNoVoices);
-          emitEvent(
-              app, ToolbarEvent.VOICE, {detail: {selectedVoice: otherVoice}});
-          voicePackController.enableLang(otherVoice.lang);
-          app.restoreSettingsFromPrefs();
-          assertEquals(otherVoice, app.getSpeechSynthesisVoice());
-        });
-
-        test('uses the device default if there\'s no current voice', () => {
-          voicePackController.setCurrentLanguage(langWithNoVoices);
-          voicePackController.enableLang(langForDefaultVoice);
-          voicePackController.enableLang(otherVoice.lang);
-          app.restoreSettingsFromPrefs();
-          assertEquals(defaultVoice, app.getSpeechSynthesisVoice());
-        });
-
-        test(
-            'to the first listed voice for this language if there\'s no default',
-            () => {
-              voicePackController.enableLang(lang2);
-              voicePackController.setCurrentLanguage(lang2);
-              app.restoreSettingsFromPrefs();
-              const currentSelectedVoice = app.getSpeechSynthesisVoice();
-              assertTrue(!!currentSelectedVoice);
-              assertEquals(firstVoiceWithLang2.name, currentSelectedVoice.name);
-              assertEquals(firstVoiceWithLang2.lang, currentSelectedVoice.lang);
-            });
-      });
-    });
   });
 });
diff --git a/chrome/test/data/webui/side_panel/read_anything/read_aloud_highlighting_test.ts b/chrome/test/data/webui/side_panel/read_anything/read_aloud_highlighting_test.ts
index 72b2af6a..083eb8b 100644
--- a/chrome/test/data/webui/side_panel/read_anything/read_aloud_highlighting_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/read_aloud_highlighting_test.ts
@@ -4,7 +4,7 @@
 import 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
 
 import type {AppElement} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
-import {PauseActionSource, playFromSelectionTimeout, SpeechController, ToolbarEvent} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
+import {PauseActionSource, playFromSelectionTimeout, SpeechController, ToolbarEvent, VoicePackController} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
 import {assertEquals, assertFalse} from 'chrome-untrusted://webui-test/chai_assert.js';
 import {MockTimer} from 'chrome-untrusted://webui-test/mock_timer.js';
 
@@ -67,6 +67,7 @@
     chrome.readingMode.onConnected = () => {};
     speechController = new SpeechController();
     SpeechController.setInstance(speechController);
+    VoicePackController.setInstance(new VoicePackController());
 
     app = await createApp();
     chrome.readingMode.setContentForTesting(axTree, leafIds);
diff --git a/chrome/test/data/webui/side_panel/read_anything/read_anything_browsertest.cc b/chrome/test/data/webui/side_panel/read_anything/read_anything_browsertest.cc
index 6fdf8e5..9ad2a6a 100644
--- a/chrome/test/data/webui/side_panel/read_anything/read_anything_browsertest.cc
+++ b/chrome/test/data/webui/side_panel/read_anything/read_anything_browsertest.cc
@@ -191,11 +191,6 @@
                    "mocha.run()");
 }
 
-IN_PROC_BROWSER_TEST_F(ReadAnythingMochaTest, VoiceSelection) {
-  RunSidePanelTest("side_panel/read_anything/voice_selection_test.js",
-                   "mocha.run()");
-}
-
 IN_PROC_BROWSER_TEST_F(ReadAnythingMochaTest, Prefs) {
   RunSidePanelTest("side_panel/read_anything/prefs_test.js", "mocha.run()");
 }
diff --git a/chrome/test/data/webui/side_panel/read_anything/read_anything_logger_test.ts b/chrome/test/data/webui/side_panel/read_anything/read_anything_logger_test.ts
index f773b17..96c69788 100644
--- a/chrome/test/data/webui/side_panel/read_anything/read_anything_logger_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/read_anything_logger_test.ts
@@ -207,7 +207,7 @@
     const expectedTime = 100;
 
     await new Promise(resolve => setTimeout(resolve, expectedTime));
-    logger.logSpeechPlaySession(startTime, undefined);
+    logger.logSpeechPlaySession(startTime, null);
 
     // The playback length should be at least the amount of time we waited above
     // and less than the starting time (i.e. we should be recording length of
diff --git a/chrome/test/data/webui/side_panel/read_anything/speech_test.ts b/chrome/test/data/webui/side_panel/read_anything/speech_test.ts
index 97f73c8..ad9de934 100644
--- a/chrome/test/data/webui/side_panel/read_anything/speech_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/speech_test.ts
@@ -423,6 +423,11 @@
     test('on text-too-long error smaller text segment plays', () => {
       createAndSetVoices(
           app, speech, [{lang: 'en', name: 'Google Bob', localService: true}]);
+      emitEvent(app, ToolbarEvent.VOICE, {
+        detail: {
+          selectedVoice: speech.getVoices()[0],
+        },
+      });
       const accessibleTextLength = app.getAccessibleTextLength(longSentences);
       app.playSpeech();
       assertEquals(longSentences, getSpokenText());
@@ -512,7 +517,7 @@
       assertTrue(app.$.toolbar.isReadAloudPlayable);
     });
 
-    test('selects default voice on language-unavailable', async () => {
+    test('stops speech on language-unavailable', async () => {
       const pageLanguage = 'es';
       assertNotEquals(
           chrome.readingMode.defaultLanguageForSpeech, pageLanguage);
@@ -532,87 +537,31 @@
       assertEquals(0, speech.getCallCount('pause'));
       assertEquals(0, speech.getCallCount('speak'));
       assertEquals(
-          chrome.readingMode.defaultLanguageForSpeech,
-          voicePackController.getCurrentLanguage());
-      assertEquals(
           chrome.readingMode.engineErrorStopSource,
           await metrics.whenCalled('recordSpeechStopSource'));
     });
 
-    suite('voice change to unavailable voice', () => {
-      let utterance: SpeechSynthesisUtterance;
+    test('stops speech on voice-unavailable', async () => {
+      const pageLanguage = 'es';
+      assertNotEquals(
+          chrome.readingMode.defaultLanguageForSpeech, pageLanguage);
+      assertNotEquals(
+          chrome.readingMode.defaultLanguageForSpeech,
+          voicePackController.getCurrentLanguage());
+      chrome.readingMode.setLanguageForTesting(pageLanguage);
+      app.playSpeech();
+      assertEquals(1, speech.getCallCount('speak'), 'speak');
+      const utterance = speech.getArgs('speak')[0];
+      speech.reset();
 
-      setup(() => {
-        app.playSpeech();
-        assertEquals(1, speech.getCallCount('speak'));
-        utterance = speech.getArgs('speak')[0];
-      });
+      utterance.onerror(createSpeechErrorEvent(utterance, 'voice-unavailable'));
 
-      test('cancels and selects default voice', async () => {
-        chrome.readingMode.setLanguageForTesting('en');
-        emitEvent(app, ToolbarEvent.VOICE, {
-          detail: {
-            selectedVoice:
-                createSpeechSynthesisVoice({lang: 'en', name: 'Lisie'}),
-          },
-        });
-        speech.reset();
-
-        assertTrue(!!utterance.onerror);
-        utterance.onerror(
-            createSpeechErrorEvent(utterance, 'voice-unavailable'));
-
-        assertEquals(1, speech.getCallCount('cancel'));
-        assertEquals(0, speech.getCallCount('pause'));
-        assertEquals(0, speech.getCallCount('speak'));
-        assertEquals(speech.getVoices()[0], app.getSpeechSynthesisVoice());
-        assertEquals(
-            chrome.readingMode.engineErrorStopSource,
-            await metrics.whenCalled('recordSpeechStopSource'));
-      });
-
-      test('still in getVoices(), cancels and selects another voice', () => {
-        chrome.readingMode.setLanguageForTesting('en');
-        createAndSetVoices(app, speech, [
-          {lang: 'en', name: 'Google George'},
-          {lang: 'en', name: 'Google Connie'},
-        ]);
-        emitEvent(app, ToolbarEvent.VOICE, {
-          detail: {selectedVoice: speech.getVoices()[0]},
-        });
-        speech.reset();
-
-        assertTrue(!!utterance.onerror);
-        utterance.onerror(
-            createSpeechErrorEvent(utterance, 'voice-unavailable'));
-
-        assertEquals(1, speech.getCallCount('cancel'));
-        assertEquals(0, speech.getCallCount('pause'));
-        assertEquals(0, speech.getCallCount('speak'));
-        assertEquals(speech.getVoices()[1], app.getSpeechSynthesisVoice());
-      });
-
-      test(
-          'continues to select default voice if no voices available in language',
-          () => {
-            chrome.readingMode.setLanguageForTesting('elvish');
-            emitEvent(app, ToolbarEvent.VOICE, {
-              detail: {
-                selectedVoice: createSpeechSynthesisVoice(
-                    {lang: 'en', name: 'Google Lauren'}),
-              },
-            });
-            speech.reset();
-
-            assertTrue(!!utterance.onerror);
-            utterance.onerror(
-                createSpeechErrorEvent(utterance, 'voice-unavailable'));
-
-            assertEquals(1, speech.getCallCount('cancel'));
-            assertEquals(0, speech.getCallCount('pause'));
-            assertEquals(0, speech.getCallCount('speak'));
-            assertEquals(speech.getVoices()[0], app.getSpeechSynthesisVoice());
-          });
+      assertEquals(2, speech.getCallCount('cancel'));
+      assertEquals(0, speech.getCallCount('pause'));
+      assertEquals(0, speech.getCallCount('speak'));
+      assertEquals(
+          chrome.readingMode.engineErrorStopSource,
+          await metrics.whenCalled('recordSpeechStopSource'));
     });
 
     test('invalid argument cancels and uses default rate', () => {
diff --git a/chrome/test/data/webui/side_panel/read_anything/update_voice_pack_test.ts b/chrome/test/data/webui/side_panel/read_anything/update_voice_pack_test.ts
index dd843c46..fc228265 100644
--- a/chrome/test/data/webui/side_panel/read_anything/update_voice_pack_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/update_voice_pack_test.ts
@@ -371,7 +371,7 @@
         setNaturalVoicesForLang(lang);
         app.updateVoicePackStatus(lang, 'kInstalled');
 
-        const selectedVoice = app.getSpeechSynthesisVoice();
+        const selectedVoice = voicePackController.getCurrentVoice();
         assertTrue(!!selectedVoice);
         assertEquals(lang, selectedVoice.lang);
         assertTrue(selectedVoice.name.includes('Natural'));
@@ -397,7 +397,7 @@
         app.updateVoicePackStatus(installedLang, 'kInstalled');
 
         // The selected voice should stay the same as it was.
-        assertEquals(currentVoice, app.getSpeechSynthesisVoice());
+        assertEquals(currentVoice, voicePackController.getCurrentVoice());
       });
 
   test('with error code marks the status', () => {
diff --git a/chrome/test/data/webui/side_panel/read_anything/voice_pack_controller_test.ts b/chrome/test/data/webui/side_panel/read_anything/voice_pack_controller_test.ts
index 9a86d35..96474d3 100644
--- a/chrome/test/data/webui/side_panel/read_anything/voice_pack_controller_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/voice_pack_controller_test.ts
@@ -20,6 +20,7 @@
   let listener: VoiceLanguageListener;
   let onEnabledLangsChange: boolean;
   let onAvailableVoicesChange: boolean;
+  let onCurrentVoiceChange: boolean;
 
   setup(() => {
     // Clearing the DOM should always be done first.
@@ -32,6 +33,7 @@
     voicePackController = new VoicePackController();
     onEnabledLangsChange = false;
     onAvailableVoicesChange = false;
+    onCurrentVoiceChange = false;
     listener = {
       onEnabledLangsChange() {
         onEnabledLangsChange = true;
@@ -39,6 +41,9 @@
       onAvailableVoicesChange() {
         onAvailableVoicesChange = true;
       },
+      onCurrentVoiceChange() {
+        onCurrentVoiceChange = true;
+      },
     };
     voicePackController.addListener(listener);
   });
@@ -141,6 +146,185 @@
     assertTrue(voicePackController.isLangEnabled('NO'));
   });
 
+  test('setCurrentVoice', () => {
+    const voice = createSpeechSynthesisVoice({lang: 'tr', name: 'Zebra'});
+    voicePackController.setCurrentVoice(voice);
+    assertTrue(onCurrentVoiceChange);
+    assertEquals(voice, voicePackController.getCurrentVoice());
+
+    onCurrentVoiceChange = false;
+    voicePackController.setCurrentVoice(voice);
+    assertFalse(onCurrentVoiceChange);
+    assertEquals(voice, voicePackController.getCurrentVoice());
+  });
+
+  test('setUserPreferredVoice', () => {
+    let sentVoiceName = '';
+    let sentLang = '';
+    chrome.readingMode.onVoiceChange = (name, lang) => {
+      sentVoiceName = name;
+      sentLang = lang;
+    };
+    const voice = createSpeechSynthesisVoice({lang: 'tr', name: 'Lion'});
+
+    voicePackController.setUserPreferredVoice(voice);
+
+    assertTrue(onCurrentVoiceChange);
+    assertEquals(voice, voicePackController.getCurrentVoice());
+    assertEquals(voice.name, sentVoiceName);
+    assertEquals(voice.lang, sentLang);
+  });
+
+  suite('setUserPreferredVoiceFromPrefs', () => {
+    const langForDefaultVoice = 'en';
+    const lang1 = 'zh';
+    const lang2 = 'tr';
+    const langWithNoVoices = 'elvish';
+
+    const defaultVoice = createSpeechSynthesisVoice({
+      lang: langForDefaultVoice,
+      name: 'Google Kristi',
+      default: true,
+    });
+    const firstVoiceWithLang1 =
+        createSpeechSynthesisVoice({lang: lang1, name: 'Google Monkey'});
+    const defaultVoiceWithLang1 = createSpeechSynthesisVoice({
+      lang: lang1,
+      name: 'Google Llama',
+      default: true,
+    });
+    const firstVoiceWithLang2 =
+        createSpeechSynthesisVoice({lang: lang2, name: 'Google Parrot'});
+    const secondVoiceWithLang2 =
+        createSpeechSynthesisVoice({lang: lang2, name: 'Google Panda'});
+    const otherVoice =
+        createSpeechSynthesisVoice({lang: 'it', name: 'Google Elephant'});
+    const voices = [
+      defaultVoice,
+      firstVoiceWithLang1,
+      defaultVoiceWithLang1,
+      otherVoice,
+      firstVoiceWithLang2,
+      secondVoiceWithLang2,
+    ];
+
+    setup(() => {
+      speech.setVoices(voices);
+    });
+
+    test('enables the lang for the chosen voice', () => {
+      chrome.readingMode.getStoredVoice = () => otherVoice.name;
+      voicePackController.setUserPreferredVoiceFromPrefs();
+      assertTrue(voicePackController.isLangEnabled(otherVoice.lang));
+    });
+
+    test('uses the stored voice for this language if there is one', () => {
+      chrome.readingMode.getStoredVoice = () => otherVoice.name;
+
+      voicePackController.setUserPreferredVoiceFromPrefs();
+
+      assertTrue(onCurrentVoiceChange);
+      assertEquals(otherVoice, voicePackController.getCurrentVoice());
+    });
+
+    test('uses the default voice if the stored voice is invalid', () => {
+      chrome.readingMode.getStoredVoice = () => 'Matt';
+      voicePackController.enableLang(langForDefaultVoice);
+
+      voicePackController.setUserPreferredVoiceFromPrefs();
+
+      assertTrue(onCurrentVoiceChange);
+      assertEquals(defaultVoice, voicePackController.getCurrentVoice());
+    });
+
+    suite('when there is no stored voice for this language', () => {
+      setup(() => {
+        chrome.readingMode.getStoredVoice = () => '';
+      });
+
+      test('uses the default voice for this language', () => {
+        voicePackController.enableLang(lang1);
+        voicePackController.setCurrentLanguage(lang1);
+
+        voicePackController.setUserPreferredVoiceFromPrefs();
+
+        assertTrue(onCurrentVoiceChange);
+        assertEquals(
+            defaultVoiceWithLang1, voicePackController.getCurrentVoice());
+      });
+
+      test('uses current voice if there\'s none for this language', () => {
+        voicePackController.setCurrentLanguage(langWithNoVoices);
+        voicePackController.setCurrentVoice(otherVoice);
+        voicePackController.enableLang(otherVoice.lang);
+
+        voicePackController.setUserPreferredVoiceFromPrefs();
+
+        assertTrue(onCurrentVoiceChange);
+        assertEquals(otherVoice, voicePackController.getCurrentVoice());
+      });
+
+      test('uses the device default if there\'s no current voice', () => {
+        voicePackController.setCurrentLanguage(langWithNoVoices);
+        voicePackController.enableLang(langForDefaultVoice);
+        voicePackController.enableLang(otherVoice.lang);
+
+        voicePackController.setUserPreferredVoiceFromPrefs();
+
+        assertTrue(onCurrentVoiceChange);
+        assertEquals(defaultVoice, voicePackController.getCurrentVoice());
+      });
+
+      test(
+          'uses the first voice for this language if there\'s no default',
+          () => {
+            voicePackController.enableLang(lang2);
+            voicePackController.setCurrentLanguage(lang2);
+
+            voicePackController.setUserPreferredVoiceFromPrefs();
+
+            assertTrue(onCurrentVoiceChange);
+            assertEquals(
+                firstVoiceWithLang2, voicePackController.getCurrentVoice());
+          });
+    });
+  });
+
+  test(
+      'updateAutoSelectedVoiceToNaturalVoice with auto selected voice, ' +
+          'switches to a Natural voice',
+      () => {
+        chrome.readingMode.getStoredVoice = () => '';
+        const voice = createSpeechSynthesisVoice({lang: 'ja', name: 'Eagle'});
+        const naturalVoice =
+            createSpeechSynthesisVoice({lang: 'ja', name: 'Horse (Natural)'});
+        voicePackController.setCurrentVoice(voice);
+        voicePackController.setCurrentLanguage(voice.lang);
+        voicePackController.setAvailableVoices([voice, naturalVoice]);
+
+        voicePackController.updateAutoSelectedVoiceToNaturalVoice();
+
+        assertEquals(naturalVoice, voicePackController.getCurrentVoice());
+      });
+
+  test(
+      'updateAutoSelectedVoiceToNaturalVoice with a user selected voice, does' +
+          ' not switch to a Natural voice',
+      () => {
+        const name = 'Emu';
+        chrome.readingMode.getStoredVoice = () => name;
+        const voice = createSpeechSynthesisVoice({lang: 'ja', name: name});
+        const naturalVoice =
+            createSpeechSynthesisVoice({lang: 'ja', name: 'Ostrich (Natural)'});
+        voicePackController.setCurrentVoice(voice);
+        voicePackController.setCurrentLanguage(voice.lang);
+        voicePackController.setAvailableVoices([voice, naturalVoice]);
+
+        voicePackController.updateAutoSelectedVoiceToNaturalVoice();
+
+        assertEquals(voice, voicePackController.getCurrentVoice());
+      });
+
   test('restoreEnabledLanguagesFromPref', () => {
     const lang1 = 'en-gb';
     const lang2 = 'fr';
@@ -336,13 +520,7 @@
       VoiceNotificationManager.getInstance().addListener(notificationListener);
     });
 
-    test('refreshVoicePackStatuses with no languages does nothing', () => {
-      voicePackController.refreshVoicePackStatuses();
-
-      assertArrayEquals([], requestInfoLangs);
-    });
-
-    test('refreshVoicePackStatuses with languages requests info', () => {
+    test('updateUnavailableVoiceToDefaultVoice requests info', () => {
       const lang1 = 'fi';
       const lang2 = 'id';
       const lang3 = 'da';
@@ -353,21 +531,20 @@
       voicePackController.setServerStatus(
           lang3, mojoVoicePackStatusToVoicePackStatusEnum('kNotInstalled'));
 
-      voicePackController.refreshVoicePackStatuses();
+      voicePackController.updateUnavailableVoiceToDefaultVoice();
 
       assertArrayEquals([lang1, lang2, lang3], requestInfoLangs);
     });
 
     test(
-        'refreshVoicePackStatuses with languages waits for engine timeout',
-        () => {
+        'updateUnavailableVoiceToDefaultVoice waits for engine timeout', () => {
           const lang = 'fi';
           voicePackController.setServerStatus(
               lang, mojoVoicePackStatusToVoicePackStatusEnum('kInstalled'));
           const mockTimer = new MockTimer();
           mockTimer.install();
 
-          voicePackController.refreshVoicePackStatuses();
+          voicePackController.updateUnavailableVoiceToDefaultVoice();
           mockTimer.tick(EXTENSION_RESPONSE_TIMEOUT_MS);
           mockTimer.uninstall();
 
@@ -376,6 +553,40 @@
         });
 
     test(
+        'updateUnavailableVoiceToDefaultVoice does nothing when current voice' +
+            ' still available',
+        () => {
+          const voice = createSpeechSynthesisVoice({lang: 'id', name: 'Dog'});
+          voicePackController.enableLang(voice.lang);
+          voicePackController.setCurrentVoice(voice);
+          voicePackController.setAvailableVoices([voice]);
+          onCurrentVoiceChange = false;
+
+          voicePackController.updateUnavailableVoiceToDefaultVoice();
+
+          assertFalse(onCurrentVoiceChange);
+          assertEquals(voice, voicePackController.getCurrentVoice());
+        });
+
+    test(
+        'updateUnavailableVoiceToDefaultVoice gets default voice when current' +
+            ' voice unavailable',
+        () => {
+          const voice = createSpeechSynthesisVoice({lang: 'id', name: 'Cat'});
+          const defaultVoice =
+              createSpeechSynthesisVoice({lang: 'id', name: 'Komodo'});
+          voicePackController.enableLang(voice.lang);
+          voicePackController.setCurrentVoice(voice);
+          voicePackController.setAvailableVoices([defaultVoice]);
+          onCurrentVoiceChange = false;
+
+          voicePackController.updateUnavailableVoiceToDefaultVoice();
+
+          assertTrue(onCurrentVoiceChange);
+          assertEquals(defaultVoice, voicePackController.getCurrentVoice());
+        });
+
+    test(
         'stopWaitingForSpeechExtension stops waiting for engine timeout',
         () => {
           const lang = 'fi';
@@ -392,7 +603,7 @@
           const mockTimer = new MockTimer();
           mockTimer.install();
 
-          voicePackController.refreshVoicePackStatuses();
+          voicePackController.updateUnavailableVoiceToDefaultVoice();
           voicePackController.stopWaitingForSpeechExtension();
           mockTimer.tick(EXTENSION_RESPONSE_TIMEOUT_MS);
           mockTimer.uninstall();
@@ -536,4 +747,42 @@
       assertArrayEquals([], installedLangs);
     });
   });
+
+  test('voiceUnavailable selects default voice', () => {
+    const voice =
+        createSpeechSynthesisVoice({lang: 'en', name: 'Google Giraffe'});
+    speech.setVoices([voice]);
+
+    voicePackController.onVoiceUnavailableError();
+
+    assertEquals(voice, voicePackController.getCurrentVoice());
+  });
+
+  test(
+      'voiceUnavailable default voice is current voice, selects another voice',
+      () => {
+        const voice1 =
+            createSpeechSynthesisVoice({lang: 'en', name: 'Google George'});
+        const voice2 =
+            createSpeechSynthesisVoice({lang: 'en', name: 'Google Connie'});
+        voicePackController.setCurrentVoice(voice1);
+        speech.setVoices([voice1, voice2]);
+
+        voicePackController.onVoiceUnavailableError();
+
+        assertEquals(voice2, voicePackController.getCurrentVoice());
+      });
+
+  test(
+      'voiceUnavailable continues to select default voice if no voices ' +
+          'available in language',
+      () => {
+        const voice =
+            createSpeechSynthesisVoice({lang: 'en', name: 'Google Penguin'});
+        speech.setVoices([voice]);
+
+        voicePackController.onVoiceUnavailableError();
+
+        assertEquals(voice, voicePackController.getCurrentVoice());
+      });
 });
diff --git a/chrome/test/data/webui/side_panel/read_anything/voice_selection_test.ts b/chrome/test/data/webui/side_panel/read_anything/voice_selection_test.ts
deleted file mode 100644
index 6750597..0000000
--- a/chrome/test/data/webui/side_panel/read_anything/voice_selection_test.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import {BrowserProxy, SpeechBrowserProxyImpl, VoicePackController} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
-import type {AppElement} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
-import {assertEquals} from 'chrome-untrusted://webui-test/chai_assert.js';
-
-import {createApp, createSpeechSynthesisVoice, setVoices} from './common.js';
-import {FakeReadingMode} from './fake_reading_mode.js';
-import {TestColorUpdaterBrowserProxy} from './test_color_updater_browser_proxy.js';
-import {TestSpeechBrowserProxy} from './test_speech_browser_proxy.js';
-
-suite('Automatic voice selection', () => {
-  const defaultLang = 'en-us';
-  const pageLang = 'en';
-  const differentLang = 'zh';
-
-  const firstVoiceWithLang = createSpeechSynthesisVoice({
-    lang: defaultLang,
-    name: 'Google Kristi',
-  });
-  const secondVoiceWithLang =
-      createSpeechSynthesisVoice({lang: defaultLang, name: 'Google Lauren'});
-  const defaultVoiceForDifferentLang = createSpeechSynthesisVoice({
-    lang: differentLang,
-    name: 'Google Eitan',
-    default: true,
-  });
-  const voices = [
-    firstVoiceWithLang,
-    secondVoiceWithLang,
-    defaultVoiceForDifferentLang,
-  ];
-
-  let app: AppElement;
-  let speech: TestSpeechBrowserProxy;
-
-  function addNaturalVoices() {
-    setVoices(
-        app, speech,
-        voices.concat(
-            createSpeechSynthesisVoice(
-                {lang: defaultLang, name: 'Google Wall-e (Natural)'}),
-            createSpeechSynthesisVoice(
-                {lang: defaultLang, name: 'Google Andy (Natural)'}),
-            ));
-  }
-
-  setup(async () => {
-    // Clearing the DOM should always be done first.
-    document.body.innerHTML = window.trustedTypes!.emptyHTML;
-    BrowserProxy.setInstance(new TestColorUpdaterBrowserProxy());
-    const readingMode = new FakeReadingMode();
-    chrome.readingMode = readingMode as unknown as typeof chrome.readingMode;
-    chrome.readingMode.baseLanguageForSpeech = pageLang;
-    chrome.readingMode.isReadAloudEnabled = true;
-    speech = new TestSpeechBrowserProxy();
-    SpeechBrowserProxyImpl.setInstance(speech);
-    VoicePackController.setInstance(new VoicePackController());
-
-    app = await createApp();
-    setVoices(app, speech, voices);
-
-    // Initializes some class variables needed for voice selection logic
-    app.restoreEnabledLanguagesFromPref();
-  });
-
-  test(
-      'with no user selected voices, switches to a Natural voice if it later ' +
-          'becomes available',
-      () => {
-        chrome.readingMode.getStoredVoice = () => '';
-        app.selectPreferredVoice();
-        assertEquals(firstVoiceWithLang, app.getSpeechSynthesisVoice());
-
-        addNaturalVoices();
-
-        assertEquals(
-            'Google Wall-e (Natural)', app.getSpeechSynthesisVoice()?.name);
-      });
-
-  test(
-      'with a user selected voices, does not switch to a Natural voice if it ' +
-          'later becomes available',
-      () => {
-        chrome.readingMode.getStoredVoice = () => secondVoiceWithLang.name;
-        app.selectPreferredVoice();
-        assertEquals(secondVoiceWithLang, app.getSpeechSynthesisVoice());
-
-        addNaturalVoices();
-
-        assertEquals(secondVoiceWithLang, app.getSpeechSynthesisVoice());
-      });
-});
diff --git a/chrome/test/data/webui/side_panel/read_anything/word_boundaries_speech_test.ts b/chrome/test/data/webui/side_panel/read_anything/word_boundaries_speech_test.ts
index 0100e3c..bd94ce2 100644
--- a/chrome/test/data/webui/side_panel/read_anything/word_boundaries_speech_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/word_boundaries_speech_test.ts
@@ -14,6 +14,7 @@
   let app: AppElement;
   let wordBoundaries: WordBoundaries;
   let speechController: SpeechController;
+  let voicePackController: VoicePackController;
 
   // root htmlTag='#document' id=1
   // ++link htmlTag='a' url='http://www.google.com' id=2
@@ -65,7 +66,8 @@
     chrome.readingMode.onConnected = () => {};
     const speech = new TestSpeechBrowserProxy();
     SpeechBrowserProxyImpl.setInstance(speech);
-    VoicePackController.setInstance(new VoicePackController());
+    voicePackController = new VoicePackController();
+    VoicePackController.setInstance(voicePackController);
     speechController = new SpeechController();
     SpeechController.setInstance(speechController);
 
@@ -106,7 +108,7 @@
 
       const state: WordBoundaryState = wordBoundaries.state;
       assertTrue(wordBoundaries.hasBoundaries());
-      assertTrue(!!app.getSpeechSynthesisVoice());
+      assertTrue(!!voicePackController.getCurrentVoice());
       assertEquals(10, state.speechUtteranceStartIndex);
       assertEquals(0, state.previouslySpokenIndex);
       assertEquals(5, state.speechUtteranceLength);
diff --git a/chrome/test/data/webui/side_panel/read_anything/word_highlighting_test.ts b/chrome/test/data/webui/side_panel/read_anything/word_highlighting_test.ts
index 4148bcd..c20466c 100644
--- a/chrome/test/data/webui/side_panel/read_anything/word_highlighting_test.ts
+++ b/chrome/test/data/webui/side_panel/read_anything/word_highlighting_test.ts
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import type {AppElement} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
-import {PauseActionSource, SpeechBrowserProxyImpl, SpeechController, ToolbarEvent, WordBoundaries} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
+import {PauseActionSource, ReadAloudHighlighter, SpeechBrowserProxyImpl, SpeechController, ToolbarEvent, VoicePackController, WordBoundaries} from 'chrome-untrusted://read-anything-side-panel.top-chrome/read_anything.js';
 import {assertEquals, assertFalse, assertTrue} from 'chrome-untrusted://webui-test/chai_assert.js';
 
 import {createApp, createSpeechSynthesisVoice, emitEvent, playFromSelectionWithMockTimer, setSimpleAxTreeWithText} from './common.js';
@@ -67,9 +67,12 @@
     SpeechBrowserProxyImpl.setInstance(speech);
     speechController = new SpeechController();
     SpeechController.setInstance(speechController);
+    VoicePackController.setInstance(new VoicePackController());
+    wordBoundaries = new WordBoundaries();
+    WordBoundaries.setInstance(wordBoundaries);
+    ReadAloudHighlighter.setInstance(new ReadAloudHighlighter());
 
     app = await createApp();
-    wordBoundaries = WordBoundaries.getInstance();
     chrome.readingMode.setContentForTesting(axTree, [2, 4]);
     chrome.readingMode.onSpeechRateChange(1);
   });
diff --git a/chromecast/browser/cast_content_browser_client.cc b/chromecast/browser/cast_content_browser_client.cc
index 67198ae..c915f3c 100644
--- a/chromecast/browser/cast_content_browser_client.cc
+++ b/chromecast/browser/cast_content_browser_client.cc
@@ -509,16 +509,6 @@
   prefs->viewport_style = blink::mojom::ViewportStyle::kTelevision;
 #endif  // BUILDFLAG(IS_ANDROID)
 
-  // Disable WebSQL databases by default.
-  prefs->databases_enabled = false;
-  if (web_contents) {
-    chromecast::CastWebContents* cast_web_contents =
-        chromecast::CastWebContents::FromWebContents(web_contents);
-    if (cast_web_contents && cast_web_contents->is_websql_enabled()) {
-      prefs->databases_enabled = true;
-    }
-  }
-
   prefs->preferred_color_scheme =
       static_cast<blink::mojom::PreferredColorScheme>(
           CastBrowserProcess::GetInstance()->pref_service()->GetInteger(
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM
index 8fc4e55..1fe9cf60 100644
--- a/chromeos/CHROMEOS_LKGM
+++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@
-16276.0.0-1068704
\ No newline at end of file
+16277.0.0-1068716
\ No newline at end of file
diff --git a/chromeos/ash/components/kcer/kcer_token_impl.cc b/chromeos/ash/components/kcer/kcer_token_impl.cc
index f4deca5..94d4f69 100644
--- a/chromeos/ash/components/kcer/kcer_token_impl.cc
+++ b/chromeos/ash/components/kcer/kcer_token_impl.cc
@@ -301,52 +301,6 @@
   return DigestWithPrefix(std::vector<uint8_t>(digest, digest + digest_len));
 }
 
-// The EC signature returned by Chaps is a concatenation of two numbers r and s
-// (see PKCS#11 v2.40: 2.3.1 EC Signatures). Kcer needs to return it as a DER
-// encoding of the following ASN.1 notations:
-// Ecdsa-Sig-Value ::= SEQUENCE {
-//     r       INTEGER,
-//     s       INTEGER
-// }
-// (according to the RFC 8422, Section 5.4).
-// This function reencodes the signature.
-base::expected<std::vector<uint8_t>, Error> ReencodeEcSignature(
-    base::span<const uint8_t> signature) {
-  if (signature.size() % 2 != 0) {
-    return base::unexpected(Error::kFailedToSignBadSignatureLength);
-  }
-  size_t order_size_bytes = signature.size() / 2;
-  base::span<const uint8_t> r_bytes = signature.first(order_size_bytes);
-  base::span<const uint8_t> s_bytes = signature.subspan(order_size_bytes);
-
-  // Convert the RAW ECDSA signature to a DER-encoded ECDSA-Sig-Value.
-  bssl::UniquePtr<ECDSA_SIG> sig(ECDSA_SIG_new());
-  if (!sig || !BN_bin2bn(r_bytes.data(), r_bytes.size(), sig->r) ||
-      !BN_bin2bn(s_bytes.data(), s_bytes.size(), sig->s)) {
-    return base::unexpected(Error::kFailedToDerEncode);
-  }
-
-  std::vector<uint8_t> result_signature;
-
-  {
-    const int len = i2d_ECDSA_SIG(sig.get(), nullptr);
-    if (len <= 0) {
-      return base::unexpected(Error::kFailedToSignBadSignatureLength);
-    }
-    result_signature.resize(len);
-  }
-
-  {
-    uint8_t* ptr = result_signature.data();
-    const int len = i2d_ECDSA_SIG(sig.get(), &ptr);
-    if (len <= 0) {
-      return base::unexpected(Error::kFailedToDerEncode);
-    }
-  }
-
-  return result_signature;
-}
-
 std::vector<uint8_t> GetPssSignParams(SigningScheme kcer_signing_scheme) {
   chromeos::PKCS11_CK_RSA_PKCS_PSS_PARAMS pss_params;
 
@@ -1736,7 +1690,7 @@
   uint64_t mechanism = SigningSchemeToPkcs11Mechanism(task.signing_scheme);
   if (mechanism == chromeos::PKCS11_CKM_ECDSA) {
     base::expected<std::vector<uint8_t>, Error> reencoded_signature =
-        ReencodeEcSignature(std::move(signature));
+        ReencodeEcSignatureAsAsn1(std::move(signature));
     if (!reencoded_signature.has_value()) {
       return std::move(task.callback)
           .Run(base::unexpected(reencoded_signature.error()));
diff --git a/chromeos/ash/components/kcer/kcer_utils.cc b/chromeos/ash/components/kcer/kcer_utils.cc
index 7c9efe3..b76494f 100644
--- a/chromeos/ash/components/kcer/kcer_utils.cc
+++ b/chromeos/ash/components/kcer/kcer_utils.cc
@@ -44,4 +44,41 @@
   return result;
 }
 
+base::expected<std::vector<uint8_t>, Error> ReencodeEcSignatureAsAsn1(
+    base::span<const uint8_t> signature) {
+  if (signature.size() % 2 != 0) {
+    return base::unexpected(Error::kFailedToSignBadSignatureLength);
+  }
+  size_t order_size_bytes = signature.size() / 2;
+  base::span<const uint8_t> r_bytes = signature.first(order_size_bytes);
+  base::span<const uint8_t> s_bytes = signature.subspan(order_size_bytes);
+
+  // Convert the RAW ECDSA signature to a DER-encoded ECDSA-Sig-Value.
+  bssl::UniquePtr<ECDSA_SIG> sig(ECDSA_SIG_new());
+  if (!sig || !BN_bin2bn(r_bytes.data(), r_bytes.size(), sig->r) ||
+      !BN_bin2bn(s_bytes.data(), s_bytes.size(), sig->s)) {
+    return base::unexpected(Error::kFailedToDerEncode);
+  }
+
+  std::vector<uint8_t> result_signature;
+
+  {
+    const int len = i2d_ECDSA_SIG(sig.get(), nullptr);
+    if (len <= 0) {
+      return base::unexpected(Error::kFailedToSignBadSignatureLength);
+    }
+    result_signature.resize(len);
+  }
+
+  {
+    uint8_t* ptr = result_signature.data();
+    const int len = i2d_ECDSA_SIG(sig.get(), &ptr);
+    if (len <= 0) {
+      return base::unexpected(Error::kFailedToDerEncode);
+    }
+  }
+
+  return result_signature;
+}
+
 }  // namespace kcer
diff --git a/chromeos/ash/components/kcer/kcer_utils.h b/chromeos/ash/components/kcer/kcer_utils.h
index 9f3e7852..cede43a 100644
--- a/chromeos/ash/components/kcer/kcer_utils.h
+++ b/chromeos/ash/components/kcer/kcer_utils.h
@@ -14,6 +14,19 @@
 std::vector<SigningScheme> GetSupportedSigningSchemes(bool supports_pss,
                                                       KeyType key_type);
 
+// The EC signature returned by Chaps is a concatenation of two numbers r and s
+// (see PKCS#11 v2.40: 2.3.1 EC Signatures). Kcer needs to return it as a DER
+// encoding of the following ASN.1 notations:
+// Ecdsa-Sig-Value ::= SEQUENCE {
+//     r       INTEGER,
+//     s       INTEGER
+// }
+// (according to the RFC 8422, Section 5.4).
+// This function reencodes the signature.
+COMPONENT_EXPORT(KCER)
+base::expected<std::vector<uint8_t>, Error> ReencodeEcSignatureAsAsn1(
+    base::span<const uint8_t> signature);
+
 }  // namespace kcer
 
 #endif  // CHROMEOS_ASH_COMPONENTS_KCER_KCER_UTILS_H_
diff --git a/chromeos/ash/experiences/arc/compat_mode/arc_splash_screen_dialog_view.cc b/chromeos/ash/experiences/arc/compat_mode/arc_splash_screen_dialog_view.cc
index b83053a0..7de5c38 100644
--- a/chromeos/ash/experiences/arc/compat_mode/arc_splash_screen_dialog_view.cc
+++ b/chromeos/ash/experiences/arc/compat_mode/arc_splash_screen_dialog_view.cc
@@ -160,7 +160,7 @@
     bool is_for_unresizable)
     : anchor_(anchor), close_callback_(std::move(close_callback)) {
   // Setup delegate.
-  set_background_color(cros_tokens::kCrosSysDialogContainer);
+  SetBackgroundColor(cros_tokens::kCrosSysDialogContainer);
   SetArrow(views::BubbleBorder::Arrow::BOTTOM_CENTER);
   SetButtons(static_cast<int>(ui::mojom::DialogButton::kNone));
   set_parent_window(parent);
diff --git a/chromeos/ash/experiences/arc/compat_mode/resize_toggle_menu.cc b/chromeos/ash/experiences/arc/compat_mode/resize_toggle_menu.cc
index cb0bb99..aca50c2 100644
--- a/chromeos/ash/experiences/arc/compat_mode/resize_toggle_menu.cc
+++ b/chromeos/ash/experiences/arc/compat_mode/resize_toggle_menu.cc
@@ -289,7 +289,7 @@
   delegate_view->SetAccessibleWindowRole(ax::mojom::Role::kMenu);
   // Clear root view's background color. We use the color in
   // `background_view`.
-  delegate_view->set_background_color(SK_ColorTRANSPARENT);
+  delegate_view->SetBackgroundColor(SK_ColorTRANSPARENT);
 
   // Setup view.
   delegate_view->SetUseDefaultFillLayout(true);
diff --git a/chromeos/profiles/arm.afdo.newest.txt b/chromeos/profiles/arm.afdo.newest.txt
index 97350fff..bd961538 100644
--- a/chromeos/profiles/arm.afdo.newest.txt
+++ b/chromeos/profiles/arm.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-arm-none-138-7137.0-1746411594-benchmark-138.0.7161.0-r1-redacted.afdo.xz
+chromeos-chrome-arm-none-138-7137.0-1746411594-benchmark-138.0.7166.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/atom.afdo.newest.txt b/chromeos/profiles/atom.afdo.newest.txt
index 4199d11..d7ce078 100644
--- a/chromeos/profiles/atom.afdo.newest.txt
+++ b/chromeos/profiles/atom.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-atom-137-7103.40-1745806155-benchmark-138.0.7155.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-atom-138-7137.0-1746412216-benchmark-138.0.7166.0-r1-redacted.afdo.xz
diff --git a/chromeos/profiles/bigcore.afdo.newest.txt b/chromeos/profiles/bigcore.afdo.newest.txt
index 4ab69b6..e78d7ba 100644
--- a/chromeos/profiles/bigcore.afdo.newest.txt
+++ b/chromeos/profiles/bigcore.afdo.newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-bigcore-138-7137.0-1746414666-benchmark-138.0.7164.0-r1-redacted.afdo.xz
+chromeos-chrome-amd64-bigcore-138-7137.0-1746414666-benchmark-138.0.7166.0-r1-redacted.afdo.xz
diff --git a/clank b/clank
index 4326f34..72050536 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 4326f348545fb5ef5cbb59389e7e3a903d2320eb
+Subproject commit 72050536a13f233b345a2f22647ff5c1a289b42f
diff --git a/components/BUILD.gn b/components/BUILD.gn
index 5c59002..0bb5e83 100644
--- a/components/BUILD.gn
+++ b/components/BUILD.gn
@@ -730,7 +730,7 @@
     deps += [ "//components/guest_view/browser:unit_tests" ]
   }
 
-  if (!is_android && !is_ios && (!is_fuchsia || !optimize_for_size)) {
+  if (!is_android && !is_ios && !is_fuchsia) {
     deps += [ "//components/autofill_ai/core/browser:unit_tests" ]
   }
 
diff --git a/components/android_autofill/browser/android_autofill_features.cc b/components/android_autofill/browser/android_autofill_features.cc
index f1afcd1..018d922 100644
--- a/components/android_autofill/browser/android_autofill_features.cc
+++ b/components/android_autofill/browser/android_autofill_features.cc
@@ -23,7 +23,8 @@
 const base::Feature* const kFeaturesExposedToJava[] = {
     &kAndroidAutofillDeprecateAccessibilityApi,
     &kAutofillVirtualViewStructureAndroidInCct,
-    &kAndroidAutofillLazyFrameworkWrapper};
+    &kAndroidAutofillLazyFrameworkWrapper,
+    &kAutofillVirtualViewStructureAndroidPasskeyLongPress};
 
 }  // namespace
 
diff --git a/components/android_autofill/browser/android_autofill_provider.cc b/components/android_autofill/browser/android_autofill_provider.cc
index 410d556..fb3d4ab 100644
--- a/components/android_autofill/browser/android_autofill_provider.cc
+++ b/components/android_autofill/browser/android_autofill_provider.cc
@@ -388,6 +388,26 @@
           : PrefillRequestState::kRequestSentStructureNotProvided);
 }
 
+bool AndroidAutofillProvider::HasPasskeyRequest() {
+  if (!manager_ || !form_ ||
+      !GetCredManDelegate(GetRenderFrameHost(manager_.get()))) {
+    return false;
+  }
+  const FormFieldData* field =
+      form_->form().FindFieldByGlobalId(current_field_.id);
+  return field && AllowCredManOnField(*field);
+}
+
+void AndroidAutofillProvider::OnTriggerPasskeyRequest() {
+  if (manager_) {
+    if (content::RenderFrameHost* rfh = GetRenderFrameHost(manager_.get())) {
+      if (WebAuthnCredManDelegate* delegate = GetCredManDelegate(rfh)) {
+        delegate->TriggerCredManUi(RequestPasswords(false));
+      }
+    }
+  }
+}
+
 void AndroidAutofillProvider::OnTextFieldValueChanged(
     AndroidAutofillManager* manager,
     const FormData& form,
diff --git a/components/android_autofill/browser/android_autofill_provider.h b/components/android_autofill/browser/android_autofill_provider.h
index f0309a9..9dffe6d 100644
--- a/components/android_autofill/browser/android_autofill_provider.h
+++ b/components/android_autofill/browser/android_autofill_provider.h
@@ -127,6 +127,8 @@
                          const gfx::RectF& bounds) override;
   void OnShowBottomSheetResult(bool is_shown,
                                bool provided_autofill_structure) override;
+  bool HasPasskeyRequest() override;
+  void OnTriggerPasskeyRequest() override;
 
   // content::WebContentsObserver:
   void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
diff --git a/components/android_autofill/browser/android_autofill_provider_bridge.h b/components/android_autofill/browser/android_autofill_provider_bridge.h
index 65ac651..e270931 100644
--- a/components/android_autofill/browser/android_autofill_provider_bridge.h
+++ b/components/android_autofill/browser/android_autofill_provider_bridge.h
@@ -41,6 +41,14 @@
                                    const gfx::RectF& bounds) = 0;
     virtual void OnShowBottomSheetResult(bool is_shown,
                                          bool provided_autofill_structure) = 0;
+
+    // Asks whether passkeys options are available that should be triggered when
+    // `OnTriggerPasskeyRequest` is called.
+    virtual bool HasPasskeyRequest() = 0;
+
+    // The user explicitly requested passkeys to be shown. Even if no passkeys
+    // are available, call the framework to allow using fallback entry points.
+    virtual void OnTriggerPasskeyRequest() = 0;
   };
 
   // A helper struct to reference a field in a form.
diff --git a/components/android_autofill/browser/android_autofill_provider_bridge_impl.cc b/components/android_autofill/browser/android_autofill_provider_bridge_impl.cc
index f2252d2..9c3949c 100644
--- a/components/android_autofill/browser/android_autofill_provider_bridge_impl.cc
+++ b/components/android_autofill/browser/android_autofill_provider_bridge_impl.cc
@@ -234,6 +234,10 @@
   java_ref_.reset();
 }
 
+jboolean AndroidAutofillProviderBridgeImpl::HasPasskeyRequest(JNIEnv* env) {
+  return delegate_->HasPasskeyRequest();
+}
+
 void AndroidAutofillProviderBridgeImpl::OnAutofillAvailable(JNIEnv* env) {
   delegate_->OnAutofillAvailable();
 }
@@ -260,4 +264,9 @@
     jboolean provided_autofill_structure) {
   delegate_->OnShowBottomSheetResult(is_shown, provided_autofill_structure);
 }
+
+void AndroidAutofillProviderBridgeImpl::OnTriggerPasskeyRequest(JNIEnv* env) {
+  delegate_->OnTriggerPasskeyRequest();
+}
+
 }  // namespace autofill
diff --git a/components/android_autofill/browser/android_autofill_provider_bridge_impl.h b/components/android_autofill/browser/android_autofill_provider_bridge_impl.h
index 8333e98..7829fb6 100644
--- a/components/android_autofill/browser/android_autofill_provider_bridge_impl.h
+++ b/components/android_autofill/browser/android_autofill_provider_bridge_impl.h
@@ -52,6 +52,9 @@
   // about to be destroyed.
   void DetachFromJavaAutofillProvider(JNIEnv* env);
 
+  // Asks the `Delegate` whether passkeys options are available.
+  jboolean HasPasskeyRequest(JNIEnv* env);
+
   // Informs the `Delegate` that the linked form should be sent to the renderer
   // for filling. Invoked when the user has accepted Autofill.
   void OnAutofillAvailable(JNIEnv* env);
@@ -78,6 +81,9 @@
                                jboolean is_shown,
                                jboolean provided_autofill_structure);
 
+  // Informs the `Delegate` that the user explicitly requested passkeys options.
+  void OnTriggerPasskeyRequest(JNIEnv* env);
+
  private:
   // The delegate of the bridge.
   raw_ref<Delegate> delegate_;
diff --git a/components/android_autofill/browser/android_autofill_provider_unittest.cc b/components/android_autofill/browser/android_autofill_provider_unittest.cc
index 70fc272f..6c34acf 100644
--- a/components/android_autofill/browser/android_autofill_provider_unittest.cc
+++ b/components/android_autofill/browser/android_autofill_provider_unittest.cc
@@ -1002,6 +1002,22 @@
   FocusFormField(webauthn_email_field());
 }
 
+TEST_F(AndroidAutofillProviderWithCredManTest, OfferLongPressOption) {
+  FocusFormField(webauthn_email_field());
+  EXPECT_TRUE(provider_bridge_delegate().HasPasskeyRequest());
+}
+
+TEST_F(AndroidAutofillProviderWithCredManTest,
+       OfferNoLongPressOptionWithoutAnnotation) {
+  FocusFormField(non_webauthn_password_field());
+  EXPECT_FALSE(provider_bridge_delegate().HasPasskeyRequest());
+}
+
+TEST_F(AndroidAutofillProviderWithCredManTest,
+       OfferNoLongPressOptionWithoutField) {
+  EXPECT_FALSE(provider_bridge_delegate().HasPasskeyRequest());
+}
+
 TEST_F(AndroidAutofillProviderWithCredManTest, NotifyFocusOnCredManError) {
   EXPECT_CALL(cred_man_delegate(), TriggerCredManUi);
   base::RepeatingCallback<void(bool)> completed_callback;
diff --git a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AndroidAutofillFeatures.java b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AndroidAutofillFeatures.java
index 179e3f4..ea15267 100644
--- a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AndroidAutofillFeatures.java
+++ b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AndroidAutofillFeatures.java
@@ -23,9 +23,11 @@
             "AndroidAutofillDeprecateAccessibilityApi";
     public static final String ANDROID_AUTOFILL_LAZY_FRAMEWORK_WRAPPER_NAME =
             "AndroidAutofillLazyFrameworkWrapper";
-
     public static final String ANDROID_AUTOFILL_VIRTUAL_VIEW_STRUCTURE_ANDROID_IN_CCT_NAME =
             "AutofillVirtualViewStructureAndroidInCct";
+    public static final String ANDROID_AUTOFILL_VIRTUAL_VIEW_STRUCTURE_PASSKEY_LONG_PRESS_NAME =
+            "AutofillVirtualViewStructureAndroidPasskeyLongPress";
+
     public static final AndroidAutofillFeatures ANDROID_AUTOFILL_DEPRECATE_ACCESSIBILITY_API =
             new AndroidAutofillFeatures(0, ANDROID_AUTOFILL_DEPRECATE_ACCESSIBILITY_API_NAME);
     public static final AndroidAutofillFeatures
@@ -34,6 +36,11 @@
                             1, ANDROID_AUTOFILL_VIRTUAL_VIEW_STRUCTURE_ANDROID_IN_CCT_NAME);
     public static final AndroidAutofillFeatures ANDROID_AUTOFILL_LAZY_FRAMEWORK_WRAPPER =
             new AndroidAutofillFeatures(2, ANDROID_AUTOFILL_LAZY_FRAMEWORK_WRAPPER_NAME);
+    public static final AndroidAutofillFeatures
+            ANDROID_AUTOFILL_VIRTUAL_VIEW_STRUCTURE_PASSKEY_LONG_PRESS =
+                    new AndroidAutofillFeatures(
+                            3, ANDROID_AUTOFILL_VIRTUAL_VIEW_STRUCTURE_PASSKEY_LONG_PRESS_NAME);
+
     private final int mOrdinal;
 
     private AndroidAutofillFeatures(int ordinal, String name) {
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 2cc1a4e6..74a1ed83 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
@@ -224,6 +224,20 @@
                 && !getAutofillManagerWrapper().isAutofillInputUiShowing();
     }
 
+    public boolean shouldOfferPasskeyEntry() {
+        if (!AndroidAutofillFeatures.ANDROID_AUTOFILL_VIRTUAL_VIEW_STRUCTURE_PASSKEY_LONG_PRESS
+                .isEnabled()) {
+            return false;
+        }
+        return AutofillProviderJni.get().hasPasskeyRequest(mNativeAutofillProvider);
+    }
+
+    public void triggerPasskeyRequest() {
+        if (mNativeAutofillProvider != 0) {
+            AutofillProviderJni.get().onTriggerPasskeyRequest(mNativeAutofillProvider);
+        }
+    }
+
     public void queryAutofillSuggestion() {
         if (shouldQueryAutofillSuggestion()) {
             FocusField focusField = mRequest.getFocusField();
@@ -841,6 +855,8 @@
 
         void detachFromJavaAutofillProvider(long nativeAndroidAutofillProviderBridgeImpl);
 
+        boolean hasPasskeyRequest(long nativeAndroidAutofillProviderBridgeImpl);
+
         void onAutofillAvailable(long nativeAndroidAutofillProviderBridgeImpl);
 
         void onAcceptDataListSuggestion(
@@ -859,5 +875,7 @@
                 long nativeAndroidAutofillProviderBridgeImpl,
                 boolean isShown,
                 boolean providedAutofillStructure);
+
+        void onTriggerPasskeyRequest(long nativeAndroidAutofillProviderBridgeImpl);
     }
 }
diff --git a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillSelectionMenuItemHelper.java b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillSelectionMenuItemHelper.java
index 6d9063ea..f273e982 100644
--- a/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillSelectionMenuItemHelper.java
+++ b/components/android_autofill/browser/java/src/org/chromium/components/autofill/AutofillSelectionMenuItemHelper.java
@@ -39,17 +39,28 @@
 
     public List<SelectionMenuItem> getAdditionalItems() {
         List<SelectionMenuItem> autofillItems = new ArrayList<>();
-        if (mAutofillMenuItemTitle == 0 || !mAutofillProvider.shouldQueryAutofillSuggestion()) {
-            return autofillItems;
+        if (mAutofillProvider.shouldOfferPasskeyEntry()) {
+            autofillItems.add(
+                    new SelectionMenuItem.Builder("Use Passkey")
+                            .setId(Menu.NONE)
+                            .setOrderInCategory(Menu.FIRST)
+                            .setShowAsActionFlags(
+                                    MenuItem.SHOW_AS_ACTION_ALWAYS
+                                            | MenuItem.SHOW_AS_ACTION_WITH_TEXT)
+                            .setClickListener(v -> mAutofillProvider.triggerPasskeyRequest())
+                            .build());
         }
-        autofillItems.add(
-                new SelectionMenuItem.Builder(mAutofillMenuItemTitle)
-                        .setId(android.R.id.autofill)
-                        .setOrderInCategory(Menu.CATEGORY_SECONDARY)
-                        .setShowAsActionFlags(
-                                MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT)
-                        .setClickListener(v -> mAutofillProvider.queryAutofillSuggestion())
-                        .build());
+        if (mAutofillMenuItemTitle != 0 && mAutofillProvider.shouldQueryAutofillSuggestion()) {
+            autofillItems.add(
+                    new SelectionMenuItem.Builder(mAutofillMenuItemTitle)
+                            .setId(android.R.id.autofill)
+                            .setOrderInCategory(Menu.CATEGORY_SECONDARY)
+                            .setShowAsActionFlags(
+                                    MenuItem.SHOW_AS_ACTION_NEVER
+                                            | MenuItem.SHOW_AS_ACTION_WITH_TEXT)
+                            .setClickListener(v -> mAutofillProvider.queryAutofillSuggestion())
+                            .build());
+        }
         return autofillItems;
     }
 }
diff --git a/components/android_autofill/browser/junit/src/org/chromium/components/autofill/AutofillProviderTest.java b/components/android_autofill/browser/junit/src/org/chromium/components/autofill/AutofillProviderTest.java
index e2b4c1d0..665e2836 100644
--- a/components/android_autofill/browser/junit/src/org/chromium/components/autofill/AutofillProviderTest.java
+++ b/components/android_autofill/browser/junit/src/org/chromium/components/autofill/AutofillProviderTest.java
@@ -55,7 +55,8 @@
 @RunWith(BaseRobolectricTestRunner.class)
 @Config(manifest = Config.NONE)
 @Features.EnableFeatures({
-    AndroidAutofillFeatures.ANDROID_AUTOFILL_VIRTUAL_VIEW_STRUCTURE_ANDROID_IN_CCT_NAME
+    AndroidAutofillFeatures.ANDROID_AUTOFILL_VIRTUAL_VIEW_STRUCTURE_ANDROID_IN_CCT_NAME,
+    AndroidAutofillFeatures.ANDROID_AUTOFILL_VIRTUAL_VIEW_STRUCTURE_PASSKEY_LONG_PRESS_NAME
 })
 public class AutofillProviderTest {
     private static final float EXPECTED_DIP_SCALE = 2;
@@ -391,6 +392,18 @@
         verify(mNativeMock, never()).onShowBottomSheetResult(anyLong(), anyBoolean(), anyBoolean());
     }
 
+    @Test
+    public void testCallsNativeToTriggerPasskeys() {
+        mAutofillProvider.triggerPasskeyRequest();
+        verify(mNativeMock).onTriggerPasskeyRequest(eq(mMockedNativeAndroidAutofillProvider));
+    }
+
+    @Test
+    public void testCallsNativeToProvidePasskeyAvailability() {
+        mAutofillProvider.shouldOfferPasskeyEntry();
+        verify(mNativeMock).hasPasskeyRequest(eq(mMockedNativeAndroidAutofillProvider));
+    }
+
     FormData setupPrefillRequest(int sessionId) {
         FormFieldDataBuilder field1Builder = new FormFieldDataBuilder();
         field1Builder.mBounds =
diff --git a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html b/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html
index 6b8c6947..6cda11f 100644
--- a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html
+++ b/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.html
@@ -326,6 +326,7 @@
     <span id="reset-cache-fake-button" class="fake-button" style="display: none">Reset Cache</span>
     <span id="download-fake-button" class="fake-button">Download Log</span>
     <span id="download-submitted-forms-json-data-fake-button" class="fake-button" style="display: none">Download submitted forms JSON data</span>
+    <span id="set-dom-node-id" class="fake-button" style="display: none">Set DOM node id attribute</span>
     <div id="settings-checkbox-placeholder"></div>
     <span id="reset-upm-eviction-fake-button" class="fake-button" style="display: none">Reset UPM eviction</span>
     <div id="tab-links" style="display:none">Tabs: </div>
diff --git a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.ts b/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.ts
index e374833..8971155 100644
--- a/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.ts
+++ b/components/autofill/core/browser/autofill_and_password_manager_internals/autofill_and_password_manager_internals.ts
@@ -262,6 +262,7 @@
   setUpSettingCheckboxe();
   setUpMarker();
   setUpSubmittedFormsJSONDataDownload();
+  setUpButtonForDomNodeIdCapture();
   setUpDownload('autofill');
   if (autofillAiEnabled) {
     addAutofillTabs();
@@ -526,6 +527,16 @@
   // </if>
 }
 
+function setUpButtonForDomNodeIdCapture() {
+  // <if expr="not is_android and not is_ios" >
+  const button = document.getElementById('set-dom-node-id')!;
+  button.style.display = 'inline';
+  button.addEventListener('click', () => {
+    chrome.send('setDomNodeId');
+  });
+  // </if>
+}
+
 interface CheckboxInfo {
   id: string;
   label?: string;
diff --git a/components/autofill/core/browser/data_model/addresses/autofill_profile.cc b/components/autofill/core/browser/data_model/addresses/autofill_profile.cc
index 6acdcd8..d70fb44 100644
--- a/components/autofill/core/browser/data_model/addresses/autofill_profile.cc
+++ b/components/autofill/core/browser/data_model/addresses/autofill_profile.cc
@@ -1254,14 +1254,6 @@
   return account_profile;
 }
 
-AutofillProfile AutofillProfile::DowngradeToAccountProfile() const {
-  CHECK(record_type() == RecordType::kAccountHome ||
-        record_type() == RecordType::kAccountWork);
-  AutofillProfile account_profile = *this;
-  account_profile.record_type_ = RecordType::kAccount;
-  return account_profile;
-}
-
 FieldTypeSet AutofillProfile::FindInaccessibleProfileValues() const {
   FieldTypeSet inaccessible_fields;
   const std::string stored_country =
diff --git a/components/autofill/core/browser/data_model/addresses/autofill_profile.h b/components/autofill/core/browser/data_model/addresses/autofill_profile.h
index ec5289b..e621a15 100644
--- a/components/autofill/core/browser/data_model/addresses/autofill_profile.h
+++ b/components/autofill/core/browser/data_model/addresses/autofill_profile.h
@@ -346,11 +346,6 @@
   // set.
   AutofillProfile ConvertToAccountProfile() const;
 
-  // Converts a kAccount(Home|Work) address back to a regular kAccount address.
-  // This is necessary to resolve inconsistencies between server and client, to
-  // ensure that only a single H/W address can exist each.
-  AutofillProfile DowngradeToAccountProfile() const;
-
   // Checks for non-empty setting-inaccessible fields and returns all that were
   // found.
   FieldTypeSet FindInaccessibleProfileValues() const;
diff --git a/components/autofill/core/browser/filling/form_autofill_history.cc b/components/autofill/core/browser/filling/form_autofill_history.cc
index 4096064..1200814 100644
--- a/components/autofill/core/browser/filling/form_autofill_history.cc
+++ b/components/autofill/core/browser/filling/form_autofill_history.cc
@@ -4,6 +4,8 @@
 
 #include "components/autofill/core/browser/filling/form_autofill_history.h"
 
+#include <algorithm>
+
 #include "base/types/zip.h"
 #include "components/autofill/core/browser/autofill_field.h"
 #include "components/autofill/core/browser/field_types.h"
@@ -37,23 +39,11 @@
 FormAutofillHistory::FieldFillingEntry::FieldFillingEntry(FieldFillingEntry&&) =
     default;
 
-const FormAutofillHistory::FieldFillingEntry&
-FormAutofillHistory::FillOperation::GetFieldFillingEntry(
-    FieldGlobalId field_id) const {
-  auto it = iterator_->field_filling_entries.find(field_id);
-  CHECK(it != iterator_->field_filling_entries.end());
-  return it->second;
-}
-
-FormAutofillHistory::FormFillingEntry::FormFillingEntry() = default;
-
-FormAutofillHistory::FormFillingEntry::~FormFillingEntry() = default;
-
 FormAutofillHistory::FormAutofillHistory() = default;
 
 FormAutofillHistory::~FormAutofillHistory() = default;
 
-void FormAutofillHistory::AddFormFillEntry(
+void FormAutofillHistory::AddFormFillingEntry(
     base::span<const FormFieldData* const> filled_fields,
     base::span<const AutofillField* const> filled_autofill_fields,
     FillingProduct filling_product,
@@ -63,12 +53,10 @@
   // - If the original fill had `filled_fields.size() >
   //   kMaxStorableFieldFillHistory`, then `history_` might be empty.
   // - If a previous fill had `filled_fields.empty()`, we could save memory.
-  if (history_.empty() ||
-      (!is_refill && !history_.front().field_filling_entries.empty())) {
+  if (history_.empty() || (!is_refill && !history_.front().empty())) {
     history_.emplace_front();
   }
 
-  history_.front().filling_product = filling_product;
   for (const auto [field, autofill_field] :
        base::zip(filled_fields, filled_autofill_fields)) {
     // During refills, a field that was previously filled in the original
@@ -78,7 +66,6 @@
     // this is what happened from a user's perspective.
     size_ +=
         history_.front()
-            .field_filling_entries
             .emplace(field->global_id(),
                      FieldFillingEntry(
                          field->value(), field->is_autofilled(),
@@ -96,26 +83,35 @@
   }
   // Drop the last history entry while the history size exceeds the limit.
   while (size_ > kMaxStorableFieldFillHistory) {
-    EraseFormFillEntry(FillOperation(--history_.end()));
+    EraseFormFillEntry(--history_.end());
   }
 }
 
-void FormAutofillHistory::EraseFormFillEntry(FillOperation fill_operation) {
-  size_ -= fill_operation.iterator_->field_filling_entries.size();
-  history_.erase(fill_operation.iterator_);
+void FormAutofillHistory::EraseFieldFillingEntry(
+    std::list<FormFillingEntry>::iterator fill_operation,
+    FieldGlobalId field_id) {
+  fill_operation->erase(field_id);
+  if (fill_operation->empty()) {
+    EraseFormFillEntry(fill_operation);
+  }
 }
 
-FormAutofillHistory::FillOperation
-FormAutofillHistory::GetLastFillingOperationForField(
-    FieldGlobalId field_id) const {
-  return FillOperation(std::ranges::find_if(
-      history_, [&field_id](const FormFillingEntry& operation) {
-        return operation.field_filling_entries.contains(field_id);
-      }));
+void FormAutofillHistory::EraseFormFillEntry(
+    std::list<FormFillingEntry>::iterator filling_entry) {
+  size_ -= filling_entry->size();
+  history_.erase(filling_entry);
+}
+
+std::list<FormAutofillHistory::FormFillingEntry>::iterator
+FormAutofillHistory::GetLastFormFillingEntryForField(FieldGlobalId field_id) {
+  return std::ranges::find_if(history_,
+                              [&field_id](const FormFillingEntry& operation) {
+                                return operation.contains(field_id);
+                              });
 }
 
 bool FormAutofillHistory::HasHistory(FieldGlobalId field_id) const {
-  return GetLastFillingOperationForField(field_id).iterator_ != history_.end();
+  return GetLastFormFillingEntryForField(field_id) != history_.end();
 }
 
 void FormAutofillHistory::Reset() {
diff --git a/components/autofill/core/browser/filling/form_autofill_history.h b/components/autofill/core/browser/filling/form_autofill_history.h
index 9e9fc557..bfa8f3a 100644
--- a/components/autofill/core/browser/filling/form_autofill_history.h
+++ b/components/autofill/core/browser/filling/form_autofill_history.h
@@ -75,36 +75,7 @@
     bool ignore_is_autofilled;
   };
 
-  struct FormFillingEntry {
-    FormFillingEntry();
-    ~FormFillingEntry();
-
-    FillingProduct filling_product = FillingProduct::kNone;
-    std::map<FieldGlobalId, FieldFillingEntry> field_filling_entries = {};
-  };
-
-  class FillOperation {
-   public:
-    // Returns the field value and autofill state stored in history for
-    // `field_id`. Assumes the underlying map contains a entry with key
-    // `field_id`.
-    const FieldFillingEntry& GetFieldFillingEntry(FieldGlobalId field_id) const;
-
-    FillingProduct get_filling_product() const {
-      return iterator_->filling_product;
-    }
-
-    friend bool operator==(const FillOperation& lhs,
-                           const FillOperation& rhs) = default;
-
-   private:
-    friend class FormAutofillHistory;
-
-    explicit FillOperation(std::list<FormFillingEntry>::const_iterator iterator)
-        : iterator_(iterator) {}
-
-    std::list<FormFillingEntry>::const_iterator iterator_;
-  };
+  using FormFillingEntry = std::map<FieldGlobalId, FieldFillingEntry>;
 
   FormAutofillHistory();
 
@@ -118,18 +89,30 @@
   // FormFieldData's are needed to get the most recent value of a field.
   // AutofillField's are needed to get the type of a field.
   // TODO(crbug.com/40232021): Only pass AutofillFields.
-  void AddFormFillEntry(
+  void AddFormFillingEntry(
       base::span<const FormFieldData* const> filled_fields,
       base::span<const AutofillField* const> filled_autofill_fields,
       FillingProduct filling_product,
       bool is_refill);
 
-  // Erases the history entry from the list represented by `fill_operation`.
-  void EraseFormFillEntry(FillOperation fill_operation);
+  // Erases the field history information corresponding to `field_id` in
+  // `fill_operation`. If the form filling entry becomes empty afterwards, the
+  // function also removes it from `history_`.
+  void EraseFieldFillingEntry(
+      std::list<FormFillingEntry>::iterator fill_operation,
+      FieldGlobalId field_id);
 
-  // Finds the latest history entry where the field represented by `field_id`
-  // was affected.
-  FillOperation GetLastFillingOperationForField(FieldGlobalId field_id) const;
+  // Returns the first entry in `history_` (corresponding to the last
+  // chronological entry) that has information about the field represented by
+  // `field_id`, if any exist. Note that this means that the returned iterator
+  // is either `history_.end()` or satisfies  `it->contains(field_id)`.
+  std::list<FormFillingEntry>::iterator GetLastFormFillingEntryForField(
+      FieldGlobalId field_id);
+  std::list<FormFillingEntry>::const_iterator GetLastFormFillingEntryForField(
+      FieldGlobalId field_id) const {
+    return const_cast<FormAutofillHistory*>(this)
+        ->GetLastFormFillingEntryForField(field_id);
+  }
 
   // Checks whether the field represented by `field_id` has some registered
   // value in any history entry.
@@ -142,6 +125,9 @@
   bool empty() const { return history_.empty(); }
 
  private:
+  // Erases the history entry from the list represented by `fill_operation`.
+  void EraseFormFillEntry(std::list<FormFillingEntry>::iterator filling_entry);
+
   // Holds, for each filling operation in reverse chronological order, a map
   // from the IDs of the fields that were affected by the corresponding filling
   // operation to the value and autofill state of the field prior to the
diff --git a/components/autofill/core/browser/filling/form_autofill_history_unittest.cc b/components/autofill/core/browser/filling/form_autofill_history_unittest.cc
index d3aace19..2247c88 100644
--- a/components/autofill/core/browser/filling/form_autofill_history_unittest.cc
+++ b/components/autofill/core/browser/filling/form_autofill_history_unittest.cc
@@ -54,8 +54,8 @@
     for (const AutofillField& autofill_field : filled_autofill_fields_) {
       autofill_fields.push_back(&autofill_field);
     }
-    form_autofill_history_.AddFormFillEntry(fields, autofill_fields,
-                                            FillingProduct::kNone, is_refill);
+    form_autofill_history_.AddFormFillingEntry(
+        fields, autofill_fields, FillingProduct::kNone, is_refill);
   }
 
   std::vector<FormFieldData> filled_fields_;
@@ -66,8 +66,9 @@
   test::AutofillUnitTestEnvironment autofill_test_environment_;
 };
 
-// Tests the function FormAutofillHistory::AddFormFillEntry upon a normal fill.
-TEST_F(FormAutofillHistoryTest, AddFormFillEntry_NormalFill) {
+// Tests the function FormAutofillHistory::AddFormFillingEntry upon a normal
+// fill.
+TEST_F(FormAutofillHistoryTest, AddFormFillingEntry_NormalFill) {
   FieldGlobalId first_name_id =
       AddNewFieldFilling("first name", "first name", "some-value",
                          FormControlType::kInputText, NAME_FIRST);
@@ -75,8 +76,8 @@
 
   ASSERT_TRUE(form_autofill_history_.HasHistory(first_name_id));
   EXPECT_EQ(
-      form_autofill_history_.GetLastFillingOperationForField(first_name_id)
-          .GetFieldFillingEntry(first_name_id),
+      form_autofill_history_.GetLastFormFillingEntryForField(first_name_id)
+          ->at(first_name_id),
       FormAutofillHistory::FieldFillingEntry(
           u"some-value", false, kGuid, /*field_autofilled_type=*/std::nullopt,
           FillingProduct::kNone, /*ignore_is_autofilled=*/false));
@@ -85,8 +86,8 @@
   EXPECT_FALSE(form_autofill_history_.HasHistory(first_name_id));
 }
 
-// Tests the function FormAutofillHistory::AddFormFillEntry upon a refill.
-TEST_F(FormAutofillHistoryTest, AddFormFillEntry_Refill) {
+// Tests the function FormAutofillHistory::AddFormFillingEntry upon a refill.
+TEST_F(FormAutofillHistoryTest, AddFormFillingEntry_Refill) {
   FieldGlobalId first_name_id =
       AddNewFieldFilling("first name", "first name", "some-first-name",
                          FormControlType::kInputText, NAME_FIRST);
@@ -103,34 +104,31 @@
   EXPECT_TRUE(form_autofill_history_.HasHistory(last_name_id));
 
   EXPECT_EQ(
-      form_autofill_history_.GetLastFillingOperationForField(first_name_id),
-      form_autofill_history_.GetLastFillingOperationForField(last_name_id));
+      form_autofill_history_.GetLastFormFillingEntryForField(first_name_id),
+      form_autofill_history_.GetLastFormFillingEntryForField(last_name_id));
 
   ASSERT_TRUE(form_autofill_history_.HasHistory(first_name_id));
   EXPECT_EQ(
-      form_autofill_history_.GetLastFillingOperationForField(first_name_id)
-          .GetFieldFillingEntry(first_name_id),
+      form_autofill_history_.GetLastFormFillingEntryForField(first_name_id)
+          ->at(first_name_id),
       FormAutofillHistory::FieldFillingEntry(
           u"some-first-name", false, kGuid,
           /*field_autofilled_type=*/std::nullopt, FillingProduct::kNone,
           /*ignore_is_autofilled=*/false));
 
   ASSERT_TRUE(form_autofill_history_.HasHistory(last_name_id));
-  EXPECT_EQ(form_autofill_history_.GetLastFillingOperationForField(last_name_id)
-                .GetFieldFillingEntry(last_name_id),
-            FormAutofillHistory::FieldFillingEntry(
-                u"some-other-last-name", true, kGuid,
-                /*field_autofilled_type=*/std::nullopt, FillingProduct::kNone,
-                /*ignore_is_autofilled=*/false));
-
-  form_autofill_history_.EraseFormFillEntry(
-      form_autofill_history_.GetLastFillingOperationForField(first_name_id));
-  EXPECT_TRUE(form_autofill_history_.empty());
+  EXPECT_EQ(
+      form_autofill_history_.GetLastFormFillingEntryForField(last_name_id)
+          ->at(last_name_id),
+      FormAutofillHistory::FieldFillingEntry(
+          u"some-other-last-name", true, kGuid,
+          /*field_autofilled_type=*/std::nullopt, FillingProduct::kNone,
+          /*ignore_is_autofilled=*/false));
 }
 
-// Tests how the function FormAutofillHistory::AddFormFillEntry clears values to
-// remain within the size limit.
-TEST_F(FormAutofillHistoryTest, AddFormFillEntry_HistoryLimit) {
+// Tests how the function FormAutofillHistory::AddFormFillingEntry clears values
+// to remain within the size limit.
+TEST_F(FormAutofillHistoryTest, AddFormFillingEntry_HistoryLimit) {
   std::vector<FieldGlobalId> fields_id(kMaxStorableFieldFillHistory);
   for (size_t i = 0; i < kMaxStorableFieldFillHistory; ++i) {
     fields_id[i] =
@@ -158,9 +156,9 @@
   }
 }
 
-// Tests how the function FormAutofillHistory::AddFormFillEntry handles a form
-// entry bigger than the history size limit.
-TEST_F(FormAutofillHistoryTest, AddFormFillEntry_FormBiggerThanLimit) {
+// Tests how the function FormAutofillHistory::AddFormFillingEntry handles a
+// form entry bigger than the history size limit.
+TEST_F(FormAutofillHistoryTest, AddFormFillingEntry_FormBiggerThanLimit) {
   // Adding a few form fill entries.
   for (int i = 0; i < 5; ++i) {
     AddNewFieldFilling(("field-label" + base::NumberToString(i)).c_str(),
@@ -185,9 +183,9 @@
   EXPECT_TRUE(form_autofill_history_.empty());
 }
 
-// Tests how the function FormAutofillHistory::AddFormFillEntry reuses space
+// Tests how the function FormAutofillHistory::AddFormFillingEntry reuses space
 // after adding an empty form fill entry.
-TEST_F(FormAutofillHistoryTest, AddFormFillEntry_ReuseEmptyFillEntries) {
+TEST_F(FormAutofillHistoryTest, AddFormFillingEntry_ReuseEmptyFillEntries) {
   // No fields were added to `filled_fields`, hence this form filling is empty.
   AddFormFilling(/*is_refill=*/false);
   EXPECT_EQ(form_autofill_history_.size(), 1u);
@@ -199,9 +197,9 @@
   EXPECT_TRUE(form_autofill_history_.HasHistory(field_id));
 }
 
-// Tests how the function FormAutofillHistory::AddFormFillEntry reuses space
+// Tests how the function FormAutofillHistory::AddFormFillingEntry reuses space
 // after adding an empty form fill entry.
-TEST_F(FormAutofillHistoryTest, AddFormFillEntry_RefillOnEmptyHistory) {
+TEST_F(FormAutofillHistoryTest, AddFormFillingEntry_RefillOnEmptyHistory) {
   // Adding a form fill entry bigger than current size limit.
   std::vector<FieldGlobalId> fields_id(kMaxStorableFieldFillHistory + 1);
   for (size_t i = 0; i < kMaxStorableFieldFillHistory + 1; ++i) {
diff --git a/components/autofill/core/browser/filling/form_filler.cc b/components/autofill/core/browser/filling/form_filler.cc
index 25370c32..a0420404 100644
--- a/components/autofill/core/browser/filling/form_filler.cc
+++ b/components/autofill/core/browser/filling/form_filler.cc
@@ -473,21 +473,21 @@
   return skip_reasons;
 }
 
-FillingProduct FormFiller::UndoAutofill(
-    mojom::ActionPersistence action_persistence,
-    FormData form,
-    FormStructure& form_structure,
-    const FormFieldData& trigger_field) {
+void FormFiller::UndoAutofill(mojom::ActionPersistence action_persistence,
+                              FormData form,
+                              FormStructure& form_structure,
+                              const FormFieldData& trigger_field,
+                              FillingProduct filling_product) {
   if (!form_autofill_history_.HasHistory(trigger_field.global_id())) {
     LOG_AF(log_manager())
         << "Could not undo the filling operation on field "
         << trigger_field.global_id()
         << " because history was dropped upon reaching history limit of "
         << kMaxStorableFieldFillHistory;
-    return FillingProduct::kNone;
   }
-  FormAutofillHistory::FillOperation operation =
-      form_autofill_history_.GetLastFillingOperationForField(
+
+  const auto fill_operation_it =
+      form_autofill_history_.GetLastFormFillingEntryForField(
           trigger_field.global_id());
 
   std::vector<FormFieldData> fields = form.ExtractFields();
@@ -497,30 +497,41 @@
           [](const std::unique_ptr<AutofillField>& field) {
             return std::make_pair(field->global_id(), field.get());
           });
+
   // Remove the fields to be skipped so that we only pass fields to be modified
   // by the renderer.
-  std::erase_if(
-      fields, [this, &operation, &cached_fields](const FormFieldData& field) {
-        return
-            // Skip fields whose last autofill operation is different
-            // than the one of the trigger field.
-            form_autofill_history_.GetLastFillingOperationForField(
-                field.global_id()) != operation ||
-            // Skip not-autofilled fields as undo only acts on autofilled
-            // fields. Only exception is the fields that were emptied due to
-            // suggestion swapping.
-            (!field.is_autofilled() && !field.value().empty() &&
-             operation.GetFieldFillingEntry(field.global_id())
-                 .ignore_is_autofilled) ||
-            // Skip fields that are not cached to avoid unexpected outcomes.
-            !cached_fields.contains(field.global_id());
-      });
+  std::erase_if(fields, [&](const FormFieldData& field) {
+    const auto field_fill_operation_it =
+        form_autofill_history_.GetLastFormFillingEntryForField(
+            field.global_id());
+    return
+        // Skip fields whose last autofill operation is different
+        // than the one of the trigger field.
+        field_fill_operation_it != fill_operation_it ||
+        // Skip not-autofilled fields as undo only acts on autofilled
+        // fields. Only exception is the fields that were emptied due to
+        // suggestion swapping.
+        // Note that `field_fill_operation` is guaranteed to have an entry for
+        // `field.global_id()` because of the condition right above.
+        (!field.is_autofilled() && !field.value().empty() &&
+         field_fill_operation_it->at(field.global_id()).ignore_is_autofilled) ||
+        // Skip fields that are not cached to avoid unexpected outcomes.
+        !cached_fields.contains(field.global_id()) ||
+        // Skip fields which have a different filling product than the trigger
+        // field. This is to avoid modifying a field that was autofilled later
+        // with a filling product that doesn't support Undo (e.g.,
+        // Autocomplete).
+        cached_fields[field.global_id()]->filling_product() != filling_product;
+  });
 
   for (FormFieldData& field : fields) {
     AutofillField& autofill_field =
         CHECK_DEREF(cached_fields[field.global_id()]);
-    const FormAutofillHistory::FieldFillingEntry& previous_state =
-        operation.GetFieldFillingEntry(field.global_id());
+    auto it = fill_operation_it->find(field.global_id());
+    // See comments in the `erase_if` block for why this is guaranteed.
+    CHECK(it != fill_operation_it->end());
+    const FormAutofillHistory::FieldFillingEntry& previous_state = it->second;
+
     // Update the FormFieldData to be sent for the renderer.
     field.set_value(previous_state.value);
     field.set_is_autofilled(previous_state.is_autofilled);
@@ -533,6 +544,13 @@
           previous_state.autofill_source_profile_guid);
       autofill_field.set_autofilled_type(previous_state.autofilled_type);
       autofill_field.set_filling_product(previous_state.filling_product);
+
+      // The filling history is not cleared on previews as it might be used for
+      // future previews or for the filling. it is also cleared field by field
+      // because some fields in the current entry might not be used now but
+      // could still be valuable (see crbug.com/416019464).
+      form_autofill_history_.EraseFieldFillingEntry(fill_operation_it,
+                                                    field.global_id());
     }
   }
   form.set_fields(std::move(fields));
@@ -550,14 +568,6 @@
                                      action_persistence, form.fields(),
                                      url::Origin(),
                                      /*field_type_map=*/{});
-
-  FillingProduct filling_product = operation.get_filling_product();
-  if (action_persistence != mojom::ActionPersistence::kPreview) {
-    // History is not cleared on previews as it might be used for future
-    // previews or for the filling.
-    form_autofill_history_.EraseFormFillEntry(std::move(operation));
-  }
-  return filling_product;
 }
 
 void FormFiller::FillOrPreviewField(mojom::ActionPersistence action_persistence,
@@ -580,7 +590,7 @@
 
     if (ShouldRecordFillingHistory(filling_product)) {
       // TODO(crbug.com/40232021): Only use AutofillField.
-      form_autofill_history_.AddFormFillEntry(
+      form_autofill_history_.AddFormFillingEntry(
           std::to_array<const FormFieldData*>({&field}),
           std::to_array<const AutofillField*>({autofill_field}),
           filling_product,
@@ -797,7 +807,7 @@
   // Save filling history to support undoing it later if needed.
   if (action_persistence == mojom::ActionPersistence::kFill &&
       ShouldRecordFillingHistory(filling_product)) {
-    form_autofill_history_.AddFormFillEntry(
+    form_autofill_history_.AddFormFillingEntry(
         safe_filled_fields.old_values, safe_filled_fields.cached,
         filling_product, refill_trigger_reason.has_value());
   }
diff --git a/components/autofill/core/browser/filling/form_filler.h b/components/autofill/core/browser/filling/form_filler.h
index ebb6b56..4b2be7f 100644
--- a/components/autofill/core/browser/filling/form_filler.h
+++ b/components/autofill/core/browser/filling/form_filler.h
@@ -120,12 +120,13 @@
 
   // Reverts the last autofill operation on `form` that affected
   // `trigger_field`. `renderer_action` denotes whether this is an actual
-  // filling or a preview operation on the renderer side. Returns the filling
-  // product of the operation being undone.
-  FillingProduct UndoAutofill(mojom::ActionPersistence action_persistence,
-                              FormData form,
-                              FormStructure& form_structure,
-                              const FormFieldData& trigger_field);
+  // filling or a preview operation on the renderer side.
+  // TODO(crbug.com/40227496): Keep only one of `form` and `form_structure`.
+  void UndoAutofill(mojom::ActionPersistence action_persistence,
+                    FormData form,
+                    FormStructure& form_structure,
+                    const FormFieldData& trigger_field,
+                    FillingProduct filling_product);
 
   // Records filling information if possible and routes back to the renderer.
   void FillOrPreviewField(mojom::ActionPersistence action_persistence,
diff --git a/components/autofill/core/browser/filling/form_filler_test_api.h b/components/autofill/core/browser/filling/form_filler_test_api.h
index ad377cd4..e4e326c 100644
--- a/components/autofill/core/browser/filling/form_filler_test_api.h
+++ b/components/autofill/core/browser/filling/form_filler_test_api.h
@@ -20,12 +20,12 @@
     form_filler_->limit_before_refill_ = limit;
   }
 
-  void AddFormFillEntry(
+  void AddFormFillingEntry(
       base::span<const FormFieldData* const> filled_fields,
       base::span<const AutofillField* const> filled_autofill_fields,
       FillingProduct filling_product,
       bool is_refill) {
-    form_filler_->form_autofill_history_.AddFormFillEntry(
+    form_filler_->form_autofill_history_.AddFormFillingEntry(
         filled_fields, filled_autofill_fields, filling_product, is_refill);
   }
 
diff --git a/components/autofill/core/browser/filling/form_filler_unittest.cc b/components/autofill/core/browser/filling/form_filler_unittest.cc
index 2f4ae079..195b0b6 100644
--- a/components/autofill/core/browser/filling/form_filler_unittest.cc
+++ b/components/autofill/core/browser/filling/form_filler_unittest.cc
@@ -185,14 +185,12 @@
     return browser_autofill_manager_->GetAutofillField(form_id, field_id);
   }
 
-  // Lets `BrowserAutofillManager` fill `form` with `filling_payload` and
+  // Lets `BrowserAutofillManager` fill `form` using `trigger`` and
   // returns `form` as it would be extracted from the renderer afterwards, i.e.,
   // with the autofilled `FormFieldData::value`s.
-  FormData FillAutofillFormData(
+  FormData ApplyFormAction(
       FormData form,
-      const FormFieldData& trigger_field,
-      FillingPayload filling_payload,
-      AutofillTriggerSource trigger_source = AutofillTriggerSource::kPopup) {
+      base::FunctionRef<void(const FormData& form)> trigger) {
     std::vector<FormFieldData> filled_fields;
     std::vector<FieldGlobalId> global_ids;
     for (const FormFieldData& field : form.fields()) {
@@ -204,11 +202,7 @@
     EXPECT_CALL(autofill_driver_, ApplyFormAction)
         .WillOnce(
             DoAll(SaveArgElementsTo<2>(&filled_fields), Return(global_ids)));
-    form_filler().FillOrPreviewForm(
-        mojom::ActionPersistence::kFill, form, filling_payload,
-        *GetFormStructure(form),
-        *GetAutofillField(form.global_id(), trigger_field.global_id()),
-        trigger_source);
+    trigger(form);
     // Copy the filled data into the form.
     for (FormFieldData& field : test_api(form).fields()) {
       if (auto it = std::ranges::find(filled_fields, field.global_id(),
@@ -220,6 +214,54 @@
     return form;
   }
 
+  // Lets `BrowserAutofillManager` fill `form` with `filling_payload` and
+  // returns `form` as it would be extracted from the renderer afterwards, i.e.,
+  // with the autofilled `FormFieldData::value`s.
+  FormData FillAutofillFormData(
+      FormData form,
+      const FormFieldData& trigger_field,
+      FillingPayload filling_payload,
+      AutofillTriggerSource trigger_source = AutofillTriggerSource::kPopup) {
+    return ApplyFormAction(std::move(form), [&](const FormData& form) {
+      form_filler().FillOrPreviewForm(
+          mojom::ActionPersistence::kFill, form, filling_payload,
+          *GetFormStructure(form),
+          *GetAutofillField(form.global_id(), trigger_field.global_id()),
+          trigger_source);
+    });
+  }
+
+  // Lets `BrowserAutofillManager` undo the last filling operation performed on
+  // `trigger_field`, which belongs to `form`, and returns `form` as it would be
+  // extracted from the renderer afterwards, i.e., with the autofilled
+  // `FormFieldData::value`s.
+  FormData UndoAutofill(FormData form, const FormFieldData& trigger_field) {
+    return ApplyFormAction(std::move(form), [&](const FormData& form) {
+      browser_autofill_manager_->UndoAutofill(mojom::ActionPersistence::kFill,
+                                              form, trigger_field);
+    });
+  }
+
+  // Lets `BrowserAutofillManager` fill `trigger_field` with `value` and
+  // modifies `form` to reflect this filling.
+  FormData FillField(FormData form,
+                     const FormFieldData& trigger_field,
+                     FillingProduct filling_product,
+                     std::u16string value) {
+    form_filler().FillOrPreviewField(
+        mojom::ActionPersistence::kFill, mojom::FieldActionType::kReplaceAll,
+        trigger_field,
+        GetAutofillField(form.global_id(), trigger_field.global_id()), value,
+        filling_product, /*field_type_used=*/std::nullopt);
+
+    FormFieldData& field =
+        *std::ranges::find(test_api(form).fields(), trigger_field.global_id(),
+                           &FormFieldData::global_id);
+    field.set_is_autofilled(true);
+    field.set_value(value);
+    return form;
+  }
+
   std::vector<FormFieldData> PreviewVirtualCardDataAndGetResults(
       const FormData& form,
       const FormFieldData& field,
@@ -446,7 +488,7 @@
   AutofillField filled_autofill_field(form.fields().front());
   test_api(form).field(0).set_is_autofilled(false);
   test_api(form_filler())
-      .AddFormFillEntry(
+      .AddFormFillingEntry(
           std::to_array<const FormFieldData*>({&form.fields().front()}),
           std::to_array<const AutofillField*>({&filled_autofill_field}),
           FillingProduct::kAddress, /*is_refill=*/false);
@@ -1867,4 +1909,111 @@
             .triggers_refill = false,
         }));
 
+// Test that, if after an initial form filling, some field is autofilled again,
+// Undoing the first filling operation doesn't change that field.
+TEST_F(FormFillerTest, UndoSkipsFieldsAutofilledFurther) {
+  FormData form = test::GetFormData(
+      {.fields = {
+           {.role = NAME_FIRST, .autocomplete_attribute = "given-name"},
+           {.role = NAME_LAST, .autocomplete_attribute = "family-name"}}});
+  FormsSeen({form});
+  FormStructure* form_structure = GetFormStructure(form);
+  ASSERT_TRUE(form_structure);
+
+  // Fill the form with an address profile.
+  AutofillProfile profile1 = test::GetFullProfile();
+  form = FillAutofillFormData(form, form.fields()[0], &profile1);
+  EXPECT_THAT(form.fields()[0], AutofilledWith(u"John"));
+  EXPECT_THAT(form.fields()[1], AutofilledWith(u"Doe"));
+
+  // Simulate a field swapping operation on the second field.
+  form = FillField(form, form.fields()[1], FillingProduct::kAddress, u"Other");
+  EXPECT_THAT(form.fields()[0], AutofilledWith(u"John"));
+  EXPECT_THAT(form.fields()[1], AutofilledWith(u"Other"));
+
+  // Now Undo the first filling operation on the first field.
+  form = UndoAutofill(form, form.fields()[0]);
+  EXPECT_TRUE(form.fields()[0].value().empty());
+  EXPECT_FALSE(form.fields()[0].is_autofilled());
+  // The second field should not change, because the last operation that
+  // modified it isn't the one that is being currently undone.
+  EXPECT_THAT(form.fields()[1], AutofilledWith(u"Other"));
+}
+
+// Regression test for crbug.com/416019464
+TEST_F(FormFillerTest, MultipleUndoOperations) {
+  FormData form = test::GetFormData(
+      {.fields = {
+           {.role = NAME_FIRST, .autocomplete_attribute = "given-name"},
+           {.role = NAME_LAST, .autocomplete_attribute = "family-name"}}});
+  FormsSeen({form});
+  FormStructure* form_structure = GetFormStructure(form);
+  ASSERT_TRUE(form_structure);
+
+  // Fill the form with an address profile.
+  AutofillProfile profile1 = test::GetFullProfile();
+  form = FillAutofillFormData(form, form.fields()[0], &profile1);
+  EXPECT_THAT(form.fields()[0], AutofilledWith(u"John"));
+  EXPECT_THAT(form.fields()[1], AutofilledWith(u"Doe"));
+
+  // Simulate a field swapping operation on the second field.
+  form = FillField(form, form.fields()[1], FillingProduct::kAddress, u"Other");
+  EXPECT_THAT(form.fields()[0], AutofilledWith(u"John"));
+  EXPECT_THAT(form.fields()[1], AutofilledWith(u"Other"));
+
+  // Now Undo the first filling operation on the first field.
+  form = UndoAutofill(form, form.fields()[0]);
+  // The first field should be cleared, as this was its initial state.
+  EXPECT_TRUE(form.fields()[0].value().empty());
+  EXPECT_FALSE(form.fields()[0].is_autofilled());
+  // The second field should not change.
+  EXPECT_THAT(form.fields()[1], AutofilledWith(u"Other"));
+
+  // Now Undo the second filling operation on the second field.
+  form = UndoAutofill(form, form.fields()[1]);
+  EXPECT_TRUE(form.fields()[0].value().empty());
+  EXPECT_FALSE(form.fields()[0].is_autofilled());
+  // The second field should restore the value of the first filling operation.
+  EXPECT_THAT(form.fields()[1], AutofilledWith(u"Doe"));
+
+  // Now Undo the first filling operation on the second field.
+  form = UndoAutofill(form, form.fields()[1]);
+  EXPECT_TRUE(form.fields()[0].value().empty());
+  EXPECT_FALSE(form.fields()[0].is_autofilled());
+  // The second field should be cleared, as this was its initial state.
+  EXPECT_TRUE(form.fields()[1].value().empty());
+  EXPECT_FALSE(form.fields()[1].is_autofilled());
+}
+
+// Tests that Undoing a filling operation on a field discards other fields that
+// changed filling product (i.e. were autofilled afterwards using some other
+// filling product).
+TEST_F(FormFillerTest, UndoDiscardsFieldsThatChangedFillingProduct) {
+  FormData form = test::GetFormData(
+      {.fields = {
+           {.role = NAME_FIRST, .autocomplete_attribute = "given-name"},
+           {.role = NAME_LAST, .autocomplete_attribute = "family-name"}}});
+  FormsSeen({form});
+  FormStructure* form_structure = GetFormStructure(form);
+  ASSERT_TRUE(form_structure);
+
+  // Fill the form with an address profile.
+  AutofillProfile profile1 = test::GetFullProfile();
+  form = FillAutofillFormData(form, form.fields()[0], &profile1);
+  EXPECT_THAT(form.fields()[0], AutofilledWith(u"John"));
+  EXPECT_THAT(form.fields()[1], AutofilledWith(u"Doe"));
+
+  // Simulate a field swapping operation on the second field.
+  form = FillField(form, form.fields()[1], FillingProduct::kAutocomplete,
+                   u"Other");
+  EXPECT_THAT(form.fields()[0], AutofilledWith(u"John"));
+  EXPECT_THAT(form.fields()[1], AutofilledWith(u"Other"));
+
+  // Now Undo the first filling operation on the first field.
+  form = UndoAutofill(form, form.fields()[0]);
+  EXPECT_TRUE(form.fields()[0].value().empty());
+  EXPECT_FALSE(form.fields()[0].is_autofilled());
+  EXPECT_THAT(form.fields()[1], AutofilledWith(u"Other"));
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/form_parsing/internal_resources b/components/autofill/core/browser/form_parsing/internal_resources
index ee4b7f29..3fb8783 160000
--- a/components/autofill/core/browser/form_parsing/internal_resources
+++ b/components/autofill/core/browser/form_parsing/internal_resources
@@ -1 +1 @@
-Subproject commit ee4b7f29de55d7867a004208580a994e5da35ed0
+Subproject commit 3fb8783d75611c5ada2ee094ad2be8331e9bd233
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager.cc b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
index 6a303d270..b47fcd0 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
@@ -1726,10 +1726,15 @@
   if (!form_structure) {
     return;
   }
-  // This will apply the undo operation and return information about the
-  // operation being undone, for metric purposes.
-  FillingProduct filling_product = form_filler_->UndoAutofill(
-      action_persistence, form, *form_structure, trigger_field);
+  const AutofillField* autofill_trigger_field =
+      form_structure->GetFieldById(trigger_field.global_id());
+  if (!autofill_trigger_field) {
+    return;
+  }
+
+  FillingProduct filling_product = autofill_trigger_field->filling_product();
+  form_filler_->UndoAutofill(action_persistence, form, *form_structure,
+                             trigger_field, filling_product);
 
   // The remaining logic is only relevant for filling.
   if (action_persistence != mojom::ActionPersistence::kPreview) {
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager.h b/components/autofill/core/browser/foundations/browser_autofill_manager.h
index 45af9fb..380025bb 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager.h
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager.h
@@ -165,7 +165,7 @@
       FieldType field_type_used_to_build_suggestion,
       const std::string& profile_used_guid);
 
-  // Calls UndoAutofillImpl and logs metrics. Virtual for testing.
+  // Calls FormFiller::UndoAutofill and logs metrics. Virtual for testing.
   virtual void UndoAutofill(mojom::ActionPersistence action_persistence,
                             const FormData& form,
                             const FormFieldData& trigger_field);
diff --git a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc
index e5add97..1732938 100644
--- a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.cc
@@ -128,4 +128,27 @@
   return suggestions;
 }
 
+void ExtendEmailSuggestionsWithLoyaltyCardSuggestions(
+    std::vector<Suggestion>& email_suggestions,
+    const ValuablesDataManager& valuables_manager,
+    const GURL& url) {
+  std::vector<Suggestion> loyalty_card_suggestions =
+      GetLoyaltyCardSuggestions(valuables_manager, url);
+  if (loyalty_card_suggestions.empty()) {
+    return;
+  }
+
+  // TODO(crbug.com/404436027): Replace with i18n string.
+  Suggestion submenu_suggestion =
+      Suggestion(u"Loyalty cards", SuggestionType::kLoyaltyCardEntry);
+  submenu_suggestion.acceptability = Suggestion::Acceptability::kUnacceptable;
+  submenu_suggestion.children = loyalty_card_suggestions;
+
+  // There is at least one email, separator and manage addresses suggestion.
+  CHECK(email_suggestions.size() >= 3);
+  email_suggestions.insert(email_suggestions.end() - 1, submenu_suggestion);
+  email_suggestions.insert(email_suggestions.end() - 1,
+                           Suggestion(SuggestionType::kSeparator));
+}
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.h b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.h
index 1da81058..bfaeeb23 100644
--- a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.h
+++ b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator.h
@@ -24,6 +24,16 @@
     const ValuablesDataManager& valuables_manager,
     const GURL& url);
 
+// Extends `email_suggestions` with loyalty cards suggestions placed in a
+// submenu.
+//
+// Loyalty cards suggestions are retrieved and follow the same logic as
+// `GetLoyaltyCardSuggestions()`.
+void ExtendEmailSuggestionsWithLoyaltyCardSuggestions(
+    std::vector<Suggestion>& email_suggestions,
+    const ValuablesDataManager& valuables_manager,
+    const GURL& url);
+
 }  // namespace autofill
 
 #endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTIONS_VALUABLES_VALUABLE_SUGGESTION_GENERATOR_H_
diff --git a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc
index ce377e8..e6da56b 100644
--- a/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/suggestions/valuables/valuable_suggestion_generator_unittest.cc
@@ -160,6 +160,83 @@
               SuggestionIconHasImageOrUrl(fake_image, program_logo));
 }
 
+TEST_F(ValuableSuggestionGeneratorTest,
+       ExtendEmailSuggestionsWithLoyaltyCardSuggestions_ExistingLoyaltyCards) {
+  const std::vector<LoyaltyCard> loyalty_cards = {LoyaltyCard(
+      /*loyalty_card_id=*/ValuableId("loyalty_card_id_1"),
+      /*merchant_name=*/"CVS Pharmacy",
+      /*program_name=*/"CVS Extra",
+      /*program_logo=*/GURL("https://empty.url.com"),
+      /*loyalty_card_number=*/"987654321987654321",
+      {GURL("https://domain1.example"),
+       GURL("https://common-domain.example")})};
+
+  test_api(valuables_data_manager()).SetLoyaltyCards(loyalty_cards);
+
+  std::vector<Suggestion> email_suggestions = {
+      Suggestion(u"test-email1@domain1.example", SuggestionType::kAddressEntry),
+      Suggestion(u"test-email2@domain2.example", SuggestionType::kAddressEntry),
+      Suggestion(SuggestionType::kSeparator),
+      Suggestion(u"Manage addresses...", SuggestionType::kManageAddress)};
+
+  ExtendEmailSuggestionsWithLoyaltyCardSuggestions(
+      email_suggestions, valuables_data_manager(),
+      GURL("https://common-domain.example/test"));
+
+  EXPECT_THAT(
+      email_suggestions,
+      testing::ElementsAre(
+          EqualsSuggestion(SuggestionType::kAddressEntry,
+                           u"test-email1@domain1.example"),
+          EqualsSuggestion(SuggestionType::kAddressEntry,
+                           u"test-email2@domain2.example"),
+          EqualsSuggestion(SuggestionType::kSeparator),
+          EqualsSuggestion(SuggestionType::kLoyaltyCardEntry, u"Loyalty cards"),
+          EqualsSuggestion(SuggestionType::kSeparator),
+          EqualsSuggestion(SuggestionType::kManageAddress,
+                           u"Manage addresses...")));
+
+  const Suggestion& lc_submenu_suggestion = email_suggestions[3];
+  EXPECT_EQ(lc_submenu_suggestion.acceptability,
+            Suggestion::Acceptability::kUnacceptable);
+  EXPECT_THAT(lc_submenu_suggestion.children,
+              testing::ElementsAre(
+                  EqualsSuggestion(
+                      SuggestionType::kLoyaltyCardEntry, u"987654321987654321",
+                      /*is_main_text_primary=*/true, Suggestion::Icon::kNoIcon,
+                      {{Suggestion::Text(u"CVS Pharmacy")}},
+                      Suggestion::Guid("loyalty_card_id_1")),
+                  EqualsSuggestion(SuggestionType::kSeparator),
+                  EqualsSuggestion(SuggestionType::kManageLoyaltyCard,
+                                   u"Manage loyalty cards...",
+                                   Suggestion::Icon::kSettings)));
+}
+
+TEST_F(ValuableSuggestionGeneratorTest,
+       ExtendEmailSuggestionsWithLoyaltyCardSuggestions_NoLoyaltyCards) {
+  test_api(valuables_data_manager()).SetLoyaltyCards({});
+
+  std::vector<Suggestion> email_suggestions = {
+      Suggestion(u"test-email1@domain1.example", SuggestionType::kAddressEntry),
+      Suggestion(u"test-email2@domain2.example", SuggestionType::kAddressEntry),
+      Suggestion(SuggestionType::kSeparator),
+      Suggestion(u"Manage addresses...", SuggestionType::kManageAddress)};
+
+  ExtendEmailSuggestionsWithLoyaltyCardSuggestions(
+      email_suggestions, valuables_data_manager(),
+      GURL("https://common-domain.example/test"));
+
+  EXPECT_THAT(
+      email_suggestions,
+      testing::ElementsAre(EqualsSuggestion(SuggestionType::kAddressEntry,
+                                            u"test-email1@domain1.example"),
+                           EqualsSuggestion(SuggestionType::kAddressEntry,
+                                            u"test-email2@domain2.example"),
+                           EqualsSuggestion(SuggestionType::kSeparator),
+                           EqualsSuggestion(SuggestionType::kManageAddress,
+                                            u"Manage addresses...")));
+}
+
 #if !BUILDFLAG(IS_ANDROID)
 TEST_F(ValuableSuggestionGeneratorTest,
        GetLoyaltyCardSuggestions_SuggestionsIPH) {
diff --git a/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller.h b/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller.h
index 7750ef06..f0adaf7 100644
--- a/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller.h
+++ b/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller.h
@@ -15,6 +15,13 @@
  public:
   virtual ~SaveAndFillDialogController() = default;
 
+  virtual std::u16string GetWindowTitle() const = 0;
+  virtual std::u16string GetExplanatoryMessage() const = 0;
+  virtual std::u16string GetCardNumberLabel() const = 0;
+  virtual std::u16string GetNameOnCardLabel() const = 0;
+  virtual std::u16string GetAcceptButtonText() const = 0;
+  virtual bool IsUploadSaveAndFill() const = 0;
+
   virtual base::WeakPtr<SaveAndFillDialogController> GetWeakPtr() = 0;
 };
 
diff --git a/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.cc b/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.cc
index 3de0ad1..86c9ad7 100644
--- a/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.cc
+++ b/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.cc
@@ -5,6 +5,9 @@
 #include "components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.h"
 
 #include "base/memory/weak_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/strings/grit/components_strings.h"
+#include "ui/base/l10n/l10n_util.h"
 
 namespace autofill {
 
@@ -18,6 +21,37 @@
   CHECK(dialog_view_);
 }
 
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+std::u16string SaveAndFillDialogControllerImpl::GetWindowTitle() const {
+  return l10n_util::GetStringUTF16(IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_TITLE);
+}
+
+std::u16string SaveAndFillDialogControllerImpl::GetExplanatoryMessage() const {
+  return l10n_util::GetStringUTF16(
+      IsUploadSaveAndFill()
+          ? IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_EXPLANATION_UPLOAD
+          : IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_EXPLANATION_LOCAL);
+}
+
+std::u16string SaveAndFillDialogControllerImpl::GetCardNumberLabel() const {
+  return l10n_util::GetStringUTF16(
+      IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_CARD_NUMBER_LABEL);
+}
+
+std::u16string SaveAndFillDialogControllerImpl::GetNameOnCardLabel() const {
+  return l10n_util::GetStringUTF16(
+      IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_NAME_ON_CARD_LABEL);
+}
+
+std::u16string SaveAndFillDialogControllerImpl::GetAcceptButtonText() const {
+  return l10n_util::GetStringUTF16(IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_ACCEPT);
+}
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+
+bool SaveAndFillDialogControllerImpl::IsUploadSaveAndFill() const {
+  return is_upload_save_and_fill_;
+}
+
 base::WeakPtr<SaveAndFillDialogController>
 SaveAndFillDialogControllerImpl::GetWeakPtr() {
   return weak_ptr_factory_.GetWeakPtr();
diff --git a/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.h b/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.h
index 44f9a5b..db8b6f7 100644
--- a/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.h
+++ b/components/autofill/core/browser/ui/payments/save_and_fill_dialog_controller_impl.h
@@ -26,11 +26,25 @@
 
   void ShowDialog(base::OnceCallback<base::WeakPtr<SaveAndFillDialogView>()>
                       create_and_show_view_callback);
+
+#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+  std::u16string GetWindowTitle() const override;
+  std::u16string GetExplanatoryMessage() const override;
+  std::u16string GetCardNumberLabel() const override;
+  std::u16string GetNameOnCardLabel() const override;
+  std::u16string GetAcceptButtonText() const override;
+#endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
+  bool IsUploadSaveAndFill() const override;
+
   base::WeakPtr<SaveAndFillDialogController> GetWeakPtr() override;
 
  private:
   base::WeakPtr<SaveAndFillDialogView> dialog_view_;
 
+  // Determines whether the local or upload save version of the UI should be
+  // shown.
+  bool is_upload_save_and_fill_ = false;
+
   base::WeakPtrFactory<SaveAndFillDialogControllerImpl> weak_ptr_factory_{this};
 };
 
diff --git a/components/autofill/core/browser/webdata/addresses/contact_info_sync_bridge.cc b/components/autofill/core/browser/webdata/addresses/contact_info_sync_bridge.cc
index 75c935e..d71b1a5 100644
--- a/components/autofill/core/browser/webdata/addresses/contact_info_sync_bridge.cc
+++ b/components/autofill/core/browser/webdata/addresses/contact_info_sync_bridge.cc
@@ -121,10 +121,6 @@
         // Since the specifics are guaranteed to be valid by
         // `IsEntityDataValid()`, the conversion will succeed.
         DCHECK(remote);
-        if (!EnsureUniquenessOfHomeAndWork(*remote)) {
-          return syncer::ModelError(FROM_HERE,
-                                    "Failed to ensure uniqueness of H/W.");
-        }
         // Since the distinction between adds and updates is not always clear,
         // we check the existence of the profile manually and act accordingly.
         // TODO(crbug.com/40100455): Consider adding an AddOrUpdate() function
@@ -373,22 +369,6 @@
   change_processor()->ModelReadyToSync(std::move(batch));
 }
 
-bool ContactInfoSyncBridge::EnsureUniquenessOfHomeAndWork(
-    const AutofillProfile& profile) {
-  if (profile.record_type() != AutofillProfile::RecordType::kAccountHome &&
-      profile.record_type() != AutofillProfile::RecordType::kAccountWork) {
-    return true;
-  }
-  std::vector<AutofillProfile> existing_profiles;
-  AddressAutofillTable& table = *GetAutofillTable();
-  return table.GetAutofillProfiles({profile.record_type()},
-                                   existing_profiles) &&
-         std::ranges::all_of(existing_profiles, [&](const AutofillProfile& p) {
-           return p.guid() == profile.guid() ||
-                  table.UpdateAutofillProfile(p.DowngradeToAccountProfile());
-         });
-}
-
 void ContactInfoSyncBridge::FlushPendingAccountProfileChanges() {
   CHECK(change_processor()->IsTrackingMetadata());
   while (!pending_account_profile_changes_.empty()) {
diff --git a/components/autofill/core/browser/webdata/addresses/contact_info_sync_bridge.h b/components/autofill/core/browser/webdata/addresses/contact_info_sync_bridge.h
index eea9ff1..a25ba7d 100644
--- a/components/autofill/core/browser/webdata/addresses/contact_info_sync_bridge.h
+++ b/components/autofill/core/browser/webdata/addresses/contact_info_sync_bridge.h
@@ -100,20 +100,6 @@
   // the processor so it can start tracking changes.
   void LoadMetadata();
 
-  // Ensures that at most one address in the storage can be labeled as home and
-  // work each. If `profile` is H/W and a different address of the same record
-  // type already exists in the storage, this function downgrades it to a
-  // regular one. The change is intentionally not re-uploaded, because:
-  // - The logic is meant to catch inconsistencies due to failed writes, which
-  //   are not reflected on the server to begin with. E.g, it can happen that an
-  //   address is promoted to H/W in Chrome, but persisting it on the backend
-  //   fails. Then, a different address might be promoted to H/W from outside of
-  //   Chrome. Since CONTACT_INFO doesn't have a way to propagate errors back to
-  //   the client, this would result in duplicate H/W addresses.
-  // - It avoids a potential ping-pong.
-  // Returns false if storage operations fail.
-  bool EnsureUniquenessOfHomeAndWork(const AutofillProfile& profile);
-
   // Uploads all `pending_profile_changes_`.
   void FlushPendingAccountProfileChanges();
 
diff --git a/components/autofill/core/browser/webdata/addresses/contact_info_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/addresses/contact_info_sync_bridge_unittest.cc
index 3f20b77..26dcb9f6 100644
--- a/components/autofill/core/browser/webdata/addresses/contact_info_sync_bridge_unittest.cc
+++ b/components/autofill/core/browser/webdata/addresses/contact_info_sync_bridge_unittest.cc
@@ -264,61 +264,6 @@
             profile.usage_history().modification_date());
 }
 
-// Tests that `ApplyIncrementalSyncChanges()` ensures that at most one H/W
-// address exists after a profile addition.
-TEST_F(ContactInfoSyncBridgeTest,
-       ApplyIncrementalSyncChanges_DuplicateHomeAndWork_Add) {
-  ASSERT_TRUE(StartSyncing(/*remote_profiles=*/{}));
-
-  // Simulate that a home address exists and that a new home address with a
-  // different storage key is received from sync.
-  AutofillProfile local =
-      TestProfile(kGUID1, AutofillProfile::RecordType::kAccountHome);
-  AddAutofillProfilesToTable({local});
-
-  AutofillProfile remote =
-      TestProfile(kGUID2, AutofillProfile::RecordType::kAccountHome);
-  syncer::EntityChangeList entity_change_list;
-  entity_change_list.push_back(
-      syncer::EntityChange::CreateAdd(kGUID2, ProfileToEntity(remote)));
-  // `ApplyIncrementalSyncChanges()` returns an error if it fails.
-  EXPECT_FALSE(bridge().ApplyIncrementalSyncChanges(
-      bridge().CreateMetadataChangeList(), std::move(entity_change_list)));
-
-  // Expect that `local` still exists, but is no longer kAccountHome.
-  EXPECT_THAT(GetAllDataFromTable(),
-              UnorderedElementsAre(local.DowngradeToAccountProfile(), remote));
-}
-
-// Tests that `ApplyIncrementalSyncChanges()` ensures that at most one H/W
-// address exists after a profile gets updated to H/W.
-TEST_F(ContactInfoSyncBridgeTest,
-       ApplyIncrementalSyncChanges_DuplicateHomeAndWork_Update) {
-  ASSERT_TRUE(StartSyncing(/*remote_profiles=*/{}));
-
-  // Simulate that a home address exists and that an existing regular address
-  // gets upgraded to home.
-  AutofillProfile local_home =
-      TestProfile(kGUID1, AutofillProfile::RecordType::kAccountHome);
-  AutofillProfile local_regular =
-      TestProfile(kGUID2, AutofillProfile::RecordType::kAccountHome);
-  AddAutofillProfilesToTable({local_home, local_regular});
-
-  AutofillProfile remote =
-      TestProfile(kGUID2, AutofillProfile::RecordType::kAccountHome);
-  syncer::EntityChangeList entity_change_list;
-  entity_change_list.push_back(
-      syncer::EntityChange::CreateUpdate(kGUID2, ProfileToEntity(remote)));
-  // `ApplyIncrementalSyncChanges()` returns an error if it fails.
-  EXPECT_FALSE(bridge().ApplyIncrementalSyncChanges(
-      bridge().CreateMetadataChangeList(), std::move(entity_change_list)));
-
-  // Expect that `local_home` still exists, but is no longer kAccountHome.
-  EXPECT_THAT(
-      GetAllDataFromTable(),
-      UnorderedElementsAre(local_home.DowngradeToAccountProfile(), remote));
-}
-
 // Tests that `GetDataForCommit()` returns all local profiles of matching GUID.
 TEST_F(ContactInfoSyncBridgeTest, GetDataForCommit) {
   const AutofillProfile profile1 = TestProfile(kGUID1);
diff --git a/components/autofill/ios/browser/autofill_agent.mm b/components/autofill/ios/browser/autofill_agent.mm
index df3d6e70..87082217 100644
--- a/components/autofill/ios/browser/autofill_agent.mm
+++ b/components/autofill/ios/browser/autofill_agent.mm
@@ -1003,6 +1003,16 @@
                    fieldToFormLookupMap:fieldToFormLookupMap];
   }
 
+  if (base::FeatureList::IsEnabled(kAutofillRefillForFormsIos) &&
+      base::FeatureList::IsEnabled(
+          autofill::features::kAutofillAcrossIframesIos)) {
+    auto* driver =
+        autofill::AutofillDriverIOS::FromWebStateAndWebFrame(_webState, frame);
+    if (driver && driver->is_processed()) {
+      driver->ScanForms();
+    }
+  }
+
   [self recordFormFillingSuccessMetrics:!fillingResults.empty()];
 }
 
diff --git a/components/autofill/ios/browser/autofill_driver_ios.h b/components/autofill/ios/browser/autofill_driver_ios.h
index c8826f0..efb1f00 100644
--- a/components/autofill/ios/browser/autofill_driver_ios.h
+++ b/components/autofill/ios/browser/autofill_driver_ios.h
@@ -140,6 +140,9 @@
   bool is_processed() const { return processed_; }
   void set_processed(bool processed) { processed_ = processed; }
   web::WebFrame* web_frame() const;
+  base::WeakPtr<AutofillDriverIOS> GetWeakPtr() {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
 
   // Methods routed by AutofillDriverRouter. These are a subset of the methods
   // in mojom::AutofillDriver; that interface is content-specific, but to
diff --git a/components/autofill/ios/browser/autofill_driver_ios_factory.mm b/components/autofill/ios/browser/autofill_driver_ios_factory.mm
index 422e94c4..b5aa211 100644
--- a/components/autofill/ios/browser/autofill_driver_ios_factory.mm
+++ b/components/autofill/ios/browser/autofill_driver_ios_factory.mm
@@ -53,8 +53,13 @@
   for (auto& observer : AutofillDriverFactory::observers()) {
     observer.OnAutofillDriverFactoryDestroyed(*this);
   }
-  base::UmaHistogramCounts1000("Autofill.NumberOfDriversPerFactory",
-                               max_drivers_);
+  if (web_state() && web_state()->IsRealized()) {
+    // Only count the max number of drivers for realized web states because
+    // unrealized web states do not have loaded frames which can heavily skew
+    // the data towards 0 frames.
+    base::UmaHistogramCounts1000("Autofill.NumberOfDriversPerFactory",
+                                max_drivers_);
+  }
 }
 
 // The AutofillClientIOS contract guarantees that WebStateDestroyed() is called
diff --git a/components/autofill/ios/common/features.h b/components/autofill/ios/common/features.h
index 8e8683e1..152c9836 100644
--- a/components/autofill/ios/common/features.h
+++ b/components/autofill/ios/common/features.h
@@ -61,6 +61,10 @@
 // the FormSuggestionController.
 BASE_DECLARE_FEATURE(kAutofillPaymentsSheetV3Ios);
 
+// Enables the refill functionality to allow autofilling of dynamically
+// expanding forms.
+BASE_DECLARE_FEATURE(kAutofillRefillForFormsIos);
+
 // Makes the autofill and password infobars sticky on iOS. The sticky infobar
 // sticks there until navigating from an explicit user gesture (e.g. reload or
 // load a new page from the omnibox). This includes the infobar UI and the
diff --git a/components/autofill/ios/common/features.mm b/components/autofill/ios/common/features.mm
index 7e582d0..3fe47794 100644
--- a/components/autofill/ios/common/features.mm
+++ b/components/autofill/ios/common/features.mm
@@ -62,6 +62,10 @@
              "AutofillPaymentsSheetV3Ios",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kAutofillRefillForFormsIos,
+             "AutofillRefillForFormsIos",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 BASE_FEATURE(kAutofillStickyInfobarIos,
              "AutofillStickyInfobarIos",
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/components/autofill_payments_strings.grdp b/components/autofill_payments_strings.grdp
index 435e64e..5495c1f2 100644
--- a/components/autofill_payments_strings.grdp
+++ b/components/autofill_payments_strings.grdp
@@ -232,6 +232,38 @@
     Saving IBAN info
   </message>
 
+  <!-- Autofill Save and Fill dialog related strings -->
+  <if expr="not is_ios and not is_android">
+    <message name="IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_TITLE" desc="Title for the Save and Fill dialog. It is shown when a user accepts the` Save and Fill` suggestion shown in the Autofill dropdown for users who don't have any cards saved in Autofill. This always users to save and fill a credit card with a single click">
+      Save card for faster checkout
+    </message>
+    <if expr="_google_chrome">
+      <message name="IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_EXPLANATION_LOCAL" desc="Explanation text for the Autofill Save and Fill dialog for cards that will be saved locally.">
+        Autofill this card for purchases made in Chrome when it's saved on this device
+      </message>
+      <message name="IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_EXPLANATION_UPLOAD" desc="Explanation text for the Autofill Save and Fill dialog for cards that will be saved to the GPay server.">
+        Autofill this card for purchases made in Chrome when it's saved to use with Google Pay
+      </message>
+    </if>
+    <if expr="not _google_chrome">
+      <message name="IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_EXPLANATION_LOCAL" desc="Explanation text for the Autofill Save and Fill dialog for cards that will be saved locally.">
+        Autofill this card for purchases made in Chromium when it's saved on this device
+      </message>
+      <message name="IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_EXPLANATION_UPLOAD" desc="Explanation text for the Autofill Save and Fill dialog for cards that will be saved to the GPay server.">
+        Autofill this card for purchases made in Chromium when it's saved to use with Google Pay
+      </message>
+    </if>
+    <message name="IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_CARD_NUMBER_LABEL" desc="The label text for the card number textfield.">
+      Card number
+    </message>
+    <message name="IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_NAME_ON_CARD_LABEL" desc="The label text for the name on card textfield.">
+      Name on card
+    </message>
+    <message name="IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_ACCEPT" desc="Text to show for the Autofill Save and Fill dialog accept button.">
+      Save and autofill
+    </message>
+  </if>
+
   <!-- Autofill error dialog related strings -->
   <message name="IDS_AUTOFILL_MASKED_SERVER_CARD_RISK_BASED_UNMASKING_ERROR_TITLE" desc="Text to be displayed as the title of the error dialog during credit card risk-based unmasking.">
     Something went wrong
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_ACCEPT.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_ACCEPT.png.sha1
new file mode 100644
index 0000000..c11af72
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_ACCEPT.png.sha1
@@ -0,0 +1 @@
+843654a6384cf11d701664d19c373387f8714310
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_CARD_NUMBER_LABEL.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_CARD_NUMBER_LABEL.png.sha1
new file mode 100644
index 0000000..c11af72
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_CARD_NUMBER_LABEL.png.sha1
@@ -0,0 +1 @@
+843654a6384cf11d701664d19c373387f8714310
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_EXPLANATION_LOCAL.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_EXPLANATION_LOCAL.png.sha1
new file mode 100644
index 0000000..c11af72
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_EXPLANATION_LOCAL.png.sha1
@@ -0,0 +1 @@
+843654a6384cf11d701664d19c373387f8714310
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_EXPLANATION_UPLOAD.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_EXPLANATION_UPLOAD.png.sha1
new file mode 100644
index 0000000..fa4133f
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_EXPLANATION_UPLOAD.png.sha1
@@ -0,0 +1 @@
+584925b5f8bf1bab94d3ec605137d4a6cd32d05c
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_NAME_ON_CARD_LABEL.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_NAME_ON_CARD_LABEL.png.sha1
new file mode 100644
index 0000000..c11af72
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_NAME_ON_CARD_LABEL.png.sha1
@@ -0,0 +1 @@
+843654a6384cf11d701664d19c373387f8714310
\ No newline at end of file
diff --git a/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_TITLE.png.sha1 b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_TITLE.png.sha1
new file mode 100644
index 0000000..c11af72
--- /dev/null
+++ b/components/autofill_payments_strings_grdp/IDS_AUTOFILL_SAVE_AND_FILL_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@
+843654a6384cf11d701664d19c373387f8714310
\ No newline at end of file
diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ContentSettingsResources.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ContentSettingsResources.java
index 371b87b7..183df6b 100644
--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ContentSettingsResources.java
+++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/ContentSettingsResources.java
@@ -133,11 +133,6 @@
             return mDisabledPrimaryText == 0 ? getDisabledSummary() : mDisabledPrimaryText;
         }
 
-        private int getEnabledDescriptionText() {
-            // TODO(crbug.com/414413495): Remove function.
-            return 0;
-        }
-
         private int getDisabledDescriptionText() {
             return mDisabledDescriptionText;
         }
@@ -1013,7 +1008,7 @@
         int[] descriptionIDs = {
             getResourceItem(contentType).getEnabledPrimaryText(),
             getResourceItem(contentType).getDisabledPrimaryText(),
-            getResourceItem(contentType).getEnabledDescriptionText(),
+            0,
             getResourceItem(contentType).getDisabledDescriptionText()
         };
         return descriptionIDs;
diff --git a/components/cdm/common/BUILD.gn b/components/cdm/common/BUILD.gn
index 4cc79e6..687d501 100644
--- a/components/cdm/common/BUILD.gn
+++ b/components/cdm/common/BUILD.gn
@@ -2,8 +2,15 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/buildflag_header.gni")
+import("//components/cdm/common/playready.gni")
 import("//third_party/widevine/cdm/widevine.gni")
 
+buildflag_header("buildflags") {
+  header = "buildflags.h"
+  flags = [ "ENABLE_PLAYREADY=$enable_playready" ]
+}
+
 static_library("common") {
   sources = []
 
@@ -39,6 +46,10 @@
     ]
   }
 
+  if (enable_playready) {
+    sources += [ "playready_cdm_common.h" ]
+  }
+
   if (enable_library_cdms) {
     sources += [
       "cdm_manifest.cc",
diff --git a/components/cdm/common/playready.gni b/components/cdm/common/playready.gni
new file mode 100644
index 0000000..c5f0803
--- /dev/null
+++ b/components/cdm/common/playready.gni
@@ -0,0 +1,13 @@
+# 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("//build/config/chrome_build.gni")
+
+declare_args() {
+  # Enables PlayReady key system support.
+  # TODO(crbug.com/1498137): This feature is still in development.
+  enable_playready = is_win && is_chrome_branded
+}
+
+assert(!enable_playready || is_win, "PlayReady is only supported on Windows.")
diff --git a/components/cdm/common/playready_cdm_common.h b/components/cdm/common/playready_cdm_common.h
new file mode 100644
index 0000000..2c55b78d
--- /dev/null
+++ b/components/cdm/common/playready_cdm_common.h
@@ -0,0 +1,52 @@
+// 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_CDM_COMMON_PLAYREADY_CDM_COMMON_H_
+#define COMPONENTS_CDM_COMMON_PLAYREADY_CDM_COMMON_H_
+
+#include <array>
+#include <string>
+
+#include "base/containers/contains.h"
+#include "base/stl_util.h"
+#include "base/token.h"
+#include "media/cdm/cdm_type.h"
+
+inline constexpr char kPlayReadyCdmDisplayName[] =
+    "PlayReady Content Decryption Module";
+
+inline constexpr media::CdmType kPlayReadyCdmType{0xCAF6576F591C4162ull,
+                                                  0xB70FB8AE9AECD2B9ull};
+
+// PlayReady KeySystem Strings
+// https://learn.microsoft.com/en-us/playready/overview/key-system-strings
+//
+// "com.microsoft.playready.recommendation" without any robustness level
+// specified represents a software secure (security level 2000) key system.
+// If a robustness of "3000" is specified with this key system string then
+// hardware secure (security level 3000) is used. Only hardware secure
+// is supported.
+//
+// "com.microsoft.playready.recommendation.3000" does not require a robustness
+// level to be specified. This always represents hardware secure (security
+// level 3000). If a robustness of "3000" is specified with this key system
+// string then it is ignored.
+inline constexpr char kPlayReadyKeySystemRecommendationDefault[] =
+    "com.microsoft.playready.recommendation";
+inline constexpr char kPlayReadyKeySystemRecommendationHwSecure[] =
+    "com.microsoft.playready.recommendation.3000";
+
+inline constexpr std::array<const char*, 2> kPlayReadyKeySystems = {
+    kPlayReadyKeySystemRecommendationDefault,
+    kPlayReadyKeySystemRecommendationHwSecure};
+
+inline bool IsPlayReadyHwSecureKeySystem(const std::string& key_system) {
+  return key_system == kPlayReadyKeySystemRecommendationHwSecure;
+}
+
+inline bool IsPlayReadyKeySystem(const std::string& name) {
+  return base::Contains(kPlayReadyKeySystems, name);
+}
+
+#endif  // COMPONENTS_CDM_COMMON_PLAYREADY_CDM_COMMON_H_
diff --git a/components/cdm/renderer/BUILD.gn b/components/cdm/renderer/BUILD.gn
index b3d270e..fbe2ec2 100644
--- a/components/cdm/renderer/BUILD.gn
+++ b/components/cdm/renderer/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//components/cdm/common/playready.gni")
 import("//third_party/widevine/cdm/widevine.gni")
 
 static_library("renderer") {
@@ -21,6 +22,7 @@
   ]
   deps = [
     "//base",
+    "//components/cdm/common:buildflags",
     "//content/public/renderer",
     "//media",
     "//media:media_buildflags",
@@ -48,4 +50,12 @@
     ]
     deps += [ "//third_party/widevine/cdm:headers" ]
   }
+
+  if (enable_playready) {
+    sources += [
+      "playready_key_system_info.cc",
+      "playready_key_system_info.h",
+    ]
+    deps += [ "//components/cdm/common" ]
+  }
 }
diff --git a/components/cdm/renderer/playready_key_system_info.cc b/components/cdm/renderer/playready_key_system_info.cc
new file mode 100644
index 0000000..4a81fe4
--- /dev/null
+++ b/components/cdm/renderer/playready_key_system_info.cc
@@ -0,0 +1,127 @@
+// 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/cdm/renderer/playready_key_system_info.h"
+
+#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
+#include "components/cdm/common/buildflags.h"
+#include "components/cdm/common/playready_cdm_common.h"
+
+#if !BUILDFLAG(ENABLE_PLAYREADY)
+#error This file should only be built when PlayReady is enabled.
+#endif
+
+using media::EmeConfig;
+using media::EmeConfigRuleState;
+using media::EmeFeatureSupport;
+using media::EmeInitDataType;
+using media::EmeMediaType;
+using media::SupportedCodecs;
+
+namespace cdm {
+
+PlayReadyKeySystemInfo::PlayReadyKeySystemInfo(
+    media::SupportedCodecs hw_secure_codecs,
+    base::flat_set<media::EncryptionScheme> hw_secure_encryption_schemes)
+    : hw_secure_codecs_(hw_secure_codecs),
+      hw_secure_encryption_schemes_(std::move(hw_secure_encryption_schemes)) {}
+
+PlayReadyKeySystemInfo::~PlayReadyKeySystemInfo() = default;
+
+std::string PlayReadyKeySystemInfo::GetBaseKeySystemName() const {
+  return kPlayReadyKeySystemRecommendationDefault;
+}
+
+bool PlayReadyKeySystemInfo::IsSupportedKeySystem(
+    const std::string& key_system) const {
+  return IsPlayReadyKeySystem(key_system);
+}
+
+bool PlayReadyKeySystemInfo::ShouldUseBaseKeySystemName() const {
+  return true;
+}
+
+bool PlayReadyKeySystemInfo::IsSupportedInitDataType(
+    EmeInitDataType init_data_type) const {
+  // Here we assume that support for a container imples support for the
+  // associated initialization data type. KeySystems handles validating
+  // |init_data_type| x |container| pairings.
+
+  // To make KeySystemConfigSelector::GetSupportedConfiguration work correctly,
+  // use the hardware secure codecs since there are no supported software codecs
+  // in Chromium. If software secure codecs (aka. codecs_) is used here when
+  // the keysystem is "com.microsoft.playready.recommendation", then this will
+  // always return false which is not correct when robustness=3000.
+  const media::SupportedCodecs codecs = hw_secure_codecs_;
+
+  if (init_data_type == EmeInitDataType::WEBM) {
+    return (codecs & media::EME_CODEC_WEBM_ALL) != 0;
+  }
+  if (init_data_type == EmeInitDataType::CENC) {
+    return (codecs & media::EME_CODEC_MP4_ALL) != 0;
+  }
+  if (init_data_type == EmeInitDataType::KEYIDS) {
+    return true;
+  }
+
+  return false;
+}
+
+EmeConfig::Rule PlayReadyKeySystemInfo::GetEncryptionSchemeConfigRule(
+    media::EncryptionScheme encryption_scheme) const {
+  if (hw_secure_encryption_schemes_.count(encryption_scheme)) {
+    return EmeConfig{.hw_secure_codecs = EmeConfigRuleState::kRequired};
+  }
+
+  return EmeConfig::UnsupportedRule();
+}
+
+SupportedCodecs PlayReadyKeySystemInfo::GetSupportedCodecs() const {
+  return media::EME_CODEC_NONE;
+}
+
+SupportedCodecs PlayReadyKeySystemInfo::GetSupportedHwSecureCodecs() const {
+  return hw_secure_codecs_;
+}
+
+// `hw_secure_requirement` is not used here because the
+// implementation only supports hardware secure PlayReady
+// key systems. Software secure is not supported.
+EmeConfig::Rule PlayReadyKeySystemInfo::GetRobustnessConfigRule(
+    const std::string& key_system,
+    EmeMediaType media_type,
+    const std::string& requested_robustness,
+    const bool* hw_secure_requirement) const {
+  if (IsPlayReadyHwSecureKeySystem(key_system) &&
+      (requested_robustness.empty() || requested_robustness == "3000")) {
+    return EmeConfig{.hw_secure_codecs = EmeConfigRuleState::kRequired};
+  }
+
+  // Passing the robustness value of "3000" with the recommendation
+  // key system also implies hardware secure PlayReady.
+  if (key_system == kPlayReadyKeySystemRecommendationDefault &&
+      requested_robustness == "3000") {
+    return EmeConfig{.hw_secure_codecs = EmeConfigRuleState::kRequired};
+  }
+
+  // Software secure PlayReady is not supported in Chromium.
+  return EmeConfig::UnsupportedRule();
+}
+
+EmeConfig::Rule PlayReadyKeySystemInfo::GetPersistentLicenseSessionSupport()
+    const {
+  return EmeConfig::UnsupportedRule();
+}
+
+EmeFeatureSupport PlayReadyKeySystemInfo::GetPersistentStateSupport() const {
+  return EmeFeatureSupport::ALWAYS_ENABLED;
+}
+
+EmeFeatureSupport PlayReadyKeySystemInfo::GetDistinctiveIdentifierSupport()
+    const {
+  return EmeFeatureSupport::ALWAYS_ENABLED;
+}
+
+}  // namespace cdm
diff --git a/components/cdm/renderer/playready_key_system_info.h b/components/cdm/renderer/playready_key_system_info.h
new file mode 100644
index 0000000..48978f6
--- /dev/null
+++ b/components/cdm/renderer/playready_key_system_info.h
@@ -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.
+
+#ifndef COMPONENTS_CDM_RENDERER_PLAYREADY_KEY_SYSTEM_INFO_H_
+#define COMPONENTS_CDM_RENDERER_PLAYREADY_KEY_SYSTEM_INFO_H_
+
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_set.h"
+#include "media/base/key_system_info.h"
+
+namespace cdm {
+
+// Implementation of KeySystemInfo for all PlayReady key systems.
+class PlayReadyKeySystemInfo : public media::KeySystemInfo {
+ public:
+  PlayReadyKeySystemInfo(
+      media::SupportedCodecs hw_secure_codecs,
+      base::flat_set<media::EncryptionScheme> hw_secure_encryption_schemes);
+  ~PlayReadyKeySystemInfo() override;
+
+  std::string GetBaseKeySystemName() const override;
+  bool IsSupportedKeySystem(const std::string& key_system) const override;
+  bool ShouldUseBaseKeySystemName() const override;
+  bool IsSupportedInitDataType(
+      media::EmeInitDataType init_data_type) const override;
+  media::EmeConfig::Rule GetEncryptionSchemeConfigRule(
+      media::EncryptionScheme encryption_scheme) const override;
+  media::SupportedCodecs GetSupportedCodecs() const override;
+  media::SupportedCodecs GetSupportedHwSecureCodecs() const override;
+  media::EmeConfig::Rule GetRobustnessConfigRule(
+      const std::string& key_system,
+      media::EmeMediaType media_type,
+      const std::string& requested_robustness,
+      const bool* hw_secure_requirement) const override;
+  media::EmeConfig::Rule GetPersistentLicenseSessionSupport() const override;
+  media::EmeConfig GetPersistentUsageRecordSessionSupport() const;
+  media::EmeFeatureSupport GetPersistentStateSupport() const override;
+  media::EmeFeatureSupport GetDistinctiveIdentifierSupport() const override;
+
+ private:
+  const media::SupportedCodecs hw_secure_codecs_;
+  const base::flat_set<media::EncryptionScheme> hw_secure_encryption_schemes_;
+};
+
+}  // namespace cdm
+
+#endif  // COMPONENTS_CDM_RENDERER_PLAYREADY_KEY_SYSTEM_INFO_H_
diff --git a/components/collaboration/internal/BUILD.gn b/components/collaboration/internal/BUILD.gn
index 9a8d12cd..bb69c1d 100644
--- a/components/collaboration/internal/BUILD.gn
+++ b/components/collaboration/internal/BUILD.gn
@@ -253,6 +253,7 @@
       "//build/android:build_java",
       "//components/collaboration/public:java",
       "//components/data_sharing/public:public_java",
+      "//components/saved_tab_groups/public:java",
       "//third_party/jni_zero:jni_zero_java",
       "//url:url_java",
     ]
diff --git a/components/collaboration/internal/android/collaboration_service_android.cc b/components/collaboration/internal/android/collaboration_service_android.cc
index 2514ade1..9529bb9 100644
--- a/components/collaboration/internal/android/collaboration_service_android.cc
+++ b/components/collaboration/internal/android/collaboration_service_android.cc
@@ -15,6 +15,7 @@
 #include "components/collaboration/public/collaboration_service.h"
 #include "components/data_sharing/public/android/conversion_utils.h"
 #include "components/saved_tab_groups/public/android/tab_group_sync_conversions_bridge.h"
+#include "components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h"
 #include "url/android/gurl_android.h"
 
 // Must come after all headers that specialize FromJniType() / ToJniType().
@@ -88,11 +89,11 @@
     JNIEnv* env,
     jlong delegateNativePtr,
     const JavaParamRef<jstring>& j_sync_group_id,
+    const JavaParamRef<jobject>& j_local_group_id,
     jint entry) {
-  std::string sync_group_id_str =
-      base::android::ConvertJavaStringToUTF8(env, j_sync_group_id);
   tab_groups::EitherGroupID either_id =
-      base::Uuid::ParseLowercase(sync_group_id_str);
+      tab_groups::JavaSyncOrLocalGroupIdToEitherGroupId(env, j_sync_group_id,
+                                                        j_local_group_id);
 
   collaboration_service_->StartShareOrManageFlow(
       conversion::GetDelegateUniquePtrFromJava(delegateNativePtr), either_id,
@@ -103,11 +104,11 @@
     JNIEnv* env,
     jlong delegateNativePtr,
     const JavaParamRef<jstring>& j_sync_group_id,
+    const JavaParamRef<jobject>& j_local_group_id,
     jint entry) {
-  std::string sync_group_id_str =
-      base::android::ConvertJavaStringToUTF8(env, j_sync_group_id);
   tab_groups::EitherGroupID either_id =
-      base::Uuid::ParseLowercase(sync_group_id_str);
+      tab_groups::JavaSyncOrLocalGroupIdToEitherGroupId(env, j_sync_group_id,
+                                                        j_local_group_id);
 
   collaboration_service_->StartLeaveOrDeleteFlow(
       conversion::GetDelegateUniquePtrFromJava(delegateNativePtr), either_id,
diff --git a/components/collaboration/internal/android/collaboration_service_android.h b/components/collaboration/internal/android/collaboration_service_android.h
index 685131c4..8f0a8902 100644
--- a/components/collaboration/internal/android/collaboration_service_android.h
+++ b/components/collaboration/internal/android/collaboration_service_android.h
@@ -31,11 +31,13 @@
       JNIEnv* env,
       jlong delegate,
       const base::android::JavaParamRef<jstring>& j_sync_group_id,
+      const base::android::JavaParamRef<jobject>& j_local_group_id,
       jint entry);
   void StartLeaveOrDeleteFlow(
       JNIEnv* env,
       jlong delegate,
       const base::android::JavaParamRef<jstring>& j_sync_group_id,
+      const base::android::JavaParamRef<jobject>& j_local_group_id,
       jint entry);
   base::android::ScopedJavaLocalRef<jobject> GetServiceStatus(JNIEnv* env);
   jint GetCurrentUserRoleForGroup(
diff --git a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java
index ff97c4eb..33f9ff3 100644
--- a/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java
+++ b/components/collaboration/internal/android/java/src/org/chromium/components/collaboration/CollaborationServiceImpl.java
@@ -14,6 +14,8 @@
 import org.chromium.build.annotations.Nullable;
 import org.chromium.components.data_sharing.GroupData;
 import org.chromium.components.data_sharing.member_role.MemberRole;
+import org.chromium.components.tab_group_sync.EitherId.EitherGroupId;
+import org.chromium.components.tab_group_sync.LocalTabGroupId;
 import org.chromium.url.GURL;
 
 /**
@@ -48,19 +50,37 @@
     @Override
     public void startShareOrManageFlow(
             CollaborationControllerDelegate delegate,
-            String syncId,
+            EitherGroupId eitherId,
             @CollaborationServiceShareOrManageEntryPoint int entry) {
+        String syncId = null;
+        LocalTabGroupId localId = null;
+        if (eitherId.isSyncId()) {
+            syncId = eitherId.getSyncId();
+        } else {
+            localId = eitherId.getLocalId();
+        }
+
         CollaborationServiceImplJni.get()
-                .startShareOrManageFlow(mNativePtr, delegate.getNativePtr(), syncId, entry);
+                .startShareOrManageFlow(
+                        mNativePtr, delegate.getNativePtr(), syncId, localId, entry);
     }
 
     @Override
     public void startLeaveOrDeleteFlow(
             CollaborationControllerDelegate delegate,
-            String syncId,
+            EitherGroupId eitherId,
             @CollaborationServiceLeaveOrDeleteEntryPoint int entry) {
+        String syncId = null;
+        LocalTabGroupId localId = null;
+        if (eitherId.isSyncId()) {
+            syncId = eitherId.getSyncId();
+        } else {
+            localId = eitherId.getLocalId();
+        }
+
         CollaborationServiceImplJni.get()
-                .startLeaveOrDeleteFlow(mNativePtr, delegate.getNativePtr(), syncId, entry);
+                .startLeaveOrDeleteFlow(
+                        mNativePtr, delegate.getNativePtr(), syncId, localId, entry);
     }
 
     @Override
@@ -122,13 +142,15 @@
         void startShareOrManageFlow(
                 long nativeCollaborationServiceAndroid,
                 long delegateNativePtr,
-                String syncId,
+                @Nullable String syncId,
+                @Nullable LocalTabGroupId localId,
                 int entry);
 
         void startLeaveOrDeleteFlow(
                 long nativeCollaborationServiceAndroid,
                 long delegateNativePtr,
-                String syncId,
+                @Nullable String syncId,
+                @Nullable LocalTabGroupId localId,
                 int entry);
 
         ServiceStatus getServiceStatus(long nativeCollaborationServiceAndroid);
diff --git a/components/collaboration/internal/collaboration_service_impl.cc b/components/collaboration/internal/collaboration_service_impl.cc
index aac1e79..e3b350f 100644
--- a/components/collaboration/internal/collaboration_service_impl.cc
+++ b/components/collaboration/internal/collaboration_service_impl.cc
@@ -296,6 +296,11 @@
     return SyncStatus::kNotSyncing;
   }
 
+  if (sync_service_->IsSetupInProgress()) {
+    // Do not update sync status when setup is in progress.
+    return current_status_.sync_status;
+  }
+
   syncer::SyncUserSettings* user_settings = sync_service_->GetUserSettings();
   // The mapping between the selected type and what is actually sync'ed is done
   // in `GetUserSelectableTypeInfo()`.
diff --git a/components/collaboration/internal/collaboration_service_impl_unittest.cc b/components/collaboration/internal/collaboration_service_impl_unittest.cc
index ec3bb1d..5601097 100644
--- a/components/collaboration/internal/collaboration_service_impl_unittest.cc
+++ b/components/collaboration/internal/collaboration_service_impl_unittest.cc
@@ -297,6 +297,18 @@
   }
 }
 
+TEST_F(CollaborationServiceImplTest, SyncStatusChanges_SettingInProgress) {
+  // By default the test sync service is signed in with sync and every DataType
+  // enabled.
+  EXPECT_EQ(service_->GetServiceStatus().sync_status, SyncStatus::kSyncEnabled);
+
+  // Setup in progress does not change sync status.
+  test_sync_service_->SetSetupInProgress();
+  test_sync_service_->SetSignedOut();
+  test_sync_service_->FireStateChanged();
+  EXPECT_EQ(service_->GetServiceStatus().sync_status, SyncStatus::kSyncEnabled);
+}
+
 TEST_F(CollaborationServiceImplTest, ConsumerSigninChanges) {
   EXPECT_EQ(service_->GetServiceStatus().signin_status,
             SigninStatus::kNotSignedIn);
diff --git a/components/collaboration/public/android/java/src/org/chromium/components/collaboration/CollaborationService.java b/components/collaboration/public/android/java/src/org/chromium/components/collaboration/CollaborationService.java
index 6befab7..92d4b993 100644
--- a/components/collaboration/public/android/java/src/org/chromium/components/collaboration/CollaborationService.java
+++ b/components/collaboration/public/android/java/src/org/chromium/components/collaboration/CollaborationService.java
@@ -11,6 +11,7 @@
 import org.chromium.build.annotations.Nullable;
 import org.chromium.components.data_sharing.GroupData;
 import org.chromium.components.data_sharing.member_role.MemberRole;
+import org.chromium.components.tab_group_sync.EitherId.EitherGroupId;
 import org.chromium.url.GURL;
 
 /**
@@ -52,24 +53,24 @@
      * Starts a new collaboration share or manage flow.
      *
      * @param delegate The delegate to perform action on the Android UI.
-     * @param either_id The ID to identify a tab group.
+     * @param eitherId The ID to identify a tab group.
      * @param entry The entry point of the flow.
      */
     void startShareOrManageFlow(
             CollaborationControllerDelegate delegate,
-            String syncId,
+            EitherGroupId eitherId,
             @CollaborationServiceShareOrManageEntryPoint int entry);
 
     /**
      * Starts a new collaboration leave or delete flow.
      *
      * @param delegate The delegate to perform action on the Android UI.
-     * @param either_id The ID to identify a tab group.
+     * @param eitherId The ID to identify a tab group.
      * @param entry The entry point of the flow.
      */
     void startLeaveOrDeleteFlow(
             CollaborationControllerDelegate delegate,
-            String syncId,
+            EitherGroupId eitherId,
             @CollaborationServiceLeaveOrDeleteEntryPoint int entry);
 
     /** Returns the current {@link ServiceStatus} of the service. */
diff --git a/components/device_signals/core/browser/signals_types.h b/components/device_signals/core/browser/signals_types.h
index b560846..e6bcb14 100644
--- a/components/device_signals/core/browser/signals_types.h
+++ b/components/device_signals/core/browser/signals_types.h
@@ -252,8 +252,13 @@
   safe_browsing::SafeBrowsingState safe_browsing_protection_level;
   bool site_isolation_enabled;
 
-  // Enterprise cloud content analysis exclusive
+  // Enterprise cloud content analysis exclusives
   enterprise_connectors::EnterpriseRealTimeUrlCheckMode realtime_url_check_mode;
+  std::vector<std::string> file_downloaded_providers{};
+  std::vector<std::string> file_attached_providers{};
+  std::vector<std::string> bulk_data_entry_providers{};
+  std::vector<std::string> print_providers{};
+  std::vector<std::string> security_event_providers{};
 };
 
 struct FileSystemInfoResponse : BaseSignalResponse {
diff --git a/components/enterprise/BUILD.gn b/components/enterprise/BUILD.gn
index bbaad51..502a582 100644
--- a/components/enterprise/BUILD.gn
+++ b/components/enterprise/BUILD.gn
@@ -175,6 +175,7 @@
     "common/proto:connectors_proto",
     "//base/test:test_support",
     "//components/device_signals/core/browser:test_support",
+    "//components/device_signals/core/common:features",
     "//components/enterprise/browser/identifiers",
     "//components/enterprise/browser/identifiers:test_support",
     "//components/enterprise/signin:unit_tests",
diff --git a/components/enterprise/browser/reporting/chrome_profile_request_generator.cc b/components/enterprise/browser/reporting/chrome_profile_request_generator.cc
index a72ee29..a3677fd 100644
--- a/components/enterprise/browser/reporting/chrome_profile_request_generator.cc
+++ b/components/enterprise/browser/reporting/chrome_profile_request_generator.cc
@@ -241,6 +241,23 @@
             profile_signals.safe_browsing_protection_level));
     profile_signals_report->set_site_isolation_enabled(
         profile_signals.site_isolation_enabled);
+
+    profile_signals_report->mutable_file_downloaded_providers()->Add(
+        profile_signals.file_downloaded_providers.begin(),
+        profile_signals.file_downloaded_providers.end());
+    profile_signals_report->mutable_file_attached_providers()->Add(
+        profile_signals.file_attached_providers.begin(),
+        profile_signals.file_attached_providers.end());
+    profile_signals_report->mutable_bulk_data_entry_providers()->Add(
+        profile_signals.bulk_data_entry_providers.begin(),
+        profile_signals.bulk_data_entry_providers.end());
+    profile_signals_report->mutable_print_providers()->Add(
+        profile_signals.print_providers.begin(),
+        profile_signals.print_providers.end());
+    profile_signals_report->mutable_security_event_providers()->Add(
+        profile_signals.security_event_providers.begin(),
+        profile_signals.security_event_providers.end());
+
     profile_report->set_allocated_profile_signals_report(
         profile_signals_report.release());
   }
diff --git a/components/enterprise/browser/reporting/report_scheduler.cc b/components/enterprise/browser/reporting/report_scheduler.cc
index 0bcb9ae..55fb4f4 100644
--- a/components/enterprise/browser/reporting/report_scheduler.cc
+++ b/components/enterprise/browser/reporting/report_scheduler.cc
@@ -107,6 +107,11 @@
   return active_trigger_;
 }
 
+ReportGenerationConfig ReportScheduler::GetActiveGenerationConfigForTesting()
+    const {
+  return active_report_generation_config_;
+}
+
 void ReportScheduler::QueueReportUploaderForTesting(
     std::unique_ptr<ReportUploader> uploader) {
   report_uploaders_for_test_.push_back(std::move(uploader));
diff --git a/components/enterprise/browser/reporting/report_scheduler.h b/components/enterprise/browser/reporting/report_scheduler.h
index 4abb99d..365375b 100644
--- a/components/enterprise/browser/reporting/report_scheduler.h
+++ b/components/enterprise/browser/reporting/report_scheduler.h
@@ -117,6 +117,7 @@
   bool IsNextReportScheduledForTesting() const;
 
   ReportTrigger GetActiveTriggerForTesting() const;
+  ReportGenerationConfig GetActiveGenerationConfigForTesting() const;
 
   void QueueReportUploaderForTesting(std::unique_ptr<ReportUploader> uploader);
   Delegate* GetDelegateForTesting();
diff --git a/components/enterprise/browser/reporting/report_util.cc b/components/enterprise/browser/reporting/report_util.cc
index 44e1ef8..9c99b75 100644
--- a/components/enterprise/browser/reporting/report_util.cc
+++ b/components/enterprise/browser/reporting/report_util.cc
@@ -241,6 +241,23 @@
             profile_signals_report.safe_browsing_protection_level()));
     signals_dict.Set("site_isolation_enabled",
                      profile_signals_report.site_isolation_enabled());
+
+    // Providers section
+    signals_dict.Set("file_downloaded_providers",
+                     RepeatedFieldptrToList(
+                         profile_signals_report.file_downloaded_providers()));
+    signals_dict.Set("file_attached_providers",
+                     RepeatedFieldptrToList(
+                         profile_signals_report.file_attached_providers()));
+    signals_dict.Set("bulk_data_entry_providers",
+                     RepeatedFieldptrToList(
+                         profile_signals_report.bulk_data_entry_providers()));
+    signals_dict.Set(
+        "print_providers",
+        RepeatedFieldptrToList(profile_signals_report.print_providers()));
+    signals_dict.Set("security_event_providers",
+                     RepeatedFieldptrToList(
+                         profile_signals_report.security_event_providers()));
   }
 
   base::JSONWriter::WriteWithOptions(
diff --git a/components/enterprise/browser/reporting/user_security_signals_service_unittest.cc b/components/enterprise/browser/reporting/user_security_signals_service_unittest.cc
index 76775a1..3af9e492 100644
--- a/components/enterprise/browser/reporting/user_security_signals_service_unittest.cc
+++ b/components/enterprise/browser/reporting/user_security_signals_service_unittest.cc
@@ -5,8 +5,10 @@
 #include "components/enterprise/browser/reporting/user_security_signals_service.h"
 
 #include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "base/time/time.h"
+#include "components/device_signals/core/common/signals_features.h"
 #include "components/enterprise/browser/reporting/common_pref_names.h"
 #include "components/enterprise/browser/reporting/report_util.h"
 #include "components/prefs/pref_registry_simple.h"
@@ -82,14 +84,25 @@
     testing_prefs_.SetBoolean(kUserSecurityAuthenticatedReporting, use_auth);
   }
 
-  void CreateUserSecuritySignalsService(bool start_service = false) {
+  void CreateAndRunSignalsService(bool expect_reporting_enabled = true,
+                                  bool expect_using_cookie = false) {
+    // Creation of the service with the pref value already enabled will trigger
+    // an upload.
+    EXPECT_CALL(delegate_, OnReportEventTriggered(_))
+        .Times(expect_reporting_enabled ? 1 : 0);
+
+    if (expect_using_cookie) {
+      EXPECT_CALL(delegate_, GetCookieManager());
+    }
+
     service_ = std::make_unique<UserSecuritySignalsService>(&testing_prefs_,
                                                             &delegate_);
+    service_->Start();
+    task_environment_.RunUntilIdle();
 
-    if (start_service) {
-      service_->Start();
-      task_environment_.RunUntilIdle();
-    }
+    ASSERT_EQ(service_->IsSecuritySignalsReportingEnabled(),
+              expect_reporting_enabled);
+    ASSERT_EQ(service_->ShouldUseCookies(), expect_using_cookie);
   }
 
   void FastForwardTimeToTrigger() {
@@ -104,6 +117,10 @@
         base::Seconds(1));
   }
 
+  void FastForwardCustomTime(base::TimeDelta time) {
+    task_environment_.FastForwardBy(time);
+  }
+
   void TriggerValidCookieInsert() {
     test_cookie_manager_.DispatchCookieChange(net::CookieChangeInfo(
         GetTestCookie(GaiaUrls::GetInstance()->secure_google_url(),
@@ -117,6 +134,14 @@
     }
   }
 
+  // Note: Cadence unit is hours.
+  void OverrideSecurityUploadCadence(base::TimeDelta new_cadence) {
+    feature_list_.InitAndEnableFeatureWithParameters(
+        enterprise_signals::features::kProfileSignalsReportingEnabled,
+        {{enterprise_signals::features::kProfileSignalsReportingInterval.name,
+          base::ToString(new_cadence.InHours()) + "h"}});
+  }
+
   base::test::TaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
 
@@ -125,10 +150,12 @@
   testing::StrictMock<MockUserSecuritySignalsServiceDelegate> delegate_;
   network::TestCookieManager test_cookie_manager_;
   base::HistogramTester histogram_tester_;
+  base::test::ScopedFeatureList feature_list_;
 };
 
 TEST_F(UserSecuritySignalsServiceTest, NotStarted) {
-  CreateUserSecuritySignalsService();
+  service_ =
+      std::make_unique<UserSecuritySignalsService>(&testing_prefs_, &delegate_);
 
   EXPECT_FALSE(service_->IsSecuritySignalsReportingEnabled());
   EXPECT_FALSE(service_->ShouldUseCookies());
@@ -148,10 +175,7 @@
 TEST_F(UserSecuritySignalsServiceTest, PolicyDefault) {
   EXPECT_CALL(delegate_, OnReportEventTriggered(_)).Times(0);
 
-  CreateUserSecuritySignalsService(/*start_service=*/true);
-
-  EXPECT_FALSE(service_->IsSecuritySignalsReportingEnabled());
-  EXPECT_FALSE(service_->ShouldUseCookies());
+  CreateAndRunSignalsService(/*expect_reporting_enabled=*/false);
 
   // No trigger should occur even if we fast forward.
   FastForwardTimeToTrigger();
@@ -161,15 +185,8 @@
 TEST_F(UserSecuritySignalsServiceTest, PolicyEnabledWithoutCookies) {
   SetEnabledPolicy(true);
 
-  // Creation of the service with the pref value already enabled will trigger an
-  // upload.
-  EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
-      .Times(1);
+  CreateAndRunSignalsService();
 
-  CreateUserSecuritySignalsService(/*start_service=*/true);
-
-  EXPECT_TRUE(service_->IsSecuritySignalsReportingEnabled());
-  EXPECT_FALSE(service_->ShouldUseCookies());
   histogram_tester_.ExpectUniqueSample(kReportTriggerMetricName,
                                        SecurityReportTrigger::kTimer, 1);
 }
@@ -178,24 +195,17 @@
   SetEnabledPolicy(true);
   SetUseAuthPolicy(true);
 
-  // A upload should occur first on service creation.
-  EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
-      .Times(1);
-  EXPECT_CALL(delegate_, GetCookieManager());
-
-  CreateUserSecuritySignalsService(/*start_service=*/true);
-
-  EXPECT_TRUE(service_->IsSecuritySignalsReportingEnabled());
-  EXPECT_TRUE(service_->ShouldUseCookies());
+  CreateAndRunSignalsService(/*expect_reporting_enabled=*/true,
+                             /*expect_using_cookie=*/true);
+  Mock::VerifyAndClearExpectations(&delegate_);
 
   // Fast forwarding should trigger another upload.
-  Mock::VerifyAndClearExpectations(&delegate_);
   EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
       .Times(1);
   FastForwardTimeToTrigger();
+  Mock::VerifyAndClearExpectations(&delegate_);
 
   // Fast forwarding again should trigger another upload.
-  Mock::VerifyAndClearExpectations(&delegate_);
   EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
       .Times(1);
   FastForwardTimeToTrigger();
@@ -212,18 +222,12 @@
   SetEnabledPolicy(true);
   SetUseAuthPolicy(true);
 
-  // A upload should occur first on service creation.
-  EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
-      .Times(1);
-  EXPECT_CALL(delegate_, GetCookieManager());
-  CreateUserSecuritySignalsService(/*start_service=*/true);
-
-  EXPECT_TRUE(service_->IsSecuritySignalsReportingEnabled());
-  EXPECT_TRUE(service_->ShouldUseCookies());
+  CreateAndRunSignalsService(/*expect_reporting_enabled=*/true,
+                             /*expect_using_cookie=*/true);
+  Mock::VerifyAndClearExpectations(&delegate_);
 
   // Signaling that a report was uploaded after waiting a halftime means waiting
   // another halftime should not result in a second report being triggered.
-  Mock::VerifyAndClearExpectations(&delegate_);
   EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
       .Times(0);
   FastForwardByHalfTimeToTrigger();
@@ -235,7 +239,7 @@
 }
 
 TEST_F(UserSecuritySignalsServiceTest, PolicyBecomesEnabledWithoutCookies) {
-  CreateUserSecuritySignalsService(/*start_service=*/true);
+  CreateAndRunSignalsService(/*expect_reporting_enabled=*/false);
   EXPECT_FALSE(service_->IsSecuritySignalsReportingEnabled());
   EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
       .Times(0);
@@ -266,7 +270,10 @@
   // Having a broken cookie manager should not cause crashes.
   EXPECT_CALL(delegate_, GetCookieManager()).WillOnce(Return(nullptr));
 
-  CreateUserSecuritySignalsService(/*start_service=*/true);
+  service_ =
+      std::make_unique<UserSecuritySignalsService>(&testing_prefs_, &delegate_);
+  service_->Start();
+  task_environment_.RunUntilIdle();
 
   EXPECT_TRUE(service_->IsSecuritySignalsReportingEnabled());
   EXPECT_TRUE(service_->ShouldUseCookies());
@@ -280,15 +287,8 @@
   SetEnabledPolicy(true);
   SetUseAuthPolicy(true);
 
-  // A upload should occur first on service creation.
-  EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
-      .Times(1);
-  EXPECT_CALL(delegate_, GetCookieManager());
-
-  CreateUserSecuritySignalsService(/*start_service=*/true);
-
-  EXPECT_TRUE(service_->IsSecuritySignalsReportingEnabled());
-  EXPECT_TRUE(service_->ShouldUseCookies());
+  CreateAndRunSignalsService(/*expect_reporting_enabled=*/true,
+                             /*expect_using_cookie=*/true);
 
   EXPECT_CALL(delegate_,
               OnReportEventTriggered(SecurityReportTrigger::kCookieChange))
@@ -297,7 +297,6 @@
   // Fake that the first-party authentication cookie was inserted for the first
   // time.
   TriggerValidCookieInsert();
-
   FlushForTesting();
 
   histogram_tester_.ExpectBucketCount(kReportTriggerMetricName,
@@ -311,15 +310,8 @@
   SetEnabledPolicy(true);
   SetUseAuthPolicy(true);
 
-  // A upload should occur first on service creation.
-  EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
-      .Times(1);
-  EXPECT_CALL(delegate_, GetCookieManager());
-
-  CreateUserSecuritySignalsService(/*start_service=*/true);
-
-  EXPECT_TRUE(service_->IsSecuritySignalsReportingEnabled());
-  EXPECT_TRUE(service_->ShouldUseCookies());
+  CreateAndRunSignalsService(/*expect_reporting_enabled=*/true,
+                             /*expect_using_cookie=*/true);
 
   EXPECT_CALL(delegate_,
               OnReportEventTriggered(SecurityReportTrigger::kCookieChange))
@@ -333,8 +325,8 @@
       GetTestCookie(GaiaUrls::GetInstance()->secure_google_url(),
                     GaiaConstants::kGaiaSigninCookieName),
       net::CookieAccessResult(), net::CookieChangeCause::OVERWRITE));
-  TriggerValidCookieInsert();
 
+  TriggerValidCookieInsert();
   FlushForTesting();
 
   histogram_tester_.ExpectBucketCount(kReportTriggerMetricName,
@@ -348,15 +340,8 @@
   SetEnabledPolicy(true);
   SetUseAuthPolicy(true);
 
-  // A upload should occur first on service creation.
-  EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
-      .Times(1);
-  EXPECT_CALL(delegate_, GetCookieManager());
-
-  CreateUserSecuritySignalsService(/*start_service=*/true);
-
-  EXPECT_TRUE(service_->IsSecuritySignalsReportingEnabled());
-  EXPECT_TRUE(service_->ShouldUseCookies());
+  CreateAndRunSignalsService(/*expect_reporting_enabled=*/true,
+                             /*expect_using_cookie=*/true);
 
   // Fake that various cookie change events were triggered, none with "INSERT".
   // This means no additional report should be triggered.
@@ -399,11 +384,7 @@
   SetEnabledPolicy(true);
   SetUseAuthPolicy(false);
 
-  // A upload should occur first on service creation.
-  EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
-      .Times(1);
-
-  CreateUserSecuritySignalsService(/*start_service=*/true);
+  CreateAndRunSignalsService();
 
   // Auth policy is not enabled, so this should not trigger any report.
   TriggerValidCookieInsert();
@@ -429,4 +410,79 @@
                                       SecurityReportTrigger::kCookieChange, 1);
 }
 
+// Test that verifies if the feature flag param overrides the upload cadence
+// correctly.
+TEST_F(UserSecuritySignalsServiceTest, FlagOverrideCadence) {
+  auto default_security_upload_cadence =
+      UserSecuritySignalsService::GetSecurityUploadCadence();
+
+  // Overwrite the feature flag parameter to double the upload interval.
+  OverrideSecurityUploadCadence(default_security_upload_cadence * 2);
+
+  SetEnabledPolicy(true);
+
+  CreateAndRunSignalsService();
+  Mock::VerifyAndClearExpectations(&delegate_);
+
+  // Fast forwarding should not trigger another upload, since the internval is
+  // overwritten.
+  EXPECT_CALL(delegate_, OnReportEventTriggered(_)).Times(0);
+  FastForwardCustomTime(default_security_upload_cadence);
+  Mock::VerifyAndClearExpectations(&delegate_);
+
+  // Fast forwarding again should trigger another upload.
+  EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
+      .Times(1);
+  FastForwardCustomTime(default_security_upload_cadence);
+
+  histogram_tester_.ExpectUniqueSample(kReportTriggerMetricName,
+                                       SecurityReportTrigger::kTimer, 2);
+}
+
+// Test that verifies if the feature flag param overrides the upload cadence
+// correctly, even when the timer has already started. Note that this affects
+// the timer on next upload while the currently running timer is unaffected.
+TEST_F(UserSecuritySignalsServiceTest, FlagOverrideCadenceWhileRunning) {
+  auto default_security_upload_cadence =
+      UserSecuritySignalsService::GetSecurityUploadCadence();
+
+  SetEnabledPolicy(true);
+
+  CreateAndRunSignalsService();
+  Mock::VerifyAndClearExpectations(&delegate_);
+
+  // Fast forwarding should trigger another upload, since the interval is still
+  // at default value.
+  EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
+      .Times(1);
+  FastForwardCustomTime(default_security_upload_cadence);
+  Mock::VerifyAndClearExpectations(&delegate_);
+
+  // Overwrite the feature flag parameter to double the upload interval.
+  OverrideSecurityUploadCadence(default_security_upload_cadence * 2);
+
+  // Fast forwarding should trigger another upload because the timer started
+  // before we overwrite the interval, so it would be using the old value (i.e
+  // not doubled).
+  EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
+      .Times(1);
+  FastForwardCustomTime(default_security_upload_cadence);
+  Mock::VerifyAndClearExpectations(&delegate_);
+
+  // No upload trigger on first fast forward because interval is already
+  // doubled now.
+  EXPECT_CALL(delegate_, OnReportEventTriggered(_)).Times(0);
+  FastForwardCustomTime(default_security_upload_cadence);
+  Mock::VerifyAndClearExpectations(&delegate_);
+
+  // Fast forward again should trigger an upload since we reached the new,
+  // doubled upload deadline.
+  EXPECT_CALL(delegate_, OnReportEventTriggered(SecurityReportTrigger::kTimer))
+      .Times(1);
+  FastForwardCustomTime(default_security_upload_cadence);
+
+  histogram_tester_.ExpectUniqueSample(kReportTriggerMetricName,
+                                       SecurityReportTrigger::kTimer, 4);
+}
+
 }  // namespace enterprise_reporting
diff --git a/components/enterprise/client_certificates/core/features.cc b/components/enterprise/client_certificates/core/features.cc
index 2c36229a..c1e64cf5 100644
--- a/components/enterprise/client_certificates/core/features.cc
+++ b/components/enterprise/client_certificates/core/features.cc
@@ -22,4 +22,12 @@
   return base::FeatureList::IsEnabled(kManagedBrowserClientCertificateEnabled);
 }
 
+BASE_FEATURE(kManagedUserClientCertificateInPrefs,
+             "ManagedUserClientCertificateInPrefs",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+bool IsManagedUserClientCertificateInPrefsEnabled() {
+  return base::FeatureList::IsEnabled(kManagedUserClientCertificateInPrefs);
+}
+
 }  // namespace client_certificates::features
diff --git a/components/enterprise/client_certificates/core/features.h b/components/enterprise/client_certificates/core/features.h
index 93e3035..5a63a68 100644
--- a/components/enterprise/client_certificates/core/features.h
+++ b/components/enterprise/client_certificates/core/features.h
@@ -23,6 +23,12 @@
 // Return true if the managed browser's client cert feature is enabled.
 bool IsManagedBrowserClientCertificateEnabled();
 
+// Controls whether user client certs storage relies on prefs or LevelDB.
+BASE_DECLARE_FEATURE(kManagedUserClientCertificateInPrefs);
+
+// Return true if the managed user certificate should be stored in prefs.
+bool IsManagedUserClientCertificateInPrefsEnabled();
+
 }  // namespace client_certificates::features
 
 #endif  // COMPONENTS_ENTERPRISE_CLIENT_CERTIFICATES_CORE_FEATURES_H_
diff --git a/components/enterprise/client_certificates/core/leveldb_certificate_store.cc b/components/enterprise/client_certificates/core/leveldb_certificate_store.cc
index 9e54ace..2370731 100644
--- a/components/enterprise/client_certificates/core/leveldb_certificate_store.cc
+++ b/components/enterprise/client_certificates/core/leveldb_certificate_store.cc
@@ -13,6 +13,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/pickle.h"
 #include "base/task/thread_pool.h"
+#include "components/enterprise/client_certificates/core/metrics_util.h"
 #include "components/enterprise/client_certificates/core/private_key.h"
 #include "components/enterprise/client_certificates/core/private_key_factory.h"
 #include "components/enterprise/client_certificates/proto/client_certificates_database.pb.h"
@@ -170,7 +171,7 @@
                                     std::move(callback)));
 }
 
-void LevelDbCertificateStore::InitializeDatabase() {
+void LevelDbCertificateStore::InitializeDatabase(bool retry_on_failure) {
   if (database_state_ != DatabaseState::kUninitialized) {
     return;
   }
@@ -178,15 +179,25 @@
   database_state_ = DatabaseState::kInitializing;
   database_->Init(
       base::BindOnce(&LevelDbCertificateStore::OnDatabaseInitialized,
-                     weak_factory_.GetWeakPtr()));
+                     weak_factory_.GetWeakPtr(), retry_on_failure));
 }
 
 void LevelDbCertificateStore::OnDatabaseInitialized(
+    bool retry_on_failure,
     leveldb_proto::Enums::InitStatus status) {
   database_state_ = status == leveldb_proto::Enums::InitStatus::kOK
                         ? DatabaseState::kInitialized
                         : DatabaseState::kUninitialized;
 
+  // Log the status. `retry_on_failure` is only true for the first call.
+  LogLevelDBInitStatus(status, /*with_retry=*/!retry_on_failure);
+
+  if (retry_on_failure && database_state_ == DatabaseState::kUninitialized) {
+    // Retry failed DB initialization at least once.
+    InitializeDatabase(/*retry_on_failure=*/false);
+    return;
+  }
+
   for (auto& operation : pending_operations_) {
     std::move(operation).Run();
   }
diff --git a/components/enterprise/client_certificates/core/leveldb_certificate_store.h b/components/enterprise/client_certificates/core/leveldb_certificate_store.h
index b9aedc8..7959fc4 100644
--- a/components/enterprise/client_certificates/core/leveldb_certificate_store.h
+++ b/components/enterprise/client_certificates/core/leveldb_certificate_store.h
@@ -88,11 +88,12 @@
 
   // Will start the initialization of the Database. Is a no-op is the database
   // is already initialized.
-  void InitializeDatabase();
+  void InitializeDatabase(bool retry_on_failure = true);
 
   // Invoked as callback when the database is done initializing with `status` as
   // result.
-  void OnDatabaseInitialized(leveldb_proto::Enums::InitStatus status);
+  void OnDatabaseInitialized(bool retry_on_failure,
+                             leveldb_proto::Enums::InitStatus status);
 
   // Will wait for the database to be initialized and then retrieve the entry
   // with `identity_name`. If successful, will invoke `callback` with
diff --git a/components/enterprise/client_certificates/core/leveldb_certificate_store_unittest.cc b/components/enterprise/client_certificates/core/leveldb_certificate_store_unittest.cc
index ca6d169..279da2cd 100644
--- a/components/enterprise/client_certificates/core/leveldb_certificate_store_unittest.cc
+++ b/components/enterprise/client_certificates/core/leveldb_certificate_store_unittest.cc
@@ -177,17 +177,47 @@
   ExpectNoDatabaseEntry("");
 }
 
-// Tests that no key is returned when failing to initialize the database.
-TEST_F(LevelDbCertificateStoreTest, CreatePrivateKey_DatabaseInitFail) {
+// Tests that no key is returned when failing to initialize the database, and
+// the retry fails.
+TEST_F(LevelDbCertificateStoreTest,
+       CreatePrivateKey_DatabaseInitFail_RetryFail) {
   base::test::TestFuture<StoreErrorOr<scoped_refptr<PrivateKey>>> test_future;
   store_->CreatePrivateKey(kTestIdentityName, test_future.GetCallback());
 
   fake_db_->InitStatusCallback(InitStatus::kCorrupt);
+  fake_db_->InitStatusCallback(InitStatus::kCorrupt);
 
   EXPECT_THAT(test_future.Get(), ErrorIs(StoreError::kInvalidDatabaseState));
   ExpectNoDatabaseEntry(kTestIdentityName);
 }
 
+// Tests that no key is returned when failing to initialize the database, but
+// the retry succeeds.
+TEST_F(LevelDbCertificateStoreTest,
+       CreatePrivateKey_DatabaseInitFail_RetrySucceeds) {
+  client_certificates_pb::PrivateKey proto_key = CreateFakeProtoKey();
+
+  auto mocked_private_key = base::MakeRefCounted<StrictMock<MockPrivateKey>>();
+  EXPECT_CALL(*mocked_private_key, ToProto()).WillOnce(Return(proto_key));
+
+  EXPECT_CALL(*mock_key_factory_, CreatePrivateKey(_))
+      .WillOnce(RunOnceCallback<0>(mocked_private_key));
+
+  base::test::TestFuture<StoreErrorOr<scoped_refptr<PrivateKey>>> test_future;
+  store_->CreatePrivateKey(kTestIdentityName, test_future.GetCallback());
+
+  fake_db_->InitStatusCallback(InitStatus::kCorrupt);
+  fake_db_->InitStatusCallback(InitStatus::kOK);
+  fake_db_->GetCallback(/*success=*/true);
+  fake_db_->UpdateCallback(/*success=*/true);
+
+  EXPECT_THAT(test_future.Get(), ValueIs(mocked_private_key));
+
+  client_certificates_pb::ClientIdentity proto_identity;
+  *proto_identity.mutable_private_key() = proto_key;
+  ExpectDatabaseEntry(kTestIdentityName, proto_identity);
+}
+
 // Tests that no key is returned when failing to verify that the database
 // doesn't already have an identity with the same name.
 TEST_F(LevelDbCertificateStoreTest, CreatePrivateKey_GetIdentityFail) {
@@ -328,6 +358,7 @@
                             test_future.GetCallback());
 
   fake_db_->InitStatusCallback(InitStatus::kCorrupt);
+  fake_db_->InitStatusCallback(InitStatus::kCorrupt);
 
   EXPECT_EQ(test_future.Get(), StoreError::kInvalidDatabaseState);
   ExpectNoDatabaseEntry(kTestIdentityName);
@@ -452,6 +483,7 @@
                          test_future.GetCallback());
 
   fake_db_->InitStatusCallback(InitStatus::kCorrupt);
+  fake_db_->InitStatusCallback(InitStatus::kCorrupt);
 
   EXPECT_EQ(test_future.Get(), StoreError::kInvalidDatabaseState);
   ExpectNoDatabaseEntry(kOtherTestIdentityName);
@@ -673,6 +705,7 @@
   store_->GetIdentity(kTestIdentityName, test_future.GetCallback());
 
   fake_db_->InitStatusCallback(InitStatus::kCorrupt);
+  fake_db_->InitStatusCallback(InitStatus::kCorrupt);
 
   EXPECT_THAT(test_future.Get(), ErrorIs(StoreError::kInvalidDatabaseState));
 }
diff --git a/components/enterprise/client_certificates/core/metrics_util.cc b/components/enterprise/client_certificates/core/metrics_util.cc
index 3df04cc..0c9cdf3 100644
--- a/components/enterprise/client_certificates/core/metrics_util.cc
+++ b/components/enterprise/client_certificates/core/metrics_util.cc
@@ -27,6 +27,10 @@
   return success ? "Success" : "Failure";
 }
 
+std::string_view WithRetryToString(bool with_retry) {
+  return with_retry ? "WithRetry" : "NoRetry";
+}
+
 }  // namespace
 
 void LogProvisioningError(const std::string& logging_context,
@@ -108,4 +112,14 @@
       source);
 }
 
+void LogLevelDBInitStatus(leveldb_proto::Enums::InitStatus status,
+                          bool with_retry) {
+  static constexpr char kLevelDBInitHistogramFormat[] =
+      "Enterprise.CertificateStore.LevelDB.InitStatus.%s";
+  base::UmaHistogramSparse(
+      base::StringPrintf(kLevelDBInitHistogramFormat,
+                         WithRetryToString(with_retry).data()),
+      status);
+}
+
 }  // namespace client_certificates
diff --git a/components/enterprise/client_certificates/core/metrics_util.h b/components/enterprise/client_certificates/core/metrics_util.h
index 0769049e..b62f3db 100644
--- a/components/enterprise/client_certificates/core/metrics_util.h
+++ b/components/enterprise/client_certificates/core/metrics_util.h
@@ -11,6 +11,7 @@
 #include "components/enterprise/client_certificates/core/private_key_types.h"
 #include "components/enterprise/client_certificates/core/store_error.h"
 #include "components/enterprise/client_certificates/core/upload_client_error.h"
+#include "components/leveldb_proto/public/proto_database.h"
 
 namespace client_certificates {
 
@@ -54,6 +55,9 @@
 void LogPrivateKeyCreationSource(const std::string& logging_context,
                                  PrivateKeySource source);
 
+void LogLevelDBInitStatus(leveldb_proto::Enums::InitStatus status,
+                          bool with_retry);
+
 }  // namespace client_certificates
 
 #endif  // COMPONENTS_ENTERPRISE_CLIENT_CERTIFICATES_CORE_METRICS_UTIL_H_
diff --git a/components/enterprise/client_certificates/core/prefs.cc b/components/enterprise/client_certificates/core/prefs.cc
index 178962c..fcc5ce4 100644
--- a/components/enterprise/client_certificates/core/prefs.cc
+++ b/components/enterprise/client_certificates/core/prefs.cc
@@ -20,6 +20,8 @@
   registry->RegisterIntegerPref(
       prefs::kProvisionManagedClientCertificateForUserPrefs,
       /*default_value=*/0);
+  registry->RegisterDictionaryPref(kManagedProfileIdentityName);
+  registry->RegisterDictionaryPref(kTemporaryManagedProfileIdentityName);
 }
 
 void RegisterLocalStatePrefs(PrefRegistrySimple* registry) {
diff --git a/components/enterprise/common/proto/BUILD.gn b/components/enterprise/common/proto/BUILD.gn
index d1144a66..f3c7005 100644
--- a/components/enterprise/common/proto/BUILD.gn
+++ b/components/enterprise/common/proto/BUILD.gn
@@ -25,6 +25,7 @@
   sources = [ "connectors.proto" ]
 
   deps = [
+    ":connectors_proto",
     "//components/safe_browsing/core/common/proto:csd_proto",
     "//components/safe_browsing/core/common/proto:csd_proto_to_value",
   ]
diff --git a/components/enterprise/common/proto/connectors.proto b/components/enterprise/common/proto/connectors.proto
index ca5efc2..e67ca2ce 100644
--- a/components/enterprise/common/proto/connectors.proto
+++ b/components/enterprise/common/proto/connectors.proto
@@ -97,6 +97,10 @@
   // The email of the currently active user in the content area. Only populated
   // for Workspace sites.
   optional string content_area_account_email = 15;
+
+  // The parent URL chain of the frame from which the action was triggered,
+  // ordered from current frame URL to tab URL, inclusive.
+  repeated string frame_url_chain = 16;
 }
 
 message ClientMetadata {
diff --git a/components/enterprise/connectors/core/features.cc b/components/enterprise/connectors/core/features.cc
index 1ab9014f..d3b864fd 100644
--- a/components/enterprise/connectors/core/features.cc
+++ b/components/enterprise/connectors/core/features.cc
@@ -22,4 +22,8 @@
              "EnterpriseActiveUserDetection",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kEnterpriseIframeDlpRulesSupport,
+             "EnterpriseIframeDlpRulesSupport",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 }  // namespace enterprise_connectors
diff --git a/components/enterprise/connectors/core/features.h b/components/enterprise/connectors/core/features.h
index 6ed39fd..c67a14a 100644
--- a/components/enterprise/connectors/core/features.h
+++ b/components/enterprise/connectors/core/features.h
@@ -23,6 +23,10 @@
 // content area user email to DLP/reporting requests on Workspace sites.
 BASE_DECLARE_FEATURE(kEnterpriseActiveUserDetection);
 
+// Controls whether the iFrame parent url chain initiated from the active frame
+// will be attached to DLP scan requests.
+BASE_DECLARE_FEATURE(kEnterpriseIframeDlpRulesSupport);
+
 }  // namespace enterprise_connectors
 
 #endif  // COMPONENTS_ENTERPRISE_CONNECTORS_CORE_FEATURES_H_
diff --git a/components/facilitated_payments/core/features/features.cc b/components/facilitated_payments/core/features/features.cc
index 56f5721..8ffc686 100644
--- a/components/facilitated_payments/core/features/features.cc
+++ b/components/facilitated_payments/core/features/features.cc
@@ -25,6 +25,12 @@
              "DisableFacilitatedPaymentsMerchantAllowlist",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+// When enabled, Chrome will prompt users without linked Pix accounts to link
+// their Pix accounts to Google Wallet.
+BASE_FEATURE(kEnablePixAccountLinking,
+             "EnablePixAccountLinking",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
 // When enabled, Chrome will offer to pay with eWallet accounts if a payment
 // link is detected.
 BASE_FEATURE(kEwalletPayments,
diff --git a/components/facilitated_payments/core/features/features.h b/components/facilitated_payments/core/features/features.h
index 4caf5e0..726daca 100644
--- a/components/facilitated_payments/core/features/features.h
+++ b/components/facilitated_payments/core/features/features.h
@@ -14,6 +14,7 @@
 BASE_DECLARE_FEATURE(kEnablePixPaymentsInLandscapeMode);
 #if BUILDFLAG(IS_ANDROID)
 BASE_DECLARE_FEATURE(kDisableFacilitatedPaymentsMerchantAllowlist);
+BASE_DECLARE_FEATURE(kEnablePixAccountLinking);
 BASE_DECLARE_FEATURE(kEwalletPayments);
 #endif  // BUILDFLAG(IS_ANDROID)
 BASE_DECLARE_FEATURE(kSupportMultipleServerRequestsForPixPayments);
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc
index b7ed803..88327f95 100644
--- a/components/feature_engagement/public/feature_constants.cc
+++ b/components/feature_engagement/public/feature_constants.cc
@@ -74,6 +74,24 @@
 BASE_FEATURE(kIPHExtensionsRequestAccessButtonFeature,
              "IPH_ExtensionsRequestAccessButton",
              base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kIPHExtensionsZeroStatePromoFeature,
+             "IPH_ExtensionsZeroStatePromo",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+const base::FeatureParam<IPHExtensionsZeroStatePromoVariant>::Option
+    kIPHExtensionsZeroStatePromoVariantOptions[] = {
+        {IPHExtensionsZeroStatePromoVariant::kCustomActionIph,
+         "custom-action-iph"},
+        {IPHExtensionsZeroStatePromoVariant::kCustomUiChipIph,
+         "custom-ui-chip-iph"},
+        {IPHExtensionsZeroStatePromoVariant::kCustomUIPlainLinkIph,
+         "custom-ui-plain-link-iph"}};
+BASE_FEATURE_ENUM_PARAM(
+    IPHExtensionsZeroStatePromoVariant,
+    kIPHExtensionsZeroStatePromoVariantParam,
+    &feature_engagement::kIPHExtensionsZeroStatePromoFeature,
+    "iph-variant",
+    IPHExtensionsZeroStatePromoVariant::kCustomUiChipIph,
+    &kIPHExtensionsZeroStatePromoVariantOptions);
 #endif
 BASE_FEATURE(kIPHFocusHelpBubbleScreenReaderPromoFeature,
              "IPH_FocusHelpBubbleScreenReaderPromo",
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h
index 38a5379..ec58e1a 100644
--- a/components/feature_engagement/public/feature_constants.h
+++ b/components/feature_engagement/public/feature_constants.h
@@ -49,6 +49,23 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHExtensionsMenuFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHExtensionsRequestAccessButtonFeature);
+FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHExtensionsZeroStatePromoFeature);
+// The variant of In-Product-Help (IPH) shown to users with zero extensions
+// installed.
+enum IPHExtensionsZeroStatePromoVariant {
+  // A custom action IPH. Triggering the action opens a new tab to the Chrome
+  // Web Store home page.
+  kCustomActionIph,
+  // A custom UI IPH, presenting the user with different collections of
+  // extension collections in cr-chip buttons.
+  kCustomUiChipIph,
+  // A custom UI IPH, presenting the user with different collections of
+  // extension collections in plain text links.
+  kCustomUIPlainLinkIph,
+};
+COMPONENT_EXPORT(FEATURE_ENGAGEMENT_FEATURE_CONSTANTS)
+BASE_DECLARE_FEATURE_PARAM(IPHExtensionsZeroStatePromoVariant,
+                           kIPHExtensionsZeroStatePromoVariantParam);
 #endif
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHFocusHelpBubbleScreenReaderPromoFeature);
 FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHGlicPromoFeature);
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc
index 4c6b202..0b126003 100644
--- a/components/feature_engagement/public/feature_list.cc
+++ b/components/feature_engagement/public/feature_list.cc
@@ -210,6 +210,7 @@
 #if BUILDFLAG(ENABLE_EXTENSIONS)
     &kIPHExtensionsMenuFeature,
     &kIPHExtensionsRequestAccessButtonFeature,
+    &kIPHExtensionsZeroStatePromoFeature,
 #endif
     &kIPHFocusHelpBubbleScreenReaderPromoFeature,
     &kIPHGMCCastStartStopFeature,
diff --git a/components/feature_engagement/public/feature_list.h b/components/feature_engagement/public/feature_list.h
index 3172cc3..41a90a8 100644
--- a/components/feature_engagement/public/feature_list.h
+++ b/components/feature_engagement/public/feature_list.h
@@ -367,6 +367,8 @@
 DEFINE_VARIATION_PARAM(kIPHExtensionsMenuFeature, "IPH_ExtensionsMenu");
 DEFINE_VARIATION_PARAM(kIPHExtensionsRequestAccessButtonFeature,
                        "IPH_ExtensionsRequestAccessButton");
+DEFINE_VARIATION_PARAM(kIPHExtensionsZeroStatePromoFeature,
+                       "IPH_ExtensionsZeroStatePromo");
 #endif
 DEFINE_VARIATION_PARAM(kIPHGMCCastStartStopFeature, "IPH_GMCCastStartStop");
 DEFINE_VARIATION_PARAM(kIPHGMCLocalMediaCastingFeature,
diff --git a/components/ip_protection/common/ip_protection_telemetry.h b/components/ip_protection/common/ip_protection_telemetry.h
index 776c83a6..edad6b4c 100644
--- a/components/ip_protection/common/ip_protection_telemetry.h
+++ b/components/ip_protection/common/ip_protection_telemetry.h
@@ -82,6 +82,14 @@
 };
 // LINT.ThenChange(//tools/metrics/histograms/metadata/network/enums.xml:IpProtectionGetAuthTokenResultForGeo)
 
+// An enumeration of events affecting the token count.
+enum class IpProtectionTokenCountEvent {
+  kIssued = 0,
+  kSpent = 1,
+  kExpired = 2,
+  kMaxValue = kExpired,
+};
+
 // An abstract interface for all of the telemetry associated with IP Protection.
 //
 // This is implemented by each telemetry platform, and a singleton made
@@ -216,6 +224,12 @@
   // QUIC proxies failed and the fallback HTTPS proxies succeeded. The argument
   // is the number of requests made with QUIC proxies before this failure.
   virtual void QuicProxiesFailed(int after_requests) = 0;
+
+  // Records the number of tokens involved in a specific event (request, spend,
+  // expiration). `count` is the number of tokens.
+  virtual void RecordTokenCountEvent(ProxyLayer layer,
+                                     IpProtectionTokenCountEvent event,
+                                     int count) = 0;
 };
 
 // Get the singleton instance of this type. This will be implemented by each
diff --git a/components/ip_protection/common/ip_protection_telemetry_uma.cc b/components/ip_protection/common/ip_protection_telemetry_uma.cc
index 7566e9e..27b3ec2 100644
--- a/components/ip_protection/common/ip_protection_telemetry_uma.cc
+++ b/components/ip_protection/common/ip_protection_telemetry_uma.cc
@@ -55,6 +55,20 @@
   }
 }
 
+// Converts an IpProtectionTokenCountEvent enum value to its corresponding
+// string representation for histogram naming.
+std::string TokenCountEventToString(IpProtectionTokenCountEvent event) {
+  switch (event) {
+    case IpProtectionTokenCountEvent::kIssued:
+      return "Issued";
+    case IpProtectionTokenCountEvent::kSpent:
+      return "Spent";
+    case IpProtectionTokenCountEvent::kExpired:
+      return "Expired";
+  }
+  NOTREACHED();
+}
+
 }  // namespace
 
 IpProtectionTelemetry& Telemetry() {
@@ -325,4 +339,23 @@
                                after_requests);
 }
 
+void IpProtectionTelemetryUma::RecordTokenCountEvent(
+    ProxyLayer layer,
+    IpProtectionTokenCountEvent event,
+    int count) {
+  // Construct the histogram name dynamically based on the layer and event type.
+  // Example: "NetworkService.IpProtection.ProxyA.TokenCount.Issued"
+  std::string histogram_name = base::StrCat({
+      "NetworkService.IpProtection.",
+      ProxyLayerToString(layer),
+      ".TokenCount.",
+      TokenCountEventToString(event),
+  });
+
+  // Using a maximum of 1000 counts, since the maximum number of tokens per
+  // event would generally be around the batch or cache size which is typically
+  // much less than 1000.
+  base::UmaHistogramCounts1000(histogram_name, count);
+}
+
 }  // namespace ip_protection
diff --git a/components/ip_protection/common/ip_protection_telemetry_uma.h b/components/ip_protection/common/ip_protection_telemetry_uma.h
index 9ec839b..f8b7309 100644
--- a/components/ip_protection/common/ip_protection_telemetry_uma.h
+++ b/components/ip_protection/common/ip_protection_telemetry_uma.h
@@ -59,6 +59,9 @@
   void ProbabilisticRevealTokenRandomizationTime(
       base::TimeDelta duration) override;
   void QuicProxiesFailed(int after_requests) override;
+  void RecordTokenCountEvent(ProxyLayer layer,
+                             IpProtectionTokenCountEvent event,
+                             int count) override;
 };
 
 }  // namespace ip_protection
diff --git a/components/ip_protection/common/ip_protection_token_manager_impl.cc b/components/ip_protection/common/ip_protection_token_manager_impl.cc
index 809cc36..ec2d7ff8 100644
--- a/components/ip_protection/common/ip_protection_token_manager_impl.cc
+++ b/components/ip_protection/common/ip_protection_token_manager_impl.cc
@@ -304,6 +304,10 @@
 
   std::deque<BlindSignedAuthToken>& cache = cache_by_geo_[geo_id_from_token];
 
+  // Log the number of tokens successfully fetched.
+  Telemetry().RecordTokenCountEvent(
+      proxy_layer_, IpProtectionTokenCountEvent::kIssued, tokens->size());
+
   cache.insert(cache.end(), std::make_move_iterator(tokens->begin()),
                std::make_move_iterator(tokens->end()));
   std::sort(cache.begin(), cache.end(),
@@ -366,6 +370,8 @@
     result.emplace(std::move(it->second.front()));
     it->second.pop_front();
     tokens_spent_++;
+    Telemetry().RecordTokenCountEvent(proxy_layer_,
+                                      IpProtectionTokenCountEvent::kSpent, 1);
   }
 
   Telemetry().GetAuthTokenResultForGeo(
@@ -384,11 +390,20 @@
     std::deque<BlindSignedAuthToken>& tokens = it->second;
     // Remove expired tokens from each geo. Tokens are sorted and sooner
     // expirations are toward the front of the deque.
+    int64_t intial_tokens_expired = tokens_expired_;
     while (!tokens.empty() && tokens.front().expiration <= fresh_after) {
       tokens.pop_front();
       tokens_expired_++;
     }
 
+    // Only emit expired token metric if tokens actually expired.
+    int64_t tokens_expired_delta = tokens_expired_ - intial_tokens_expired;
+    if (tokens_expired_delta > 0) {
+      Telemetry().RecordTokenCountEvent(proxy_layer_,
+                                        IpProtectionTokenCountEvent::kExpired,
+                                        tokens_expired_delta);
+    }
+
     // A map entry should be removed if the entry contains no tokens and the
     // current geo does not match.
     if (tokens.empty()) {
diff --git a/components/ip_protection/common/ip_protection_token_manager_impl_unittest.cc b/components/ip_protection/common/ip_protection_token_manager_impl_unittest.cc
index a9a9b510..ab84b75 100644
--- a/components/ip_protection/common/ip_protection_token_manager_impl_unittest.cc
+++ b/components/ip_protection/common/ip_protection_token_manager_impl_unittest.cc
@@ -47,6 +47,18 @@
     "NetworkService.IpProtection.TokenBatchGenerationTime";
 constexpr char kGetAuthTokenResultForGeoHistogram[] =
     "NetworkService.IpProtection.GetAuthTokenResultForGeo";
+constexpr char kProxyATokenCountIssuedHistogram[] =
+    "NetworkService.IpProtection.ProxyA.TokenCount.Issued";
+constexpr char kProxyATokenCountSpentHistogram[] =
+    "NetworkService.IpProtection.ProxyA.TokenCount.Spent";
+constexpr char kProxyATokenCountExpiredHistogram[] =
+    "NetworkService.IpProtection.ProxyA.TokenCount.Expired";
+constexpr char kProxyBTokenCountIssuedHistogram[] =
+    "NetworkService.IpProtection.ProxyB.TokenCount.Issued";
+constexpr char kProxyBTokenCountSpentHistogram[] =
+    "NetworkService.IpProtection.ProxyB.TokenCount.Spent";
+constexpr char kProxyBTokenCountExpiredHistogram[] =
+    "NetworkService.IpProtection.ProxyB.TokenCount.Expired";
 
 constexpr base::TimeDelta kTokenLimitExceededDelay = base::Minutes(10);
 constexpr base::TimeDelta kTokenRateMeasurementInterval = base::Minutes(5);
@@ -472,7 +484,8 @@
   CallTryGetAuthTokensAndWait(ProxyLayer::kProxyA);
   ASSERT_TRUE(ipp_proxy_a_token_fetcher_->GotAllExpectedMockCalls());
 
-  auto got_token = ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId);
+  std::optional<BlindSignedAuthToken> got_token =
+      ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId);
   EXPECT_EQ(got_token.value().token, "good-token");
   EXPECT_EQ(got_token.value().expiration, kFutureExpiration);
   EXPECT_EQ(got_token.value().geo_hint, kMountainViewGeo);
@@ -490,7 +503,8 @@
   CallTryGetAuthTokensAndWait(ProxyLayer::kProxyA);
   ASSERT_TRUE(ipp_proxy_a_token_fetcher_->GotAllExpectedMockCalls());
 
-  auto got_token = ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId);
+  std::optional<BlindSignedAuthToken> got_token =
+      ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId);
   EXPECT_EQ(got_token.value().token, "token-0");
   EXPECT_LT(got_token.value().expiration, kFutureExpiration);
   EXPECT_EQ(got_token.value().geo_hint, kMountainViewGeo);
@@ -529,7 +543,7 @@
 
   // Get four tokens from the batch.
   for (int i = 0; i < 4; i++) {
-    auto got_token =
+    std::optional<BlindSignedAuthToken> got_token =
         ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId);
     EXPECT_EQ(got_token.value().token, base::StringPrintf("token-%d", i));
     EXPECT_EQ(got_token.value().expiration, kFutureExpiration);
@@ -542,7 +556,8 @@
   histogram_tester_.ExpectUniqueSample(kProxyATokenSpendRateHistogram, 48, 1);
 
   // Get the remaining token in the batch.
-  auto got_token = ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId);
+  std::optional<BlindSignedAuthToken> got_token =
+      ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId);
   EXPECT_EQ(got_token.value().token, "token-4");
   EXPECT_EQ(got_token.value().expiration, kFutureExpiration);
 
@@ -567,7 +582,8 @@
   ASSERT_TRUE(ipp_proxy_a_token_fetcher_->GotAllExpectedMockCalls());
 
   // Try to get a token, which will incidentally record the expired tokens.
-  auto got_token = ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId);
+  std::optional<BlindSignedAuthToken> got_token =
+      ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId);
   EXPECT_FALSE(got_token);
 
   // Fast-forward to run the measurement timer.
@@ -598,7 +614,7 @@
 
   // Get four tokens from the batch.
   for (int i = 0; i < 4; i++) {
-    auto got_token =
+    std::optional<BlindSignedAuthToken> got_token =
         ipp_proxy_b_token_manager_->GetAuthToken(kMountainViewGeoId);
     EXPECT_EQ(got_token.value().token, base::StringPrintf("token-%d", i));
     EXPECT_EQ(got_token.value().expiration, kFutureExpiration);
@@ -611,7 +627,8 @@
   histogram_tester_.ExpectUniqueSample(kProxyBTokenSpendRateHistogram, 48, 1);
 
   // Get the remaining token in the batch.
-  auto got_token = ipp_proxy_b_token_manager_->GetAuthToken(kMountainViewGeoId);
+  std::optional<BlindSignedAuthToken> got_token =
+      ipp_proxy_b_token_manager_->GetAuthToken(kMountainViewGeoId);
   EXPECT_EQ(got_token.value().token, "token-4");
   EXPECT_EQ(got_token.value().expiration, kFutureExpiration);
 
@@ -636,7 +653,8 @@
   ASSERT_TRUE(ipp_proxy_b_token_fetcher_->GotAllExpectedMockCalls());
 
   // Try to get a token, which will incidentally record the expired tokens.
-  auto got_token = ipp_proxy_b_token_manager_->GetAuthToken(kMountainViewGeoId);
+  std::optional<BlindSignedAuthToken> got_token =
+      ipp_proxy_b_token_manager_->GetAuthToken(kMountainViewGeoId);
   EXPECT_FALSE(got_token);
 
   // Fast-forward to run the measurement timer.
@@ -779,7 +797,8 @@
       ipp_proxy_a_token_manager_->IsAuthTokenAvailable(kMountainViewGeoId));
 
   // The un-expired token should be returned.
-  auto got_token = ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId);
+  std::optional<BlindSignedAuthToken> got_token =
+      ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId);
   EXPECT_EQ(got_token.value().token, "exp3");
 
   // Histogram should have no samples because after the initial fill there was
@@ -1108,5 +1127,129 @@
   histogram_tester_.ExpectBucketCount(kGeoChangeTokenPresence, true, 1);
   histogram_tester_.ExpectBucketCount(kGeoChangeTokenPresence, false, 1);
 }
+
+// Verify that requesting tokens logs the correct histogram count.
+TEST_F(IpProtectionTokenManagerImplTest, TokenCountRequested) {
+  const int batch_size = 5;
+  ipp_proxy_a_token_fetcher_->ExpectTryGetAuthTokensCall(
+      expected_batch_size_,
+      TokenBatch(batch_size, kFutureExpiration, kMountainViewGeo));
+  CallTryGetAuthTokensAndWait(ProxyLayer::kProxyA);
+  ASSERT_TRUE(ipp_proxy_a_token_fetcher_->GotAllExpectedMockCalls());
+
+  // Verify that 5 tokens were recorded as issued for ProxyA.
+  histogram_tester_.ExpectUniqueSample(kProxyATokenCountIssuedHistogram,
+                                       batch_size, 1);
+  // Verify other histograms were not recorded.
+  histogram_tester_.ExpectTotalCount(kProxyATokenCountSpentHistogram, 0);
+  histogram_tester_.ExpectTotalCount(kProxyATokenCountExpiredHistogram, 0);
+  histogram_tester_.ExpectTotalCount(kProxyBTokenCountIssuedHistogram, 0);
+}
+
+// Verify that spending a token logs the correct histogram count.
+TEST_F(IpProtectionTokenManagerImplTest, TokenCountSpent) {
+  // Fill the cache.
+  ipp_proxy_a_token_fetcher_->ExpectTryGetAuthTokensCall(
+      expected_batch_size_, TokenBatch(1, kFutureExpiration, kMountainViewGeo));
+  CallTryGetAuthTokensAndWait(ProxyLayer::kProxyA);
+  ASSERT_TRUE(ipp_proxy_a_token_fetcher_->GotAllExpectedMockCalls());
+  histogram_tester_.ExpectUniqueSample(kProxyATokenCountIssuedHistogram, 1, 1);
+
+  // Get the token.
+  std::optional<BlindSignedAuthToken> got_token =
+      ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId);
+  ASSERT_TRUE(got_token);
+
+  // Verify that 1 token was recorded as spent for ProxyA.
+  histogram_tester_.ExpectUniqueSample(kProxyATokenCountSpentHistogram, 1, 1);
+  // Verify other histograms were not recorded (beyond the initial issue).
+  histogram_tester_.ExpectTotalCount(kProxyATokenCountExpiredHistogram, 0);
+  histogram_tester_.ExpectTotalCount(kProxyBTokenCountSpentHistogram, 0);
+}
+
+// Verify that expired tokens log the correct histogram count.
+TEST_F(IpProtectionTokenManagerImplTest, TokenCountExpired) {
+  const int expired_count = 3;
+  // Fill the cache with expired tokens.
+  ipp_proxy_a_token_fetcher_->ExpectTryGetAuthTokensCall(
+      expected_batch_size_,
+      TokenBatch(expired_count, kPastExpiration, kMountainViewGeo));
+  CallTryGetAuthTokensAndWait(ProxyLayer::kProxyA);
+  ASSERT_TRUE(ipp_proxy_a_token_fetcher_->GotAllExpectedMockCalls());
+  histogram_tester_.ExpectUniqueSample(kProxyATokenCountIssuedHistogram,
+                                       expired_count, 1);
+
+  // Attempt to get a token, which triggers RemoveExpiredTokens.
+  std::optional<BlindSignedAuthToken> got_token =
+      ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId);
+  ASSERT_FALSE(got_token);
+
+  // Verify that 3 tokens were recorded as expired (each logged individually).
+  histogram_tester_.ExpectUniqueSample(kProxyATokenCountExpiredHistogram,
+                                       expired_count,
+                                       /*expected_bucket_count=*/1);
+  // Verify other histograms were not recorded (beyond the initial issue).
+  histogram_tester_.ExpectTotalCount(kProxyATokenCountSpentHistogram, 0);
+  histogram_tester_.ExpectTotalCount(kProxyBTokenCountExpiredHistogram, 0);
+}
+
+// Verify that events for different proxy layers are recorded separately.
+TEST_F(IpProtectionTokenManagerImplTest, TokenCountProxyLayerSeparation) {
+  // Issue 5 tokens for Proxy A.
+  ipp_proxy_a_token_fetcher_->ExpectTryGetAuthTokensCall(
+      expected_batch_size_, TokenBatch(5, kFutureExpiration, kMountainViewGeo));
+  CallTryGetAuthTokensAndWait(ProxyLayer::kProxyA);
+  ASSERT_TRUE(ipp_proxy_a_token_fetcher_->GotAllExpectedMockCalls());
+
+  // Issue 3 tokens for Proxy B.
+  ipp_proxy_b_token_fetcher_->ExpectTryGetAuthTokensCall(
+      expected_batch_size_, TokenBatch(3, kFutureExpiration, kMountainViewGeo));
+  CallTryGetAuthTokensAndWait(ProxyLayer::kProxyB);
+  ASSERT_TRUE(ipp_proxy_b_token_fetcher_->GotAllExpectedMockCalls());
+
+  // Spend 1 token for Proxy A.
+  ASSERT_TRUE(ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId));
+
+  // Spend 1 token for Proxy B.
+  ASSERT_TRUE(ipp_proxy_b_token_manager_->GetAuthToken(kMountainViewGeoId));
+
+  // Verify Proxy A counts.
+  histogram_tester_.ExpectUniqueSample(kProxyATokenCountIssuedHistogram, 5, 1);
+  histogram_tester_.ExpectUniqueSample(kProxyATokenCountSpentHistogram, 1, 1);
+  histogram_tester_.ExpectTotalCount(kProxyATokenCountExpiredHistogram, 0);
+
+  // Verify Proxy B counts.
+  histogram_tester_.ExpectUniqueSample(kProxyBTokenCountIssuedHistogram, 3, 1);
+  histogram_tester_.ExpectUniqueSample(kProxyBTokenCountSpentHistogram, 1, 1);
+  histogram_tester_.ExpectTotalCount(kProxyBTokenCountExpiredHistogram, 0);
+}
+
+// Verify multiple event types are recorded correctly within one manager.
+TEST_F(IpProtectionTokenManagerImplTest, TokenCountMultipleEvents) {
+  // Issue 5 tokens, 2 of which are already expired.
+  std::vector<BlindSignedAuthToken> tokens =
+      TokenBatch(3, kFutureExpiration, kMountainViewGeo);
+  std::vector<BlindSignedAuthToken> expired_tokens =
+      TokenBatch(2, kPastExpiration, kMountainViewGeo);
+  tokens.insert(tokens.end(), std::make_move_iterator(expired_tokens.begin()),
+                std::make_move_iterator(expired_tokens.end()));
+
+  ipp_proxy_a_token_fetcher_->ExpectTryGetAuthTokensCall(expected_batch_size_,
+                                                         std::move(tokens));
+  CallTryGetAuthTokensAndWait(ProxyLayer::kProxyA);
+  ASSERT_TRUE(ipp_proxy_a_token_fetcher_->GotAllExpectedMockCalls());
+
+  // Spend 1 token (this also triggers removal of expired tokens).
+  ASSERT_TRUE(ipp_proxy_a_token_manager_->GetAuthToken(kMountainViewGeoId));
+
+  // Verify counts.
+  histogram_tester_.ExpectUniqueSample(kProxyATokenCountIssuedHistogram, 5,
+                                       1);  // 3 good + 2 expired
+  histogram_tester_.ExpectUniqueSample(kProxyATokenCountSpentHistogram, 1, 1);
+  histogram_tester_.ExpectUniqueSample(
+      kProxyATokenCountExpiredHistogram, /*sample=*/2,
+      /*expected_bucket_count=*/1);  // 2 expired tokens removed
+}
+
 }  // namespace
 }  // namespace ip_protection
diff --git a/components/live_caption/views/DEPS b/components/live_caption/views/DEPS
new file mode 100644
index 0000000..21a3fd6
--- /dev/null
+++ b/components/live_caption/views/DEPS
@@ -0,0 +1,7 @@
+specific_include_rules = {
+  "caption_bubble_browsertest.cc": [
+    "+chrome/browser/ui",
+    "+chrome/test",
+    "+content/public",
+  ]
+}
diff --git a/components/live_caption/views/caption_bubble.cc b/components/live_caption/views/caption_bubble.cc
index 92ec2326..dbebcc6 100644
--- a/components/live_caption/views/caption_bubble.cc
+++ b/components/live_caption/views/caption_bubble.cc
@@ -982,7 +982,7 @@
   }
 
   // Call this after SetCaptionButtonStyle(), not before, since
-  // SetCaptionButtonStyle() calls set_background_color(), which
+  // SetCaptionButtonStyle() calls SetBackgroundColor(), which
   // OnThemeChanged() will trigger a read of.
   views::BubbleDialogDelegateView::OnThemeChanged();
 }
@@ -1491,7 +1491,7 @@
                                           &background_color, color_provider);
   }
 
-  set_background_color(background_color);
+  views::BubbleDialogDelegateView::SetBackgroundColor(background_color);
   GetWidget()->SetColorModeOverride(ui::ColorProviderKey::ColorMode::kDark);
 }
 
diff --git a/components/live_caption/views/caption_bubble_browsertest.cc b/components/live_caption/views/caption_bubble_browsertest.cc
new file mode 100644
index 0000000..0eaecbc68
--- /dev/null
+++ b/components/live_caption/views/caption_bubble_browsertest.cc
@@ -0,0 +1,144 @@
+// 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/live_caption/views/caption_bubble.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/cfi_buildflags.h"
+#include "base/functional/callback.h"
+#include "base/functional/callback_forward.h"
+#include "base/scoped_observation.h"
+#include "base/strings/strcat.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/task/bind_post_task.h"
+#include "base/task/thread_pool/thread_pool_instance.h"
+#include "base/test/bind.h"
+#include "base/test/metrics/user_action_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/test_future.h"
+#include "base/test/test_timeouts.h"
+#include "base/types/expected.h"
+#include "build/build_config.h"
+#include "caption_bubble_model.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/confirm_bubble.h"
+#include "chrome/browser/ui/test/test_browser_ui.h"
+#include "chrome/browser/ui/views/accessibility/caption_bubble_context_views.h"
+#include "chrome/test/base/interactive_test_utils.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/live_caption/caption_bubble_context.h"
+#include "components/live_caption/caption_bubble_controller.h"
+#include "components/live_caption/caption_bubble_settings.h"
+#include "components/live_caption/live_caption_bubble_settings.h"
+#include "components/live_caption/pref_names.h"
+#include "components/prefs/pref_registry_simple.h"
+#include "components/prefs/pref_service.h"
+#include "components/prefs/testing_pref_service.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/test/ui_controls.h"
+
+namespace captions {
+namespace {
+
+constexpr char kEnglishLanguage[] = "en-US";
+
+class CaptionBubbleBrowserTest : public UiBrowserTest {
+ protected:
+  CaptionBubbleBrowserTest() = default;
+
+  void SetUpOnMainThread() override {
+    pref_service_.registry()->RegisterBooleanPref(
+        prefs::kLiveCaptionBubbleExpanded, false);
+    pref_service_.registry()->RegisterBooleanPref(prefs::kLiveTranslateEnabled,
+                                                  false);
+    pref_service_.registry()->RegisterBooleanPref(prefs::kLiveCaptionEnabled,
+                                                  false);
+    pref_service_.registry()->RegisterStringPref(
+        prefs::kLiveCaptionLanguageCode, kEnglishLanguage);
+    pref_service_.registry()->RegisterStringPref(
+        prefs::kLiveTranslateTargetLanguageCode, kEnglishLanguage);
+    UiBrowserTest::SetUpOnMainThread();
+  }
+
+  void TearDownOnMainThread() override {
+    context_.reset();
+    UiBrowserTest::TearDownOnMainThread();
+  }
+
+  // UiBrowserTest:
+  void ShowUi(const std::string& name) override {
+    context_ = std::make_unique<CaptionBubbleContextViews>(
+        browser()->GetActiveTabInterface()->GetContents());
+    OnCaptionBubbleClosedCallback callback;
+    model_ = std::make_unique<CaptionBubbleModel>(context_.get(),
+                                                  std::move(callback));
+    settings_ = std::make_unique<LiveCaptionBubbleSettings>(&pref_service_);
+    settings_->SetLiveCaptionBubbleExpanded(true);
+
+    const std::string application_locale;
+    base::OnceClosure destroyed_callback;
+    auto bubble = std::make_unique<CaptionBubble>(
+        settings_.get(), application_locale, std::move(destroyed_callback));
+    bubble_ = bubble.get();
+    views::BubbleDialogDelegateView::CreateBubble(std::move(bubble))->Show();
+    bubble_->SetModel(model_.get());
+    model_->SetPartialText("ABCDEF");
+    model_->CommitPartialText();
+  }
+
+  // These next two are not necessary if subclassing DialogBrowserTest.
+  bool VerifyUi() override {
+    views::Widget* widget = GetWidgetForScreenshot();
+
+    auto* const test_info =
+        testing::UnitTest::GetInstance()->current_test_info();
+    const std::string screenshot_name =
+        base::StrCat({test_info->test_suite_name(), "_", test_info->name()});
+
+    return VerifyPixelUi(widget, "CaptureBubblePixelTest", screenshot_name) !=
+           ui::test::ActionResult::kFailed;
+  }
+
+  void DismissUi() override {
+    if (bubble_) {
+      bubble_->SetModel(nullptr);
+      bubble_ = nullptr;
+    }
+    IgnoreNetworkServiceCrashes();
+  }
+
+  void WaitForUserDismissal() override {
+    /* Block until the UI has been dismissed. */
+    ui_test_utils::WaitForBrowserToClose();
+    if (bubble_) {
+      bubble_->SetModel(nullptr);
+      bubble_ = nullptr;
+    }
+    IgnoreNetworkServiceCrashes();
+  }
+
+ private:
+  views::Widget* GetWidgetForScreenshot() { return bubble_->GetWidget(); }
+
+  TestingPrefServiceSimple pref_service_;
+
+  std::unique_ptr<CaptionBubbleContextViews> context_;
+  std::unique_ptr<CaptionBubbleModel> model_;
+  std::unique_ptr<LiveCaptionBubbleSettings> settings_;
+  raw_ptr<CaptionBubble> bubble_;
+};
+
+// Test that calls ShowUi("default").
+IN_PROC_BROWSER_TEST_F(CaptionBubbleBrowserTest, InvokeUi_default) {
+  ShowAndVerifyUi();
+}
+
+}  // namespace
+}  // namespace captions
diff --git a/components/omnibox/browser/autocomplete_grouper_sections.cc b/components/omnibox/browser/autocomplete_grouper_sections.cc
index 268b6be..f8f5583 100644
--- a/components/omnibox/browser/autocomplete_grouper_sections.cc
+++ b/components/omnibox/browser/autocomplete_grouper_sections.cc
@@ -10,8 +10,13 @@
 
 #include "base/containers/contains.h"
 #include "base/dcheck_is_on.h"
+#include "base/debug/dump_without_crashing.h"
+#include "base/notreached.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
 #include "components/omnibox/browser/autocomplete_grouper_groups.h"
 #include "components/omnibox/browser/autocomplete_match.h"
+#include "components/omnibox/browser/autocomplete_match_type.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/common/omnibox_feature_configs.h"
 #include "third_party/omnibox_proto/groups.pb.h"
@@ -108,6 +113,61 @@
     : Section(limit, std::move(groups), group_configs, side_type) {}
 
 void ZpsSection::InitFromMatches(ACMatches& matches) {
+  // Ensure matches are sorted in the order of their potential containing
+  // groups. E.g., if `groups_ = {group 1, group 2}, matches that can be added
+  // to group 1 must appear before those that can only be added to group 2.
+  size_t last_group_index = 0;
+  for (const auto& match : matches) {
+    auto group_itr = FindGroup(match);
+    if (group_itr == groups_.end()) {
+      continue;
+    }
+    size_t current_group_index = std::distance(groups_.begin(), group_itr);
+    if (current_group_index < last_group_index) {
+      const std::string match_type =
+          AutocompleteMatchType::ToString(match.type);
+      const std::string match_group_id =
+          omnibox::GroupId_Name(match.suggestion_group_id.value());
+      const std::string match_relevance = base::NumberToString(match.relevance);
+      const std::string group_description = base::JoinString(
+          [&]() {
+            std::vector<std::string> transformed;
+            std::ranges::transform(
+                group_itr->group_id_limits_and_counts(),
+                std::back_inserter(transformed), [](const auto& pair) {
+                  return omnibox::GroupId_Name(pair.first) + " (" +
+                         base::NumberToString(
+                             static_cast<int>(pair.second.limit)) +
+                         ")";
+                });
+            return transformed;
+          }(),
+          ", ");
+      SCOPED_CRASH_KEY_STRING32("ZpsSection", "match-type", match_type);
+      SCOPED_CRASH_KEY_STRING32("ZpsSection", "match-group-id", match_group_id);
+      SCOPED_CRASH_KEY_STRING32("ZpsSection", "match-relevance",
+                                match_relevance);
+      SCOPED_CRASH_KEY_STRING32("ZpsSection", "group-description",
+                                group_description);
+      base::debug::DumpWithoutCrashing();
+#if DCHECK_IS_ON()
+      NOTREACHED() << "Match with type " << match_type << " and group id "
+                   << match_group_id << " and relevance " << match_relevance
+                   << " is not sorted correctly while being added to Group "
+                   << group_description;
+#endif  // DCHECK_IS_ON()
+    }
+    last_group_index = current_group_index;
+  }
+}
+
+ZpsSectionWithLocalHistory::ZpsSectionWithLocalHistory(
+    size_t limit,
+    Groups groups,
+    omnibox::GroupConfigMap& group_configs)
+    : ZpsSection(limit, std::move(groups), group_configs) {}
+
+void ZpsSectionWithLocalHistory::InitFromMatches(ACMatches& matches) {
   // Sort matches in the order of their potential containing groups. E.g., if
   // `groups_ = {group 1, group 2}, this sorts all matches that can be added to
   // group 1 before those that can only be added to group 2.
@@ -116,6 +176,7 @@
     // those matches won't be added to the section anyways.
     return std::distance(groups_.begin(), FindGroup(match));
   });
+  ZpsSection::InitFromMatches(matches);
 }
 
 // Number of matches that fit in the visible section of the screen.
@@ -168,8 +229,7 @@
                          show_only_search_suggestions ? 0 : 14},
                     }),
           },
-          group_configs,
-          omnibox::GroupConfig_SideType_DEFAULT_PRIMARY) {}
+          group_configs) {}
 
 void AndroidNonZPSSection::InitFromMatches(ACMatches& matches) {
   auto rich_answer_match = std::ranges::find_if(
@@ -207,8 +267,7 @@
                             {omnibox::GROUP_MOBILE_OPEN_TABS, 5},
                         }),
               },
-              group_configs,
-              omnibox::GroupConfig_SideType_DEFAULT_PRIMARY) {}
+              group_configs) {}
 
 AndroidHubNonZPSSection::AndroidHubNonZPSSection(
     omnibox::GroupConfigMap& group_configs)
@@ -236,12 +295,11 @@
                         {omnibox::GROUP_SEARCH, 5},
                     }),
           },
-          group_configs,
-          omnibox::GroupConfig_SideType_DEFAULT_PRIMARY) {}
+          group_configs) {}
 
 AndroidNTPZpsSection::AndroidNTPZpsSection(
     omnibox::GroupConfigMap& group_configs)
-    : ZpsSection(
+    : ZpsSectionWithLocalHistory(
           30,
           {
               Group(1,
@@ -328,18 +386,19 @@
 DesktopNTPZpsSection::DesktopNTPZpsSection(
     omnibox::GroupConfigMap& group_configs,
     size_t limit)
-    : ZpsSection(limit,
-                 {
-                     Group(8,
-                           {
-                               {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, 8},
-                           }),
-                     Group(8,
-                           {
-                               {omnibox::GROUP_TRENDS, 8},
-                           }),
-                 },
-                 group_configs) {}
+    : ZpsSectionWithLocalHistory(
+          limit,
+          {
+              Group(8,
+                    {
+                        {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, 8},
+                    }),
+              Group(8,
+                    {
+                        {omnibox::GROUP_TRENDS, 8},
+                    }),
+          },
+          group_configs) {}
 
 DesktopNTPZpsIPHSection::DesktopNTPZpsIPHSection(
     omnibox::GroupConfigMap& group_configs)
@@ -481,8 +540,7 @@
                              contextual_search_limit},
                         }),
               },
-              group_configs,
-              omnibox::GroupConfig_SideType_DEFAULT_PRIMARY) {}
+              group_configs) {}
 
 DesktopWebZpsActionsSection::DesktopWebZpsActionsSection(
     omnibox::GroupConfigMap& group_configs)
@@ -542,8 +600,7 @@
                             {omnibox::GROUP_OTHER_NAVS, 7},
                         }),
               },
-              group_configs,
-              omnibox::GroupConfig_SideType_DEFAULT_PRIMARY) {}
+              group_configs) {}
 
 void DesktopNonZpsSection::InitFromMatches(ACMatches& matches) {
   auto& default_group = groups_[0];
@@ -605,22 +662,23 @@
 }
 
 IOSNTPZpsSection::IOSNTPZpsSection(omnibox::GroupConfigMap& group_configs)
-    : ZpsSection(26,
-                 {
-                     Group(1,
-                           {
-                               {omnibox::GROUP_MOBILE_CLIPBOARD, 1},
-                           }),
-                     Group(20,
-                           {
-                               {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, 20},
-                           }),
-                     Group(5,
-                           {
-                               {omnibox::GROUP_TRENDS, 5},
-                           }),
-                 },
-                 group_configs) {}
+    : ZpsSectionWithLocalHistory(
+          26,
+          {
+              Group(1,
+                    {
+                        {omnibox::GROUP_MOBILE_CLIPBOARD, 1},
+                    }),
+              Group(20,
+                    {
+                        {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST, 20},
+                    }),
+              Group(5,
+                    {
+                        {omnibox::GROUP_TRENDS, 5},
+                    }),
+          },
+          group_configs) {}
 
 IOSSRPZpsSection::IOSSRPZpsSection(omnibox::GroupConfigMap& group_configs)
     : ZpsSectionWithMVTiles(
@@ -693,23 +751,24 @@
     size_t trends_count,
     size_t total_count,
     omnibox::GroupConfigMap& group_configs)
-    : ZpsSection(total_count,
-                 {
-                     Group(1,
-                           {
-                               {omnibox::GROUP_MOBILE_CLIPBOARD, 1},
-                           }),
-                     Group(total_count - trends_count - 1,
-                           {
-                               {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST,
-                                total_count - trends_count - 1},
-                           }),
-                     Group(trends_count,
-                           {
-                               {omnibox::GROUP_TRENDS, trends_count},
-                           }),
-                 },
-                 group_configs) {}
+    : ZpsSectionWithLocalHistory(
+          total_count,
+          {
+              Group(1,
+                    {
+                        {omnibox::GROUP_MOBILE_CLIPBOARD, 1},
+                    }),
+              Group(total_count - trends_count - 1,
+                    {
+                        {omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST,
+                         total_count - trends_count - 1},
+                    }),
+              Group(trends_count,
+                    {
+                        {omnibox::GROUP_TRENDS, trends_count},
+                    }),
+          },
+          group_configs) {}
 
 IOSIpadSRPZpsSection::IOSIpadSRPZpsSection(
     size_t total_count,
diff --git a/components/omnibox/browser/autocomplete_grouper_sections.h b/components/omnibox/browser/autocomplete_grouper_sections.h
index b70354147..51bae80 100644
--- a/components/omnibox/browser/autocomplete_grouper_sections.h
+++ b/components/omnibox/browser/autocomplete_grouper_sections.h
@@ -26,7 +26,8 @@
   explicit Section(size_t limit,
                    Groups groups,
                    omnibox::GroupConfigMap& group_configs,
-                   omnibox::GroupConfig_SideType side_type);
+                   omnibox::GroupConfig_SideType side_type =
+                       omnibox::GroupConfig_SideType_DEFAULT_PRIMARY);
   virtual ~Section();
   // Returns `matches` ranked and culled according to `sections`. All `matches`
   // should have `suggestion_group_id` set and be sorted by relevance.
@@ -55,9 +56,8 @@
   omnibox::GroupConfig_SideType side_type_;
 };
 
-// Base section for ZPS limits and grouping. Ensures that matches with higher
-// relevance scores do not fill up the section if others with lower scores are
-// expected to be placed earlier based on their `Group`'s position.
+// Base section for ZPS limits and grouping. Asserts that matches are sorted by
+// their `Group`s position.
 class ZpsSection : public Section {
  public:
   ZpsSection(size_t limit,
@@ -69,6 +69,21 @@
   void InitFromMatches(ACMatches& matches) override;
 };
 
+// Base section for ZPS limits and grouping where local history zero-prefix
+// suggestions are enabled. Sorts the matches by their `Group`s position to
+// ensure zero-prefix suggestions from local history backfill remote
+// personalized zero-prefix suggestions.
+// TODO(crbug.com/409810808): Find a more general solution for accommodating
+// local history backfill and remove this class.
+class ZpsSectionWithLocalHistory : public ZpsSection {
+ protected:
+  explicit ZpsSectionWithLocalHistory(size_t limit,
+                                      Groups groups,
+                                      omnibox::GroupConfigMap& group_configs);
+  // Section:
+  void InitFromMatches(ACMatches& matches) override;
+};
+
 // A ZpsSection that automatically counts all MV Tiles as one suggestion when
 // applying the total limit.
 class ZpsSectionWithMVTiles : public ZpsSection {
@@ -121,7 +136,7 @@
 //  - up to 1 clipboard suggestion.
 //  - up to 15 personalized suggestions.
 //  - up to 5 trending search suggestions.
-class AndroidNTPZpsSection : public ZpsSection {
+class AndroidNTPZpsSection : public ZpsSectionWithLocalHistory {
  public:
   explicit AndroidNTPZpsSection(omnibox::GroupConfigMap& group_configs);
 };
@@ -155,7 +170,7 @@
 // suggestion being the IPH).
 //  - up to 8 personalized suggestions.
 //  - up to 8 trending search suggestions.
-class DesktopNTPZpsSection : public ZpsSection {
+class DesktopNTPZpsSection : public ZpsSectionWithLocalHistory {
  public:
   explicit DesktopNTPZpsSection(omnibox::GroupConfigMap& group_configs,
                                 size_t limit);
@@ -217,10 +232,6 @@
 //  - up to `limit` page related or personalized search suggestions.
 //  - up to `contextual_action_limit` contextual search action suggestions.
 //  - up to `contextual_search_limit` contextual search suggestions.
-// TODO(crbug.com/409810808): Extending `ZpsSection` would reorder the matches
-// demoting contextual search suggestions in `ZpsSection::InitFromMatches()`.
-// This is not the desired behavior as those matches should take precedence over
-// the other search suggestions, despite visually appearing after them.
 class DesktopWebSearchZpsSection : public Section {
  public:
   explicit DesktopWebSearchZpsSection(omnibox::GroupConfigMap& group_configs,
@@ -276,7 +287,7 @@
 //  - up to 1 clipboard suggestion.
 //  - up to `psuggest_count` personalized suggestions.
 //  - up to `max_trending_queries` trending suggestions.
-class IOSNTPZpsSection : public ZpsSection {
+class IOSNTPZpsSection : public ZpsSectionWithLocalHistory {
  public:
   explicit IOSNTPZpsSection(omnibox::GroupConfigMap& group_configs);
 };
@@ -318,7 +329,7 @@
 // - up to 10 suggestions total.
 //  - up to 1 clipboard suggestion.
 //  - up to 10 personalized suggestions.
-class IOSIpadNTPZpsSection : public ZpsSection {
+class IOSIpadNTPZpsSection : public ZpsSectionWithLocalHistory {
  public:
   explicit IOSIpadNTPZpsSection(size_t trends_count,
                                 size_t total_count,
diff --git a/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc b/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc
index cf9fe19..f043f54 100644
--- a/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc
+++ b/components/omnibox/browser/autocomplete_grouper_sections_unittest.cc
@@ -82,43 +82,43 @@
   test({CreateMatch(1, omnibox::GROUP_SEARCH)}, {});
 }
 
-// Tests rules for ZpsSection.
-TEST(AutocompleteGrouperGroupsTest, ZpsSection) {
-  class TestZpsSection : public ZpsSection {
+// Tests rules for Section.
+TEST(AutocompleteGrouperGroupsTest, Section) {
+  class TestSection : public Section {
    public:
     // Up to 2 items of the following types.
-    explicit TestZpsSection(omnibox::GroupConfigMap& group_configs)
-        : ZpsSection(
-              2,
-              {
-                  Group(1,
-                        {
-                            {omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX, 1},
-                        }),
-                  Group(1,
-                        {
-                            {omnibox::GROUP_MOBILE_CLIPBOARD, 1},
-                        }),
-                  Group(1,
-                        {
-                            {omnibox::GROUP_MOBILE_MOST_VISITED, 1},
-                        }),
-                  Group(1,
-                        {
-                            {omnibox::GROUP_VISITED_DOC_RELATED, 1},
-                        }),
-                  Group(1,
-                        {
-                            {omnibox::GROUP_RELATED_QUERIES, 1},
-                        }),
-              },
-              group_configs) {}
+    explicit TestSection(omnibox::GroupConfigMap& group_configs)
+        : Section(2,
+                  {
+                      Group(1,
+                            {
+                                {omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX, 1},
+                            }),
+                      Group(1,
+                            {
+                                {omnibox::GROUP_MOBILE_CLIPBOARD, 1},
+                            }),
+                      Group(1,
+                            {
+                                {omnibox::GROUP_MOBILE_MOST_VISITED, 1},
+                            }),
+                      Group(1,
+                            {
+                                {omnibox::GROUP_VISITED_DOC_RELATED, 1},
+                            }),
+                      Group(1,
+                            {
+                                {omnibox::GROUP_RELATED_QUERIES, 1},
+                            }),
+                  },
+                  group_configs,
+                  omnibox::GroupConfig_SideType_DEFAULT_PRIMARY) {}
   };
 
   auto test = [](ACMatches matches, std::vector<int> expected_relevances) {
     PSections sections;
     omnibox::GroupConfigMap group_configs;
-    sections.push_back(std::make_unique<TestZpsSection>(group_configs));
+    sections.push_back(std::make_unique<TestSection>(group_configs));
     auto out_matches = Section::GroupMatches(std::move(sections), matches);
     VerifyMatches(out_matches, expected_relevances);
   };
@@ -135,7 +135,7 @@
             CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
             CreateMatch(1, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
         },
-        {2, 3});
+        {5, 6});
   }
 }
 
@@ -165,11 +165,10 @@
         {98});
   }
   {
-    SCOPED_TRACE(
-        "Matches should be ranked by group, not relevance or add order.");
+    SCOPED_TRACE("Personalized suggestions get precedence over trending ones");
     test(
         {
-            // `GROUP_TRENDS` matches come 2rd and should not be added.
+            // `GROUP_TRENDS` matches are more relevant but will not be added.
             CreateMatch(90, omnibox::GROUP_TRENDS),
             CreateMatch(89, omnibox::GROUP_TRENDS),
             CreateMatch(88, omnibox::GROUP_TRENDS),
@@ -180,8 +179,7 @@
             CreateMatch(83, omnibox::GROUP_TRENDS),
             CreateMatch(82, omnibox::GROUP_TRENDS),
             CreateMatch(81, omnibox::GROUP_TRENDS),
-            // `GROUP_PERSONALIZED_ZERO_SUGGEST` matches come 1st and should be
-            // added.
+            // `GROUP_PERSONALIZED_ZERO_SUGGEST` matches should be added.
             CreateMatch(80, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(79, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(78, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
@@ -192,17 +190,6 @@
             CreateMatch(73, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(72, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(71, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            // `GROUP_PREVIOUS_SEARCH_RELATED` matches should not be added.
-            CreateMatch(70, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(69, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(68, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(67, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(66, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(65, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(64, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(63, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(62, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(61, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
         },
         {
             80,
@@ -246,8 +233,7 @@
     test(
         {
             // `GROUP_TRENDS` matches should be added up to the remaining
-            // section limit
-            // (3).
+            // section limit (3).
             CreateMatch(90, omnibox::GROUP_TRENDS),
             CreateMatch(89, omnibox::GROUP_TRENDS),
             CreateMatch(88, omnibox::GROUP_TRENDS),
@@ -259,18 +245,18 @@
             CreateMatch(82, omnibox::GROUP_TRENDS),
             CreateMatch(81, omnibox::GROUP_TRENDS),
             // `GROUP_PERSONALIZED_ZERO_SUGGEST` matches should all be added.
-            CreateMatch(80, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(79, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(78, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(77, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(76, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(75, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(74, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
         },
         {
-            80,
-            79,
             78,
             77,
             76,
+            75,
+            74,
             90,
             89,
             88,
@@ -308,11 +294,10 @@
         {98});
   }
   {
-    SCOPED_TRACE(
-        "Matches should be ranked by group, not relevance or add order.");
+    SCOPED_TRACE("Personalized suggestions get precedence over trending ones");
     test(
         {
-            // `GROUP_TRENDS` matches come 2nd and should not be added.
+            // `GROUP_TRENDS` matches are more relevant but will not be added.
             CreateMatch(90, omnibox::GROUP_TRENDS),
             CreateMatch(89, omnibox::GROUP_TRENDS),
             CreateMatch(88, omnibox::GROUP_TRENDS),
@@ -323,8 +308,7 @@
             CreateMatch(83, omnibox::GROUP_TRENDS),
             CreateMatch(82, omnibox::GROUP_TRENDS),
             CreateMatch(81, omnibox::GROUP_TRENDS),
-            // `GROUP_PERSONALIZED_ZERO_SUGGEST` matches come 1st and should be
-            // added.
+            // `GROUP_PERSONALIZED_ZERO_SUGGEST` matches should be added.
             CreateMatch(80, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(79, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(78, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
@@ -392,8 +376,7 @@
     test(
         {
             // `GROUP_TRENDS` matches should be added up to the remaining
-            // section limit
-            // (2).
+            // section limit (2).
             CreateMatch(90, omnibox::GROUP_TRENDS),
             CreateMatch(89, omnibox::GROUP_TRENDS),
             CreateMatch(88, omnibox::GROUP_TRENDS),
@@ -774,7 +757,6 @@
   }
   {
     SCOPED_TRACE("Android/ZPS with extra searches.");
-    // Verify that the Clipboard suggestion is retained on top.
     test(
         {
             CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
@@ -799,9 +781,12 @@
   }
   {
     SCOPED_TRACE("Android/ZPS with Clipboard entries.");
-    // Verify that the Clipboard suggestion is retained on top.
+    // Verify that up to one Clipboard suggestion is retained on top.
     test(
         {
+            CreateMatch(200, omnibox::GROUP_MOBILE_CLIPBOARD),
+            CreateMatch(199, omnibox::GROUP_MOBILE_CLIPBOARD),
+            CreateMatch(198, omnibox::GROUP_MOBILE_CLIPBOARD),
             CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
@@ -819,18 +804,17 @@
             CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(3, omnibox::GROUP_MOBILE_CLIPBOARD),
-            // Bogus, repetitive, only one allowed.
-            CreateMatch(2, omnibox::GROUP_MOBILE_CLIPBOARD),
-            CreateMatch(1, omnibox::GROUP_MOBILE_CLIPBOARD),
         },
-        {3, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87});
+        {200, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87});
   }
   {
     SCOPED_TRACE("Android/ZPS with Search Ready Omnibox.");
-    // Verify that the Clipboard suggestion is retained on top.
+    // Verify that up to one SRO suggestion is retained on top.
     test(
         {
+            CreateMatch(200, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
+            CreateMatch(199, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
+            CreateMatch(198, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
             CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
@@ -848,16 +832,12 @@
             CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            // Not allowed.
-            CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
-            CreateMatch(1, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
-            CreateMatch(0, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
         },
-        {2, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87});
+        {200, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87});
   }
   {
     SCOPED_TRACE("Android/ZPS on SRP with recent searches only.");
-    // Verify that the Clipboard suggestion is retained on top.
+    // Verify that recent searches are shown up to the section limit.
     test(
         {
             CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
@@ -877,15 +857,17 @@
             CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
         },
-        {2, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87});
+        {100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86});
   }
   {
     SCOPED_TRACE("Android/ZPS with MV Tiles.");
-    // Verify that the Clipboard suggestion is retained on top.
+    // Verify that the MV suggestions are not allowed.
     test(
         {
+            CreateMatch(300, omnibox::GROUP_MOBILE_MOST_VISITED),
+            CreateMatch(299, omnibox::GROUP_MOBILE_MOST_VISITED),
+            CreateMatch(298, omnibox::GROUP_MOBILE_MOST_VISITED),
             CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
@@ -903,82 +885,43 @@
             CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            // Not allowed.
-            CreateMatch(4, omnibox::GROUP_MOBILE_MOST_VISITED),
-            CreateMatch(3, omnibox::GROUP_MOBILE_MOST_VISITED),
-            CreateMatch(2, omnibox::GROUP_MOBILE_MOST_VISITED),
         },
         {100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86});
   }
   {
     SCOPED_TRACE("Android/ZPS with multiple auxiliary suggestions.");
-    // Verify that the Clipboard suggestion is retained on top.
     test(
         {
-            CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            // Up to one SRO should be shown first.
+            CreateMatch(300, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
+            CreateMatch(299, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
+            // Up to one Clipboard should be shown after SRO.
+            CreateMatch(298, omnibox::GROUP_MOBILE_CLIPBOARD),
+            CreateMatch(297, omnibox::GROUP_MOBILE_CLIPBOARD),
+            // MV Tiles are not allowed.
+            CreateMatch(296, omnibox::GROUP_MOBILE_MOST_VISITED),
+            CreateMatch(295, omnibox::GROUP_MOBILE_MOST_VISITED),
+            // Previous Search Related and recent searches should be shown up to
+            // the remaining section limit.
+            CreateMatch(100, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
             CreateMatch(99, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(98, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
             CreateMatch(97, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(96, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
             CreateMatch(95, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(94, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
             CreateMatch(93, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
             CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(91, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(91, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(89, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(87, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(87, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(85, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            // SRO should always be shown first, despite low relevance.
-            // Only one item permitted.
-            CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
-            CreateMatch(1, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
-            // Clipboard should always be shown after SRO, if both are present.
-            // Only one item permitted.
-            CreateMatch(20, omnibox::GROUP_MOBILE_CLIPBOARD),
-            CreateMatch(19, omnibox::GROUP_MOBILE_CLIPBOARD),
-            // MV Tiles should always be on the third position if both SRO and
-            // Clipboard are present.
-            // Currently only one item is permitted.
-            CreateMatch(40, omnibox::GROUP_MOBILE_MOST_VISITED),
-            CreateMatch(39, omnibox::GROUP_MOBILE_MOST_VISITED),
         },
-        // Observe that PERSONALIZED_ZERO_SUGGEST and VISITED_DOC suggestions
-        // are grouped together. VISITED_DOC_RELATED are prioritized over the
-        // PERSONALIZED_ZERO_SUGGEST because these are more context relevant.
-        {2, 20, 99, 97, 95, 93, 91, 89, 87, 85, 100, 98, 96, 94, 92});
-  }
-  {
-    SCOPED_TRACE("No Inspire Me content shown in the core ZPS content");
-    test(
-        {
-            CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(99, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(97, omnibox::GROUP_RELATED_QUERIES),
-            CreateMatch(96, omnibox::GROUP_TRENDS),
-            CreateMatch(95, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(93, omnibox::GROUP_RELATED_QUERIES),
-            CreateMatch(92, omnibox::GROUP_TRENDS),
-            CreateMatch(91, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(89, omnibox::GROUP_RELATED_QUERIES),
-            CreateMatch(88, omnibox::GROUP_TRENDS),
-            CreateMatch(87, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(85, omnibox::GROUP_RELATED_QUERIES),
-            CreateMatch(84, omnibox::GROUP_TRENDS),
-            // Auxiliary suggestions.
-            CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
-            CreateMatch(3, omnibox::GROUP_MOBILE_CLIPBOARD),
-            // Not allowed.
-            CreateMatch(4, omnibox::GROUP_MOBILE_MOST_VISITED),
-        },
-        {2, 3, 99, 95, 91, 87, 100, 98, 94, 90, 86});
+        {300, 298, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88});
   }
 }
 
@@ -998,7 +941,6 @@
   }
   {
     SCOPED_TRACE("Android/ZPS with extra searches.");
-    // Verify that the Clipboard suggestion is retained on top.
     test(
         {
             CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
@@ -1023,9 +965,12 @@
   }
   {
     SCOPED_TRACE("Android/ZPS with Clipboard entries.");
-    // Verify that the Clipboard suggestion is retained on top.
+    // Verify that up to one Clipboard suggestion is retained on top.
     test(
         {
+            CreateMatch(200, omnibox::GROUP_MOBILE_CLIPBOARD),
+            CreateMatch(199, omnibox::GROUP_MOBILE_CLIPBOARD),
+            CreateMatch(198, omnibox::GROUP_MOBILE_CLIPBOARD),
             CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
@@ -1043,18 +988,17 @@
             CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(3, omnibox::GROUP_MOBILE_CLIPBOARD),
-            // Bogus, repetitive, only one allowed.
-            CreateMatch(2, omnibox::GROUP_MOBILE_CLIPBOARD),
-            CreateMatch(1, omnibox::GROUP_MOBILE_CLIPBOARD),
         },
-        {3, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87});
+        {200, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87});
   }
   {
     SCOPED_TRACE("Android/ZPS with Search Ready Omnibox.");
-    // Verify that the Clipboard suggestion is retained on top.
+    // Verify that up to one SRO suggestion is retained on top.
     test(
         {
+            CreateMatch(200, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
+            CreateMatch(199, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
+            CreateMatch(198, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
             CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
@@ -1072,44 +1016,18 @@
             CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
-            // Bogus, repetitive, only one allowed.
-            CreateMatch(1, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
-            CreateMatch(0, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
         },
-        {2, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87});
-  }
-  {
-    SCOPED_TRACE("Android/ZPS on Web with recent searches only.");
-    // Verify that the Clipboard suggestion is retained on top.
-    test(
-        {
-            CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(97, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(95, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(93, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(91, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(87, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
-        },
-        {2, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87});
+        {200, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87});
   }
   {
     SCOPED_TRACE("Android/ZPS with MV Tiles.");
-    // Verify that the Clipboard suggestion is retained on top.
+    // Verify that the MV suggestions are retained on top.
     test(
         {
+            // Slotted in horizontal render group, taking up 1 row.
+            CreateMatch(300, omnibox::GROUP_MOBILE_MOST_VISITED),
+            CreateMatch(299, omnibox::GROUP_MOBILE_MOST_VISITED),
+            CreateMatch(298, omnibox::GROUP_MOBILE_MOST_VISITED),
             CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(99, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
@@ -1127,81 +1045,46 @@
             CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            // Slotted in horizontal render group.
-            CreateMatch(4, omnibox::GROUP_MOBILE_MOST_VISITED),
-            CreateMatch(3, omnibox::GROUP_MOBILE_MOST_VISITED),
-            CreateMatch(2, omnibox::GROUP_MOBILE_MOST_VISITED),
         },
-        {4, 3, 2, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87});
+        {300, 299, 298, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88,
+         87});
   }
   {
     SCOPED_TRACE("Android/ZPS with multiple auxiliary suggestions.");
-    // Verify that the Clipboard suggestion is retained on top.
     test(
         {
-            CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            // Up to one SRO should be shown first.
+            CreateMatch(300, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
+            CreateMatch(299, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
+            // Up to one Clipboard should be shown after SRO.
+            CreateMatch(298, omnibox::GROUP_MOBILE_CLIPBOARD),
+            CreateMatch(297, omnibox::GROUP_MOBILE_CLIPBOARD),
+            CreateMatch(297, omnibox::GROUP_MOBILE_CLIPBOARD),
+            // MV Tiles are slotted in horizontal render group, taking up 1
+            // row.
+            CreateMatch(296, omnibox::GROUP_MOBILE_MOST_VISITED),
+            CreateMatch(295, omnibox::GROUP_MOBILE_MOST_VISITED),
+            // Visited Doc Related and recent searches should be shown up to
+            // the remaining section limit.
+            CreateMatch(100, omnibox::GROUP_VISITED_DOC_RELATED),
             CreateMatch(99, omnibox::GROUP_VISITED_DOC_RELATED),
-            CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(98, omnibox::GROUP_VISITED_DOC_RELATED),
             CreateMatch(97, omnibox::GROUP_VISITED_DOC_RELATED),
-            CreateMatch(96, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(96, omnibox::GROUP_VISITED_DOC_RELATED),
             CreateMatch(95, omnibox::GROUP_VISITED_DOC_RELATED),
-            CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
+            CreateMatch(94, omnibox::GROUP_VISITED_DOC_RELATED),
             CreateMatch(93, omnibox::GROUP_VISITED_DOC_RELATED),
             CreateMatch(92, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(91, omnibox::GROUP_VISITED_DOC_RELATED),
+            CreateMatch(91, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(89, omnibox::GROUP_VISITED_DOC_RELATED),
+            CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(88, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(87, omnibox::GROUP_VISITED_DOC_RELATED),
+            CreateMatch(87, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(85, omnibox::GROUP_VISITED_DOC_RELATED),
+            CreateMatch(85, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(84, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            // SRO should always be shown first, despite low relevance.
-            // Only one item permitted.
-            CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
-            CreateMatch(1, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
-            // Clipboard should always be shown after SRO, if both are present.
-            // Only one item permitted.
-            CreateMatch(20, omnibox::GROUP_MOBILE_CLIPBOARD),
-            CreateMatch(19, omnibox::GROUP_MOBILE_CLIPBOARD),
-            // MV Tiles should always be on the third position if both SRO and
-            // Clipboard are present.
-            // Slotted in horizontal render group.
-            CreateMatch(40, omnibox::GROUP_MOBILE_MOST_VISITED),
-            CreateMatch(39, omnibox::GROUP_MOBILE_MOST_VISITED),
         },
-        // Observe that PERSONALIZED_ZERO_SUGGEST and VISITED_DOC suggestions
-        // are grouped together. VISITED_DOC_RELATED are prioritized over the
-        // PERSONALIZED_ZERO_SUGGEST because these are more context relevant.
-        {2, 20, 40, 39, 99, 97, 95, 93, 91, 89, 87, 85, 100, 98, 96, 94});
-  }
-  {
-    SCOPED_TRACE("No Inspire Me content shown in the core ZPS content");
-    test(
-        {
-            CreateMatch(100, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(99, omnibox::GROUP_VISITED_DOC_RELATED),
-            CreateMatch(98, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(97, omnibox::GROUP_RELATED_QUERIES),
-            CreateMatch(96, omnibox::GROUP_TRENDS),
-            CreateMatch(95, omnibox::GROUP_VISITED_DOC_RELATED),
-            CreateMatch(94, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(93, omnibox::GROUP_RELATED_QUERIES),
-            CreateMatch(92, omnibox::GROUP_TRENDS),
-            CreateMatch(91, omnibox::GROUP_VISITED_DOC_RELATED),
-            CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(89, omnibox::GROUP_RELATED_QUERIES),
-            CreateMatch(88, omnibox::GROUP_TRENDS),
-            CreateMatch(87, omnibox::GROUP_VISITED_DOC_RELATED),
-            CreateMatch(86, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
-            CreateMatch(85, omnibox::GROUP_RELATED_QUERIES),
-            CreateMatch(84, omnibox::GROUP_TRENDS),
-            // Auxiliary suggestions.
-            CreateMatch(2, omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX),
-            CreateMatch(3, omnibox::GROUP_MOBILE_CLIPBOARD),
-            CreateMatch(4, omnibox::GROUP_MOBILE_MOST_VISITED),
-        },
-        {2, 3, 4, 99, 95, 91, 87, 100, 98, 94, 90, 86});
+        {300, 298, 296, 295, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89});
   }
 }
 
@@ -1742,31 +1625,31 @@
   {
     SCOPED_TRACE(
         "Given 12 srp zps matches, the group should respect the search "
-        "suggestion limit as well as show them first in the suggestion list.");
+        "suggestion limit");
     test(
         {
-            CreateMatch(100, omnibox::GROUP_MOST_VISITED),
-            CreateMatch(99, omnibox::GROUP_MOST_VISITED),
-            CreateMatch(98, omnibox::GROUP_MOST_VISITED),
-            CreateMatch(97, omnibox::GROUP_MOST_VISITED),
-            CreateMatch(96, omnibox::GROUP_MOST_VISITED),
-            CreateMatch(95, omnibox::GROUP_MOST_VISITED),
-            CreateMatch(94, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(93, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(92, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(91, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(90, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
-            CreateMatch(89, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(100, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(99, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(98, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(97, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(96, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(95, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
+            CreateMatch(94, omnibox::GROUP_MOST_VISITED),
+            CreateMatch(93, omnibox::GROUP_MOST_VISITED),
+            CreateMatch(92, omnibox::GROUP_MOST_VISITED),
+            CreateMatch(91, omnibox::GROUP_MOST_VISITED),
+            CreateMatch(89, omnibox::GROUP_MOST_VISITED),
+            CreateMatch(88, omnibox::GROUP_MOST_VISITED),
         },
-        {94, 93, 92, 91, 100, 99, 98, 97});
+        {100, 99, 98, 97, 94, 93, 92, 91});
   }
   {
     SCOPED_TRACE(
-        "Given 12 srp zps matches, if there aren't enough serach suggestions, "
-        "backfill with max_url_suggestions suggestions");
+        "Given 12 srp zps matches, the group should respect the url suggestion "
+        "limit");
     test(
         {
-            CreateMatch(100, omnibox::GROUP_MOST_VISITED),
+            CreateMatch(100, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
             CreateMatch(99, omnibox::GROUP_MOST_VISITED),
             CreateMatch(98, omnibox::GROUP_MOST_VISITED),
             CreateMatch(97, omnibox::GROUP_MOST_VISITED),
@@ -1774,12 +1657,12 @@
             CreateMatch(95, omnibox::GROUP_MOST_VISITED),
             CreateMatch(94, omnibox::GROUP_MOST_VISITED),
             CreateMatch(93, omnibox::GROUP_MOST_VISITED),
+            CreateMatch(92, omnibox::GROUP_MOST_VISITED),
             CreateMatch(91, omnibox::GROUP_MOST_VISITED),
             CreateMatch(90, omnibox::GROUP_MOST_VISITED),
             CreateMatch(89, omnibox::GROUP_MOST_VISITED),
-            CreateMatch(88, omnibox::GROUP_PREVIOUS_SEARCH_RELATED),
         },
-        {88, 100, 99, 98, 97});
+        {100, 99, 98, 97, 96});
   }
 }
 
@@ -1800,7 +1683,8 @@
     // Max 4 url suggestions.
     sections.push_back(
         std::make_unique<DesktopWebURLZpsSection>(group_configs, 4u));
-    // Max 4 search suggestions.
+    // Max 4 suggestions, with an upper limit of 4 contextual search
+    // suggestions and no contextual actions.
     sections.push_back(std::make_unique<DesktopWebSearchZpsSection>(
         group_configs, /*limit=*/4u, /*contextual_action_limit=*/0u,
         /*contextual_search_limit=*/4u));
@@ -1859,15 +1743,16 @@
     // Max 3 suggestions, with an upper limit of 3 url suggestions.
     sections.push_back(
         std::make_unique<DesktopWebURLZpsSection>(group_configs, 3u));
-    // Max 3 suggestions, with an upper limit of 3 search suggestions.
+    // Max 3 suggestions, with an upper limit of 3 contextual search
+    // suggestions and one contextual action.
     sections.push_back(std::make_unique<DesktopWebSearchZpsSection>(
-        group_configs, /*limit=*/4u, /*contextual_action_limit=*/1u,
+        group_configs, /*limit=*/3u, /*contextual_action_limit=*/1u,
         /*contextual_search_limit=*/3u));
     auto out_matches = Section::GroupMatches(std::move(sections), matches);
     VerifyMatches(out_matches, expected_relevances);
   };
   {
-    SCOPED_TRACE("ZPS action matches group after contextual search matches");
+    SCOPED_TRACE("ZPS action matches group before contextual search matches");
     test(
         {
             CreateMatch(300, omnibox::GROUP_CONTEXTUAL_SEARCH_ACTION),
@@ -1887,8 +1772,8 @@
             CreateMatch(90, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
             CreateMatch(89, omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST),
         },
-        // 3 URLs, 1 other search, 1 action, 2 contextual searches.
-        {100, 99, 98, 94, 300, 200, 199});
+        // 3 URLs, 1 action, 2 contextual searches.
+        {100, 99, 98, 300, 200, 199});
   }
 }
 
diff --git a/components/omnibox/browser/autocomplete_result_unittest.cc b/components/omnibox/browser/autocomplete_result_unittest.cc
index 54683c2..c3fbe08 100644
--- a/components/omnibox/browser/autocomplete_result_unittest.cc
+++ b/components/omnibox/browser/autocomplete_result_unittest.cc
@@ -286,9 +286,15 @@
 void AutocompleteResultTest::AssertResultMatches(
     const AutocompleteResult& result,
     base::span<const TestData> expected) {
-  ASSERT_EQ(expected.size(), result.size());
-  for (size_t i = 0; i < expected.size(); ++i)
-    AssertMatch(*(result.begin() + i), expected[i], i);
+  std::vector<int> relevances = {};
+  std::ranges::transform(
+      expected, std::back_inserter(relevances),
+      [](const auto& test_data) { return test_data.relevance; });
+  std::vector<int> expected_relevances = {};
+  std::ranges::transform(
+      result, std::back_inserter(expected_relevances),
+      [&](const AutocompleteMatch& match) { return match.relevance; });
+  EXPECT_THAT(relevances, testing::ElementsAreArray(expected_relevances));
 }
 
 void AutocompleteResultTest::AssertMatch(AutocompleteMatch match,
@@ -2580,24 +2586,9 @@
   scoped_config.Get().max_url_suggestions = 4U;
   scoped_config.Get().max_search_suggestions = 4U;
 
-  const auto group1 = omnibox::GROUP_MOST_VISITED;
-  const auto group2 = omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST;
-  TestData data[] = {
-      {0, 1, 500, false, {}, AutocompleteMatchType::HISTORY_URL, group1},
-      {1, 1, 490, false, {}, AutocompleteMatchType::HISTORY_URL, group1},
-      {2, 1, 480, false, {}, AutocompleteMatchType::HISTORY_URL, group1},
-      {3, 1, 470, false, {}, AutocompleteMatchType::HISTORY_URL, group1},
-      {4, 1, 460, false, {}, AutocompleteMatchType::HISTORY_URL, group1},
-      {5, 1, 450, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
-      {6, 1, 440, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
-      {7, 1, 430, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
-      {8, 1, 420, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
-      {9, 1, 420, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
-  };
-  ACMatches matches;
-  PopulateAutocompleteMatches(data, std::size(data), &matches);
-
   // Suggestion groups have the omnibox::SECTION_DEFAULT by default.
+  const auto group1 = omnibox::GROUP_PERSONALIZED_ZERO_SUGGEST;
+  const auto group2 = omnibox::GROUP_MOST_VISITED;
   omnibox::GroupConfigMap suggestion_groups_map;
   suggestion_groups_map[group1];
   suggestion_groups_map[group2];
@@ -2613,23 +2604,38 @@
   {
     SCOPED_TRACE("Query from omnibox in srp");
 
+    TestData data[] = {
+        {0, 1, 500, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+        {1, 1, 490, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+        {2, 1, 480, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+        {3, 1, 470, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+        {4, 1, 460, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+        {5, 1, 450, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {6, 1, 440, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {7, 1, 430, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {8, 1, 420, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {9, 1, 410, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+    };
+    ACMatches matches;
+    PopulateAutocompleteMatches(data, std::size(data), &matches);
+
     AutocompleteResult result;
     result.MergeSuggestionGroupsMap(suggestion_groups_map);
     result.AppendMatches(matches);
     result.SortAndCull(omnibox_srp_zps_input, &template_url_service(),
                        triggered_feature_service());
 
-    // There should be 8 total suggestions, 4 from the group2 and 4 from group1.
-    // Group 1 should follow group 2 since this is a search results page.
+    // There should be 8 total suggestions, 4 from the group 1 and 4 from group
+    // 2. Group 2 should follow group 1 since this is a search results page.
     const std::array<TestData, 8> expected_data{{
-        {5, 1, 450, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
-        {6, 1, 440, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
-        {7, 1, 430, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
-        {8, 1, 420, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
-        {0, 1, 500, false, {}, AutocompleteMatchType::HISTORY_URL, group1},
-        {1, 1, 490, false, {}, AutocompleteMatchType::HISTORY_URL, group1},
-        {2, 1, 480, false, {}, AutocompleteMatchType::HISTORY_URL, group1},
-        {3, 1, 470, false, {}, AutocompleteMatchType::HISTORY_URL, group1},
+        {5, 1, 500, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+        {6, 1, 490, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+        {7, 1, 480, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+        {8, 1, 470, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+        {0, 1, 450, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {1, 1, 440, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {2, 1, 430, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {3, 1, 420, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
     }};
     AssertResultMatches(result, expected_data);
   }
@@ -2647,19 +2653,35 @@
 
   {
     SCOPED_TRACE("Query from web page");
+
+    TestData data[] = {
+        {0, 1, 500, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {1, 1, 490, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {2, 1, 480, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {3, 1, 470, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {4, 1, 460, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {5, 1, 450, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+        {6, 1, 440, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+        {7, 1, 430, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+        {8, 1, 420, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+        {9, 1, 410, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+    };
+    ACMatches matches;
+    PopulateAutocompleteMatches(data, std::size(data), &matches);
+
     AutocompleteResult result;
     result.MergeSuggestionGroupsMap(suggestion_groups_map);
     result.AppendMatches(matches);
     result.SortAndCull(web_zps_input, &template_url_service(),
                        triggered_feature_service());
 
-    // There should be 6 suggestions total, 2 from group1 (url) and 4 from
-    // group2 (search), since search suggestions backfill url suggestions.
+    // There should be 4 total suggestions, 2 from the group 1 and 2 from group
+    // 2. Group 1 should follow group 2 since this is a web page.
     const std::array<TestData, 4> expected_data{{
-        {0, 1, 500, false, {}, AutocompleteMatchType::HISTORY_URL, group1},
-        {1, 1, 490, false, {}, AutocompleteMatchType::HISTORY_URL, group1},
-        {5, 1, 450, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
-        {6, 1, 440, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {0, 1, 500, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {1, 1, 490, false, {}, AutocompleteMatchType::HISTORY_URL, group2},
+        {5, 1, 450, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
+        {6, 1, 440, false, {}, AutocompleteMatchType::SEARCH_HISTORY, group1},
     }};
     AssertResultMatches(result, expected_data);
   }
diff --git a/components/omnibox/browser/clipboard_provider.cc b/components/omnibox/browser/clipboard_provider.cc
index 7f7f11b..c8cf52e 100644
--- a/components/omnibox/browser/clipboard_provider.cc
+++ b/components/omnibox/browser/clipboard_provider.cc
@@ -28,6 +28,7 @@
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/browser/page_classification_functions.h"
+#include "components/omnibox/browser/suggestion_group_util.h"
 #include "components/omnibox/browser/verbatim_match.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/open_from_clipboard/clipboard_recent_content.h"
@@ -49,12 +50,6 @@
 
 const size_t kMaxClipboardSuggestionShownNumTimesSimpleSize = 20;
 
-// Clipboard suggestion is placed either in a dedicated
-// SECTION_MOBILE_CLIPBOARD, or SECTION_PERSONALIZED_ZERO_SUGGEST.
-// The score for the former is irrelevant, but for the latter we need to be
-// confident the suggestion shows up on top.
-const int kClipboardMatchRelevanceScore = 1600;
-
 bool IsMatchDeletionEnabled() {
   return base::FeatureList::IsEnabled(
       omnibox::kOmniboxRemoveSuggestionsFromClipboard);
@@ -447,7 +442,7 @@
 }
 
 AutocompleteMatch ClipboardProvider::NewBlankURLMatch() {
-  AutocompleteMatch match(this, kClipboardMatchRelevanceScore,
+  AutocompleteMatch match(this, omnibox::kClipboardMatchZeroSuggestRelevance,
                           IsMatchDeletionEnabled(),
                           AutocompleteMatchType::CLIPBOARD_URL);
 
@@ -466,7 +461,7 @@
 }
 
 AutocompleteMatch ClipboardProvider::NewBlankTextMatch() {
-  AutocompleteMatch match(this, kClipboardMatchRelevanceScore,
+  AutocompleteMatch match(this, omnibox::kClipboardMatchZeroSuggestRelevance,
                           IsMatchDeletionEnabled(),
                           AutocompleteMatchType::CLIPBOARD_TEXT);
   // Any path leading here should first verify whether
@@ -495,7 +490,7 @@
 }
 
 AutocompleteMatch ClipboardProvider::NewBlankImageMatch() {
-  AutocompleteMatch match(this, kClipboardMatchRelevanceScore,
+  AutocompleteMatch match(this, omnibox::kClipboardMatchZeroSuggestRelevance,
                           IsMatchDeletionEnabled(),
                           AutocompleteMatchType::CLIPBOARD_IMAGE);
   // Any path leading here should first verify whether
diff --git a/components/omnibox/browser/contextual_search_provider.cc b/components/omnibox/browser/contextual_search_provider.cc
index 0340c02..ea48f45c 100644
--- a/components/omnibox/browser/contextual_search_provider.cc
+++ b/components/omnibox/browser/contextual_search_provider.cc
@@ -36,6 +36,7 @@
 #include "components/omnibox/browser/page_classification_functions.h"
 #include "components/omnibox/browser/remote_suggestions_service.h"
 #include "components/omnibox/browser/search_suggestion_parser.h"
+#include "components/omnibox/browser/suggestion_group_util.h"
 #include "components/omnibox/browser/zero_suggest_provider.h"
 #include "components/omnibox/common/omnibox_feature_configs.h"
 #include "components/omnibox/common/omnibox_features.h"
@@ -53,15 +54,8 @@
 
 namespace {
 
-// Relevance for pedal-like action matches to be provided when not in keyword
-// mode and input is empty.
-constexpr int kAdvertActionRelevance = 10000;
-
 // The internal default verbatim match relevance.
-constexpr int kDefaultMatchRelevance = 1500;
-
-// Relevance value to use if it was not set explicitly by the server.
-constexpr int kDefaultSuggestResultRelevance = 100;
+constexpr int kDefaultVerbatimMatchRelevance = 1500;
 
 // Populates |results| with the response if it can be successfully parsed for
 // |input|. Returns true if the response can be successfully parsed.
@@ -82,7 +76,7 @@
 
   return SearchSuggestionParser::ParseSuggestResults(
       *response_data, input, client->GetSchemeClassifier(),
-      /*default_result_relevance=*/kDefaultSuggestResultRelevance,
+      /*default_result_relevance=*/omnibox::kDefaultRemoteZeroSuggestRelevance,
       /*is_keyword_result=*/true, results);
 }
 
@@ -309,8 +303,8 @@
 void ContextualSearchProvider::AddPageSearchActionMatches(
     const AutocompleteInput& input) {
   // These matches are effectively pedals that don't require any query matching.
-  AutocompleteMatch match(this, kAdvertActionRelevance, false,
-                          AutocompleteMatchType::PEDAL);
+  AutocompleteMatch match(this, omnibox::kContextualActionZeroSuggestRelevance,
+                          false, AutocompleteMatchType::PEDAL);
   match.contents_class = {{0, ACMatchClassification::NONE}};
   match.transition = ui::PAGE_TRANSITION_GENERATED;
   match.suggest_type = omnibox::SuggestType::TYPE_NATIVE_CHROME;
@@ -341,7 +335,7 @@
   const TemplateURL* template_url = GetKeywordTemplateURL();
   std::u16string text = base::CollapseWhitespace(input.text(), false);
 
-  AutocompleteMatch match(this, kDefaultMatchRelevance, false,
+  AutocompleteMatch match(this, kDefaultVerbatimMatchRelevance, false,
                           AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED);
   if (text.empty()) {
     // Inert/static keyword mode helper text match for empty input. This match
@@ -369,7 +363,7 @@
         /*subtypes=*/{omnibox::SUBTYPE_CONTEXTUAL_SEARCH},
         /*from_keyword=*/true,
         /*navigational_intent=*/omnibox::NAV_INTENT_NONE,
-        /*relevance=*/kDefaultMatchRelevance,
+        /*relevance=*/kDefaultVerbatimMatchRelevance,
         /*relevance_from_server=*/false,
         /*input_text=*/text);
     match = CreateSearchSuggestion(
diff --git a/components/omnibox/browser/featured_search_provider.cc b/components/omnibox/browser/featured_search_provider.cc
index 64969c3..5dcf8e5 100644
--- a/components/omnibox/browser/featured_search_provider.cc
+++ b/components/omnibox/browser/featured_search_provider.cc
@@ -28,6 +28,7 @@
 #include "components/omnibox/browser/keyword_provider.h"
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/browser/omnibox_prefs.h"
+#include "components/omnibox/browser/suggestion_group_util.h"
 #include "components/omnibox/common/omnibox_feature_configs.h"
 #include "components/optimization_guide/core/optimization_guide_features.h"
 #include "components/prefs/pref_service.h"
@@ -451,7 +452,7 @@
       /*matched_term=*/u"@gemini",
       /*iph_link_text=*/u"",
       /*iph_link_url=*/{},
-      /*relevance=*/kIPHRelevance,
+      /*relevance=*/omnibox::kIPHZeroSuggestRelevance,
       /*deletable=*/true);
 }
 
@@ -498,7 +499,7 @@
               /*matched_term=*/u"",
               /*iph_link_text=*/u"",
               /*iph_link_url=*/{},
-              /*relevance=*/kIPHRelevance,
+              /*relevance=*/omnibox::kIPHZeroSuggestRelevance,
               /*deletable=*/true);
 }
 
@@ -584,7 +585,7 @@
               /*matched_term=*/u"@history",
               /*iph_link_text=*/u"",
               /*iph_link_url=*/{},
-              /*relevance=*/kIPHRelevance,
+              /*relevance=*/omnibox::kIPHZeroSuggestRelevance,
               /*deletable=*/true);
 }
 
@@ -604,6 +605,6 @@
       /*matched_term=*/u"@history",
       /*iph_link_text=*/u"",
       /*iph_link_url=*/{},
-      /*relevance=*/kIPHRelevance,
+      /*relevance=*/omnibox::kIPHZeroSuggestRelevance,
       /*deletable=*/true);
 }
diff --git a/components/omnibox/browser/local_history_zero_suggest_provider.cc b/components/omnibox/browser/local_history_zero_suggest_provider.cc
index bf4a103..e01a895 100644
--- a/components/omnibox/browser/local_history_zero_suggest_provider.cc
+++ b/components/omnibox/browser/local_history_zero_suggest_provider.cc
@@ -36,6 +36,7 @@
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/omnibox/browser/omnibox_prefs.h"
 #include "components/omnibox/browser/page_classification_functions.h"
+#include "components/omnibox/browser/suggestion_group_util.h"
 #include "components/omnibox/browser/zero_suggest_provider.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/search/search.h"
@@ -211,8 +212,7 @@
       "Omnibox.LocalHistoryZeroSuggest.SearchTermsExtractionTimeV2",
       db_query_timer.Elapsed());
 
-  int relevance =
-      OmniboxFieldTrial::kLocalHistoryZeroSuggestRelevanceScore.Get();
+  int relevance = omnibox::kLocalHistoryZeroSuggestRelevance;
   for (const auto& result : results) {
     SearchSuggestionParser::SuggestResult suggestion(
         /*suggestion=*/result->normalized_term,
diff --git a/components/omnibox/browser/local_history_zero_suggest_provider_unittest.cc b/components/omnibox/browser/local_history_zero_suggest_provider_unittest.cc
index 31c0565..c6e6a2b 100644
--- a/components/omnibox/browser/local_history_zero_suggest_provider_unittest.cc
+++ b/components/omnibox/browser/local_history_zero_suggest_provider_unittest.cc
@@ -28,6 +28,7 @@
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
 #include "components/omnibox/browser/autocomplete_result.h"
 #include "components/omnibox/browser/fake_autocomplete_provider_client.h"
+#include "components/omnibox/browser/suggestion_group_util.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/search_engines/search_engines_test_util.h"
 #include "components/search_engines/template_url.h"
@@ -38,7 +39,6 @@
 
 using base::Time;
 using metrics::OmniboxEventProto;
-using OmniboxFieldTrial::kLocalHistoryZeroSuggestRelevanceScore;
 
 namespace {
 
@@ -244,8 +244,7 @@
       "Omnibox.LocalHistoryZeroSuggest.SearchTermsExtractionTimeV2", 0);
 
   StartProviderAndWaitUntilDone();
-  ExpectMatches(
-      {{"hello world", kLocalHistoryZeroSuggestRelevanceScore.Get()}});
+  ExpectMatches({{"hello world", omnibox::kLocalHistoryZeroSuggestRelevance}});
 
   // Following histograms should be logged when zero-prefix suggestions are
   // allowed and the keyword search terms database is queried.
@@ -281,8 +280,7 @@
       .WillRepeatedly(testing::Return(false));
 
   StartProviderAndWaitUntilDone();
-  ExpectMatches(
-      {{"hello world", kLocalHistoryZeroSuggestRelevanceScore.Get()}});
+  ExpectMatches({{"hello world", omnibox::kLocalHistoryZeroSuggestRelevance}});
 }
 
 // Tests that suggestions are allowed in the eligibile entry points.
@@ -299,7 +297,7 @@
 
     // Local history zero-prefix suggestions are enabled by default.
     ExpectMatches(
-        {{"hello world", kLocalHistoryZeroSuggestRelevanceScore.Get()}});
+        {{"hello world", omnibox::kLocalHistoryZeroSuggestRelevance}});
   }
   {
     // Disable local history zero-prefix suggestions beyond NTP.
@@ -334,7 +332,7 @@
 #endif
     // Local history zero-prefix suggestions are enabled for on-focus SRP.
     ExpectMatches(
-        {{"hello world", kLocalHistoryZeroSuggestRelevanceScore.Get()}});
+        {{"hello world", omnibox::kLocalHistoryZeroSuggestRelevance}});
   }
 }
 
@@ -350,8 +348,7 @@
   });
 
   StartProviderAndWaitUntilDone();
-  ExpectMatches(
-      {{"hello world", kLocalHistoryZeroSuggestRelevanceScore.Get()}});
+  ExpectMatches({{"hello world", omnibox::kLocalHistoryZeroSuggestRelevance}});
 
   template_url_service->SetUserSelectedDefaultSearchProvider(
       other_search_provider);
@@ -381,8 +378,8 @@
 
   StartProviderAndWaitUntilDone();
   ExpectMatches(
-      {{"سلام دنیا", kLocalHistoryZeroSuggestRelevanceScore.Get()},
-       {"hello world", kLocalHistoryZeroSuggestRelevanceScore.Get() - 1}});
+      {{"سلام دنیا", omnibox::kLocalHistoryZeroSuggestRelevance},
+       {"hello world", omnibox::kLocalHistoryZeroSuggestRelevance - 1}});
 }
 
 // Tests that the suggestions are ranked correctly.
@@ -408,9 +405,8 @@
   // More recent searches are ranked higher when searches are just as frequent.
   StartProviderAndWaitUntilDone();
   ExpectMatches(
-      {{"more recent search", kLocalHistoryZeroSuggestRelevanceScore.Get()},
-       {"less recent search",
-        kLocalHistoryZeroSuggestRelevanceScore.Get() - 1}});
+      {{"more recent search", omnibox::kLocalHistoryZeroSuggestRelevance},
+       {"less recent search", omnibox::kLocalHistoryZeroSuggestRelevance - 1}});
 
   // More frequent searches are ranked higher when searches are nearly as old.
   LoadURLs({
@@ -421,9 +417,8 @@
 
   StartProviderAndWaitUntilDone();
   ExpectMatches(
-      {{"less recent search", kLocalHistoryZeroSuggestRelevanceScore.Get()},
-       {"more recent search",
-        kLocalHistoryZeroSuggestRelevanceScore.Get() - 1}});
+      {{"less recent search", omnibox::kLocalHistoryZeroSuggestRelevance},
+       {"more recent search", omnibox::kLocalHistoryZeroSuggestRelevance - 1}});
 }
 
 // Tests that the provider supports deletion of matches.
@@ -446,9 +441,9 @@
   });
 
   StartProviderAndWaitUntilDone();
-  ExpectMatches({{"hello world", kLocalHistoryZeroSuggestRelevanceScore.Get()},
-                 {"not to be deleted",
-                  kLocalHistoryZeroSuggestRelevanceScore.Get() - 1}});
+  ExpectMatches(
+      {{"hello world", omnibox::kLocalHistoryZeroSuggestRelevance},
+       {"not to be deleted", omnibox::kLocalHistoryZeroSuggestRelevance - 1}});
 
   // The keyword search terms database should be queried for the search terms
   // submitted to the default search provider.
@@ -467,12 +462,12 @@
   // Make sure the deletion takes effect immediately in the provider before the
   // history service asynchronously performs the deletion or even before the
   // provider is started again.
-  ExpectMatches({{"not to be deleted",
-                  kLocalHistoryZeroSuggestRelevanceScore.Get() - 1}});
+  ExpectMatches(
+      {{"not to be deleted", omnibox::kLocalHistoryZeroSuggestRelevance - 1}});
 
   StartProviderAndWaitUntilDone();
   ExpectMatches(
-      {{"not to be deleted", kLocalHistoryZeroSuggestRelevanceScore.Get()}});
+      {{"not to be deleted", omnibox::kLocalHistoryZeroSuggestRelevance}});
 
   // Wait until the history service performs the deletion.
   history::BlockUntilHistoryProcessesPendingRequests(
@@ -485,7 +480,7 @@
 
   StartProviderAndWaitUntilDone();
   ExpectMatches(
-      {{"not to be deleted", kLocalHistoryZeroSuggestRelevanceScore.Get()}});
+      {{"not to be deleted", omnibox::kLocalHistoryZeroSuggestRelevance}});
 
   history::URLDatabase* url_db =
       client_->GetHistoryService()->InMemoryDatabase();
diff --git a/components/omnibox/browser/most_visited_sites_provider.cc b/components/omnibox/browser/most_visited_sites_provider.cc
index 6915ef0..d042f4e 100644
--- a/components/omnibox/browser/most_visited_sites_provider.cc
+++ b/components/omnibox/browser/most_visited_sites_provider.cc
@@ -22,6 +22,7 @@
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/autocomplete_match_classification.h"
 #include "components/omnibox/browser/page_classification_functions.h"
+#include "components/omnibox/browser/suggestion_group_util.h"
 #include "components/omnibox/browser/tab_matcher.h"
 #include "components/omnibox/browser/zero_suggest_provider.h"
 #include "components/omnibox/common/omnibox_feature_configs.h"
@@ -35,22 +36,6 @@
 #include "url/gurl.h"
 
 namespace {
-// The relevance score for suggest tiles represented as a single tiling match.
-// Suggest tiles are placed in a dedicated SECTION_MOBILE_MOST_VISITED
-// making its relative relevance score not important.
-constexpr const int kMostVisitedTilesAggregateRelevance = 1;
-
-// The relevance score for suggest tiles represented as individual matches.
-// Repeatable Queries are recognized as searches, and may get merged to higher
-// ranking search suggestions listed below the carousel.
-constexpr const int kMostVisitedTilesIndividualHighRelevance = 1600;
-// Matches known to be off-screen by default are listed as low-relevance.
-// If we have additional AutocompleteMatches listed below the MV carousel
-// pointing to the same destination, we want the tiles to be deduplicated to
-// these matches.
-constexpr const int kMostVisitedTilesIndividualLowRelevance = 100;
-// Index of the last high-relevance tile.
-constexpr const int kLastHighRelevanceIndividualTile = 4;
 
 constexpr const int kMaxRecordedTileIndex = 15;
 
@@ -156,7 +141,10 @@
   replacements.ClearQuery();
 
   TemplateURLService* const url_service = client->GetTemplateURLService();
-  int relevance = kMostVisitedTilesIndividualHighRelevance;
+  int relevance =
+      omnibox::IsSearchResultsPage(input.current_page_classification())
+          ? omnibox::kMostVisitedTilesZeroSuggestLowRelevance
+          : omnibox::kMostVisitedTilesZeroSuggestHighRelevance;
   for (const auto& url : urls) {
     GURL stripped_url = StripURL(client, url.url, replacements);
     // Skip the match if the following is true:
@@ -206,7 +194,7 @@
           omnibox::kMostVisitedTilesHorizontalRenderGroup)) {
     auto* const url_service = client->GetTemplateURLService();
     auto* const dse = url_service->GetDefaultSearchProvider();
-    int relevance = kMostVisitedTilesIndividualHighRelevance;
+    int relevance = omnibox::kMostVisitedTilesZeroSuggestHighRelevance;
     for (const auto& tile : container) {
       // TODO(crbug.com/40279214): pass this information from History layer via
       // history::MostVisitedURL.
@@ -243,19 +231,12 @@
       }
       matches.emplace_back(std::move(match));
 
-      // On phones, we fully expose a fixed number of matches. Matches beyond
-      // that number are partially or fully concealed by design. Drop relevance
-      // for these matches.
-      if (matches.size() == kLastHighRelevanceIndividualTile &&
-          device_form_factor == ui::DEVICE_FORM_FACTOR_PHONE) {
-        relevance = kMostVisitedTilesIndividualLowRelevance;
-      }
       --relevance;
     }
   } else {
     AutocompleteMatch match =
         BuildMatch(provider, client, std::u16string(), GURL(),
-                   kMostVisitedTilesAggregateRelevance,
+                   omnibox::kMostVisitedTilesZeroSuggestHighRelevance,
                    AutocompleteMatchType::TILE_NAVSUGGEST);
 
     match.suggest_tiles.reserve(container.size());
diff --git a/components/omnibox/browser/most_visited_sites_provider_unittest.cc b/components/omnibox/browser/most_visited_sites_provider_unittest.cc
index 4a32087..716e203 100644
--- a/components/omnibox/browser/most_visited_sites_provider_unittest.cc
+++ b/components/omnibox/browser/most_visited_sites_provider_unittest.cc
@@ -17,6 +17,7 @@
 #include "components/omnibox/browser/autocomplete_enums.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
 #include "components/omnibox/browser/fake_autocomplete_provider_client.h"
+#include "components/omnibox/browser/suggestion_group_util.h"
 #include "components/omnibox/browser/test_scheme_classifier.h"
 #include "components/omnibox/common/omnibox_feature_configs.h"
 #include "components/omnibox/common/omnibox_features.h"
@@ -257,7 +258,7 @@
   } else if (ui_type == ExpectedUiType::kIndividualTiles) {
     ASSERT_EQ(data.size(), NumMostVisitedMatches())
         << "Unexpected number of TILE matches";
-    int expected_relevance = 1600;  // kMostVisitedTilesIndividualHighRelevance
+    int expected_relevance = omnibox::kMostVisitedTilesZeroSuggestHighRelevance;
     for (const auto& match : result) {
       if (data[match_index].is_search) {
         EXPECT_EQ(match.type, AutocompleteMatchType::TILE_REPEATABLE_QUERY);
@@ -277,12 +278,6 @@
       EXPECT_EQ(expected_relevance, match.relevance)
           << "Invalid Match Relevance at position " << match_index;
       ++match_index;
-      // Degrade relevance of partially visible and invisible matches.
-      if (match_index == 4 &&
-          ui::GetDeviceFormFactor() ==
-              ui::DeviceFormFactor::DEVICE_FORM_FACTOR_PHONE) {
-        expected_relevance = 100;  // kMostVisitedTilesIndividualLowRelevance
-      }
       --expected_relevance;
     }
   }
@@ -300,7 +295,7 @@
   size_t match_index = 0;
   ASSERT_EQ(url_limit, NumMostVisitedMatches())
       << "Unexpected number of TILE matches";
-  int expected_relevance = 1600;  // kMostVisitedTilesIndividualHighRelevance
+  int expected_relevance = omnibox::kMostVisitedTilesZeroSuggestHighRelevance;
   for (const auto& match : result) {
     EXPECT_EQ(match.type, AutocompleteMatchType::TILE_MOST_VISITED_SITE);
     EXPECT_TRUE(match.subtypes.contains(
diff --git a/components/omnibox/browser/omnibox_field_trial.cc b/components/omnibox/browser/omnibox_field_trial.cc
index 0fe9fb9..1304e79c 100644
--- a/components/omnibox/browser/omnibox_field_trial.cc
+++ b/components/omnibox/browser/omnibox_field_trial.cc
@@ -736,13 +736,6 @@
     "ZeroSuggestCacheMaxSize",
     5);
 
-// The relevance score for remote zero-suggest ranges from 550-1400. A default
-// value of 500 places local history zero-suggest below the remote zero-suggest.
-const base::FeatureParam<int> kLocalHistoryZeroSuggestRelevanceScore(
-    &omnibox::kAdjustLocalHistoryZeroSuggestRelevanceScore,
-    "LocalHistoryZeroSuggestRelevanceScore",
-    500);
-
 bool IsZeroSuggestPrefetchingEnabled() {
   return base::FeatureList::IsEnabled(omnibox::kZeroSuggestPrefetching) ||
          base::FeatureList::IsEnabled(omnibox::kZeroSuggestPrefetchingOnSRP) ||
diff --git a/components/omnibox/browser/omnibox_field_trial.h b/components/omnibox/browser/omnibox_field_trial.h
index d9c3d963..ed1d3eef 100644
--- a/components/omnibox/browser/omnibox_field_trial.h
+++ b/components/omnibox/browser/omnibox_field_trial.h
@@ -405,9 +405,6 @@
 // Determines the maximum number of entries stored by the in-memory ZPS cache.
 extern const base::FeatureParam<int> kZeroSuggestCacheMaxSize;
 
-// Determines the relevance score for the local history zero-prefix suggestions.
-extern const base::FeatureParam<int> kLocalHistoryZeroSuggestRelevanceScore;
-
 // Returns true if any of the zero-suggest prefetching features are enabled.
 bool IsZeroSuggestPrefetchingEnabled();
 
diff --git a/components/omnibox/browser/open_tab_provider.cc b/components/omnibox/browser/open_tab_provider.cc
index a2eea59..0c28a495 100644
--- a/components/omnibox/browser/open_tab_provider.cc
+++ b/components/omnibox/browser/open_tab_provider.cc
@@ -9,6 +9,7 @@
 #include "base/i18n/case_conversion.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "components/omnibox/browser/suggestion_group_util.h"
 #if BUILDFLAG(IS_ANDROID)
 #include "components/browser_ui/util/android/url_constants.h"
 #endif
@@ -34,7 +35,6 @@
 namespace {
 
 constexpr bool is_android = !!BUILDFLAG(IS_ANDROID);
-constexpr int kOpenTabDefaultScore = 1500;
 
 int Score(const AutocompleteInput& input,
           const query_parser::QueryNodeVector& input_query_nodes,
@@ -51,7 +51,7 @@
 #endif
 
   if ((input.IsZeroSuggest() || input.text().empty()) && is_android) {
-    return kOpenTabDefaultScore +
+    return omnibox::kOpenTabMatchZeroSuggestRelevance +
            tab.last_shown_time.InSecondsFSinceUnixEpoch();
   }
   // TODO(crbug.com/40211187): The bookmark provider also uses on `query_parser`
diff --git a/components/omnibox/browser/suggestion_group_util.h b/components/omnibox/browser/suggestion_group_util.h
index 025c1045..0f7a5878e 100644
--- a/components/omnibox/browser/suggestion_group_util.h
+++ b/components/omnibox/browser/suggestion_group_util.h
@@ -10,6 +10,43 @@
 
 namespace omnibox {
 
+// Verbatim suggestion is assigned the highest relevance to ensure #1 it appears
+// at the top and #2 duplicate suggestions are merged to the verbatim suggestion
+// and not the other way around.
+inline constexpr int kVerbatimMatchZeroSuggestRelevance = 1602;
+// Clipboard suggestion is assigned the 2nd highest relevance to ensure it
+// appears after the verbatim suggestion.
+inline constexpr int kClipboardMatchZeroSuggestRelevance = 1601;
+// MostVisited suggestions on Web are assigned the 3rd highest relevance to
+// ensure they appear after the Clipboard suggestion.
+inline constexpr int kMostVisitedTilesZeroSuggestHighRelevance = 1600;
+// OpenTab suggestions are assigned the 4th highest relevance to ensure they
+// appear #1 below the MostVisited tiles and #2 above the remote zero-prefix
+// suggestions which have relevance scores between 550-1400.
+inline constexpr int kOpenTabMatchZeroSuggestRelevance = 1500;
+// Contextual action (pedal) suggestions are assigned a relevance of 1500 to
+// ensure they appear above the remote zero-prefix suggestions which have
+// relevance scores between 550-1400.
+inline constexpr int kContextualActionZeroSuggestRelevance = 1500;
+// Remote zero-prefix suggestions are assigned a default relevance of 1400 when
+// not explicitly specified by the server, ensuring consistency with their usual
+// relevance scores.
+inline constexpr int kDefaultRemoteZeroSuggestRelevance = 1400;
+// Local History zero-prefix suggestions are assigned a default relevance of 500
+// to ensure #1 they appear below and #2 are merged into (if are duplicates) the
+// remote zero-prefix suggestions which have relevance scores between 550-1400.
+inline constexpr int kLocalHistoryZeroSuggestRelevance = 500;
+// MostVisited suggestions on SRP are assigned a default relevance of 500 to
+// ensure they appear below the remote zero-prefix suggestions which have
+// relevance scores between 550-1400.
+inline constexpr int kMostVisitedTilesZeroSuggestLowRelevance = 500;
+// Unscoped Extension suggestions are assigned a default relevance of 400 to
+// ensure they appear below all other suggestions, except for IPH suggestions.
+inline constexpr int kUnscopedExtensionZeroSuggestRelevance = 400;
+// IPH suggestions get a default relevance of 300 to ensure they appear at the
+// bottom.
+inline constexpr int kIPHZeroSuggestRelevance = 300;
+
 using GroupConfigMap = std::unordered_map<GroupId, GroupConfig>;
 
 // Builds the pre-defined static groups that are useful for sorting suggestions.
diff --git a/components/omnibox/browser/zero_suggest_provider.cc b/components/omnibox/browser/zero_suggest_provider.cc
index 0c8bc96..d519866 100644
--- a/components/omnibox/browser/zero_suggest_provider.cc
+++ b/components/omnibox/browser/zero_suggest_provider.cc
@@ -33,6 +33,7 @@
 #include "components/omnibox/browser/page_classification_functions.h"
 #include "components/omnibox/browser/remote_suggestions_service.h"
 #include "components/omnibox/browser/search_suggestion_parser.h"
+#include "components/omnibox/browser/suggestion_group_util.h"
 #include "components/omnibox/browser/zero_suggest_cache_service.h"
 #include "components/omnibox/common/omnibox_feature_configs.h"
 #include "components/omnibox/common/omnibox_features.h"
@@ -119,9 +120,6 @@
       request_event);
 }
 
-// Relevance value to use if it was not set explicitly by the server.
-const int kDefaultZeroSuggestRelevance = 100;
-
 // Called in StoreRemoteResponse() and ReadStoredResponse() to determine if the
 // zero suggest cache is being used to store ZPS responses received from the
 // remote Suggest service for the given |result_type|.
@@ -168,7 +166,8 @@
 
   if (!SearchSuggestionParser::ParseSuggestResults(
           *response_data, input, client->GetSchemeClassifier(),
-          /*default_result_relevance=*/kDefaultZeroSuggestRelevance,
+          /*default_result_relevance=*/
+          omnibox::kDefaultRemoteZeroSuggestRelevance,
           /*is_keyword_result=*/false, results)) {
     return false;
   }
@@ -225,7 +224,8 @@
 
   if (!SearchSuggestionParser::ParseSuggestResults(
           *response_data, input, client->GetSchemeClassifier(),
-          /*default_result_relevance=*/kDefaultZeroSuggestRelevance,
+          /*default_result_relevance=*/
+          omnibox::kDefaultRemoteZeroSuggestRelevance,
           /*is_keyword_result=*/false, results)) {
     return false;
   }
diff --git a/components/omnibox/browser/zero_suggest_verbatim_match_provider.cc b/components/omnibox/browser/zero_suggest_verbatim_match_provider.cc
index c732fbc..5b2d695d 100644
--- a/components/omnibox/browser/zero_suggest_verbatim_match_provider.cc
+++ b/components/omnibox/browser/zero_suggest_verbatim_match_provider.cc
@@ -16,6 +16,7 @@
 #include "components/omnibox/browser/autocomplete_match_classification.h"
 #include "components/omnibox/browser/autocomplete_provider_client.h"
 #include "components/omnibox/browser/autocomplete_provider_listener.h"
+#include "components/omnibox/browser/suggestion_group_util.h"
 #include "components/omnibox/browser/verbatim_match.h"
 #include "components/omnibox/common/omnibox_features.h"
 #include "components/search_engines/template_url_service.h"
@@ -24,14 +25,6 @@
 namespace {
 constexpr bool is_android = !!BUILDFLAG(IS_ANDROID);
 
-// Verbatim Match is placed in a dedicated SECTION_MOBILE_VERBATIM.
-// While there are no other occupants of this section, the Relevance score
-// remains important, because the Verbatim Match may get de-duplicated to other,
-// higher ranking suggestions listed later on the list.
-// Keep the relevance high to ensure matching suggestions listed later are
-// merged to the Verbatim Match, not the other way around.
-const int kVerbatimMatchRelevanceScore = 1602;
-
 // Returns whether specific context is eligible for a verbatim match.
 // Only offer verbatim match on a site visit and SRP (no NTP etc).
 bool IsVerbatimMatchEligible(
@@ -123,9 +116,9 @@
   verbatim_input.set_prevent_inline_autocomplete(true);
   verbatim_input.set_allow_exact_keyword_match(false);
 
-  AutocompleteMatch match =
-      VerbatimMatchForURL(this, client_, verbatim_input, input.current_url(),
-                          std::move(page_title), kVerbatimMatchRelevanceScore);
+  AutocompleteMatch match = VerbatimMatchForURL(
+      this, client_, verbatim_input, input.current_url(), std::move(page_title),
+      omnibox::kVerbatimMatchZeroSuggestRelevance);
   // Make sure the URL is formatted the same was as most visited sites.
   auto format_types = AutocompleteMatch::GetFormatTypes(false, false);
   match.suggestion_group_id = omnibox::GROUP_MOBILE_SEARCH_READY_OMNIBOX;
diff --git a/components/omnibox/common/omnibox_features.cc b/components/omnibox/common/omnibox_features.cc
index ddda598..4008d781 100644
--- a/components/omnibox/common/omnibox_features.cc
+++ b/components/omnibox/common/omnibox_features.cc
@@ -82,13 +82,6 @@
              "DisambiguateTabMatchingForEntitySuggestions",
              ENABLED);
 
-// Used to adjust the relevance for the local history zero-prefix suggestions.
-// If enabled, the relevance is determined by this feature's companion
-// parameter, OmniboxFieldTrial::kLocalHistoryZeroSuggestRelevanceScore.
-BASE_FEATURE(kAdjustLocalHistoryZeroSuggestRelevanceScore,
-             "AdjustLocalHistoryZeroSuggestRelevanceScore",
-             DISABLED);
-
 // Enables omnibox focus as a trigger for zero-prefix suggestions on web and
 // SRP, subject to the same requirements and conditions as on-clobber
 // suggestions.
diff --git a/components/omnibox/common/omnibox_features.h b/components/omnibox/common/omnibox_features.h
index 23af711b..4bd909c 100644
--- a/components/omnibox/common/omnibox_features.h
+++ b/components/omnibox/common/omnibox_features.h
@@ -37,7 +37,6 @@
 BASE_DECLARE_FEATURE(kDisambiguateTabMatchingForEntitySuggestions);
 
 // Local history zero-prefix (aka zero-suggest) and prefix suggestions.
-BASE_DECLARE_FEATURE(kAdjustLocalHistoryZeroSuggestRelevanceScore);
 BASE_DECLARE_FEATURE(kFocusTriggersWebAndSRPZeroSuggest);
 BASE_DECLARE_FEATURE(kHideSuggestionGroupHeaders);
 BASE_DECLARE_FEATURE(kLocalHistoryZeroSuggestBeyondNTP);
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 3fe0af9..11db37b 100644
--- a/components/optimization_guide/core/model_execution/on_device_execution.cc
+++ b/components/optimization_guide/core/model_execution/on_device_execution.cc
@@ -188,7 +188,8 @@
 
   auto options = on_device_model::mojom::GenerateOptions::New();
   options->max_output_tokens = opts_.token_limits.max_output_tokens;
-  options->constraint = std::move(constraint_);
+  options->constraint = constraint_ ? std::move(constraint_)
+                                    : opts_.adapter->GetResponseConstraint();
 
   opts_.safety_checker->RunRequestChecks(
       last_message_,
diff --git a/components/optimization_guide/core/model_execution/on_device_model_feature_adapter.cc b/components/optimization_guide/core/model_execution/on_device_model_feature_adapter.cc
index 6dc8713..da52da7 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_feature_adapter.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_feature_adapter.cc
@@ -185,4 +185,20 @@
   return token_limits_;
 }
 
+on_device_model::mojom::ResponseConstraintPtr
+OnDeviceModelFeatureAdapter::GetResponseConstraint() const {
+  const auto& constraint = config_.output_config().response_constraint();
+  switch (constraint.format_case()) {
+    case proto::ResponseConstraint::kJsonSchema:
+      return on_device_model::mojom::ResponseConstraint::NewJsonSchema(
+          constraint.json_schema());
+    case proto::ResponseConstraint::kRegex:
+      return on_device_model::mojom::ResponseConstraint::NewRegex(
+          constraint.regex());
+    default:
+      // Not configured, or not supported configuration.
+      return nullptr;
+  }
+}
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/on_device_model_feature_adapter.h b/components/optimization_guide/core/model_execution/on_device_model_feature_adapter.h
index 92ce8f7..d9f9f74 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_feature_adapter.h
+++ b/components/optimization_guide/core/model_execution/on_device_model_feature_adapter.h
@@ -24,6 +24,7 @@
 #include "components/optimization_guide/core/optimization_guide_model_executor.h"
 #include "components/optimization_guide/proto/features/text_safety.pb.h"
 #include "components/optimization_guide/proto/on_device_model_execution_config.pb.h"
+#include "services/on_device_model/public/mojom/on_device_model.mojom-forward.h"
 
 namespace optimization_guide {
 
@@ -72,6 +73,9 @@
     return config_;
   }
 
+  // Get the configured response constraint, may be null.
+  on_device_model::mojom::ResponseConstraintPtr GetResponseConstraint() const;
+
  private:
   friend class base::RefCounted<OnDeviceModelFeatureAdapter>;
   ~OnDeviceModelFeatureAdapter();
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc b/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc
index b06863d..7b52335 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc
+++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller.cc
@@ -207,20 +207,6 @@
       feature, std::move(opts), std::move(execute_remote_fn), config_params);
 }
 
-// static
-void OnDeviceModelServiceController::GetEstimatedPerformanceClass(
-    scoped_refptr<OnDeviceModelServiceController> controller,
-    base::OnceCallback<void(OnDeviceModelPerformanceClass)> callback) {
-  auto* raw_controller = controller.get();
-  raw_controller->service_client_.Get()->GetEstimatedPerformanceClass(
-      base::BindOnce(&ConvertToOnDeviceModelPerformanceClass)
-          .Then(mojo::WrapCallbackWithDefaultInvokeIfNotRun(
-              std::move(callback),
-              OnDeviceModelPerformanceClass::kServiceCrash))
-          .Then(base::OnceClosure(
-              base::DoNothingWithBoundArgs(std::move(controller)))));
-}
-
 void OnDeviceModelServiceController::SetLanguageDetectionModel(
     base::optional_ref<const ModelInfo> model_info) {
   safety_client_.SetLanguageDetectionModel(model_info);
@@ -420,6 +406,14 @@
 void OnDeviceModelServiceController::Subscribe(
     mojom::ModelSubscriptionOptionsPtr opts,
     mojo::PendingRemote<mojom::ModelSubscriber> subscriber) {
+  EnsurePerformanceClassAvailable(base::BindOnce(
+      &OnDeviceModelServiceController::SubscribeInternal,
+      weak_ptr_factory_.GetWeakPtr(), std::move(opts), std::move(subscriber)));
+}
+
+void OnDeviceModelServiceController::SubscribeInternal(
+    mojom::ModelSubscriptionOptionsPtr opts,
+    mojo::PendingRemote<mojom::ModelSubscriber> subscriber) {
   auto feature = ToModelBasedCapabilityKey(opts->id);
   if (opts->mark_used && on_device_component_state_manager_) {
     on_device_component_state_manager_->OnDeviceEligibleFeatureUsed(feature);
@@ -710,4 +704,55 @@
   controller_->access_controller_->OnResponseCompleted();
 }
 
+void OnDeviceModelServiceController::EnsurePerformanceClassAvailable(
+    base::OnceClosure complete) {
+  if (!on_device_component_state_manager_ ||
+      !on_device_component_state_manager_->NeedsPerformanceClassUpdate()) {
+    std::move(complete).Run();
+    return;
+  }
+
+  if (performance_class_state_ == PerformanceClassState::kComplete) {
+    std::move(complete).Run();
+    return;
+  }
+
+  // Use unsafe because cancellation isn't needed.
+  performance_class_callbacks_.AddUnsafe(std::move(complete));
+
+  if (performance_class_state_ == PerformanceClassState::kComputing) {
+    return;
+  }
+
+  performance_class_state_ = PerformanceClassState::kComputing;
+  service_client_.Get()->GetEstimatedPerformanceClass(
+      base::BindOnce(&ConvertToOnDeviceModelPerformanceClass)
+          .Then(mojo::WrapCallbackWithDefaultInvokeIfNotRun(
+              base::BindOnce(
+                  &OnDeviceModelServiceController::PerformanceClassUpdated,
+                  base::RetainedRef(this)),
+              OnDeviceModelPerformanceClass::kServiceCrash)));
+}
+
+void OnDeviceModelServiceController::PerformanceClassUpdated(
+    OnDeviceModelPerformanceClass perf_class) {
+  base::UmaHistogramEnumeration(
+      "OptimizationGuide.ModelExecution.OnDeviceModelPerformanceClass",
+      perf_class);
+  RegisterPerformanceClassSyntheticTrial(perf_class);
+
+  auto complete = base::BindOnce(
+      [](scoped_refptr<OnDeviceModelServiceController> controller) {
+        controller->performance_class_state_ = PerformanceClassState::kComplete;
+        controller->performance_class_callbacks_.Notify();
+      },
+      base::RetainedRef(this));
+  if (on_device_component_state_manager_) {
+    on_device_component_state_manager_->DevicePerformanceClassChanged(
+        std::move(complete), perf_class);
+  } else {
+    std::move(complete).Run();
+  }
+}
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/core/model_execution/on_device_model_service_controller.h b/components/optimization_guide/core/model_execution/on_device_model_service_controller.h
index 071a44dc..c0f4e29 100644
--- a/components/optimization_guide/core/model_execution/on_device_model_service_controller.h
+++ b/components/optimization_guide/core/model_execution/on_device_model_service_controller.h
@@ -11,6 +11,7 @@
 #include <optional>
 #include <string_view>
 
+#include "base/callback_list.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/raw_ref.h"
 #include "base/memory/ref_counted.h"
@@ -103,14 +104,6 @@
       base::WeakPtr<OptimizationGuideLogger> logger,
       const std::optional<SessionConfigParams>& config_params);
 
-  // Starts the service and executes a benchmark to determine the performance
-  // class, returning the result via `callback`. The controller will be kept
-  // alive until the benchmark completes. Returns kServiceCrash if the service
-  // crashes.
-  static void GetEstimatedPerformanceClass(
-      scoped_refptr<OnDeviceModelServiceController> controller,
-      base::OnceCallback<void(OnDeviceModelPerformanceClass)> callback);
-
   // Sets the language detection model to be used by the ODM service when text
   // safety evaluation is restricted to a specific set of languages.
   void SetLanguageDetectionModel(
@@ -151,6 +144,13 @@
     receivers_.Add(this, std::move(receiver));
   }
 
+  // Ensures the performance class will be up to date and available when
+  // `complete` runs.
+  void EnsurePerformanceClassAvailable(base::OnceClosure complete);
+
+  virtual void RegisterPerformanceClassSyntheticTrial(
+      OnDeviceModelPerformanceClass perf_class) {}
+
  protected:
   ~OnDeviceModelServiceController() override;
 
@@ -365,6 +365,12 @@
   void Subscribe(mojom::ModelSubscriptionOptionsPtr opts,
                  mojo::PendingRemote<mojom::ModelSubscriber> client) override;
 
+  void SubscribeInternal(mojom::ModelSubscriptionOptionsPtr opts,
+                         mojo::PendingRemote<mojom::ModelSubscriber> client);
+
+  // Called when performance class has finished updating.
+  void PerformanceClassUpdated(OnDeviceModelPerformanceClass perf_class);
+
   // This may be null in the destructor, otherwise non-null.
   std::unique_ptr<OnDeviceModelAccessController> access_controller_;
   std::optional<OnDeviceModelMetadataLoader> model_metadata_loader_;
@@ -388,6 +394,17 @@
 
   mojo::ReceiverSet<mojom::ModelBroker> receivers_;
 
+  enum class PerformanceClassState {
+    kNotSet,
+    kComputing,
+    kComplete,
+  };
+  PerformanceClassState performance_class_state_ =
+      PerformanceClassState::kNotSet;
+
+  // Callbacks waiting for performance class to finish computing.
+  base::OnceClosureList performance_class_callbacks_;
+
   // Used to get `weak_ptr_` to self.
   base::WeakPtrFactory<OnDeviceModelServiceController> weak_ptr_factory_{this};
 };
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 c4f8f14..f6a806b0 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
@@ -66,6 +66,7 @@
 #include "services/on_device_model/public/cpp/capabilities.h"
 #include "services/on_device_model/public/cpp/service_client.h"
 #include "services/on_device_model/public/cpp/test_support/fake_service.h"
+#include "services/on_device_model/public/mojom/on_device_model.mojom.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -276,7 +277,6 @@
 TEST_F(OnDeviceModelServiceControllerTest, ScoreBeforeContext) {
   Initialize(standard_assets_);
 
-  base::HistogramTester histogram_tester;
   auto session = CreateSession();
   ASSERT_TRUE(session);
   base::test::TestFuture<std::optional<float>> score_future;
@@ -287,7 +287,6 @@
 TEST_F(OnDeviceModelServiceControllerTest, ScorePresentAfterContext) {
   Initialize(standard_assets_);
 
-  base::HistogramTester histogram_tester;
   auto session = CreateSession();
   ASSERT_TRUE(session);
 
@@ -301,7 +300,6 @@
 TEST_F(OnDeviceModelServiceControllerTest, ScoreAfterExecute) {
   Initialize(standard_assets_);
 
-  base::HistogramTester histogram_tester;
   auto session = CreateSession();
   ASSERT_TRUE(session);
 
@@ -628,7 +626,6 @@
   });
 
   // Model not yet available.
-  base::HistogramTester histogram_tester;
   auto session = CreateSession();
   EXPECT_FALSE(session);
 
@@ -679,7 +676,6 @@
   EXPECT_EQ(0ull, fake_launcher_.on_device_model_receiver_count());
 
   // Create a new session and verify it uses the new model.
-  base::HistogramTester histogram_tester;
   auto session2 = CreateSession();
   ASSERT_TRUE(session2);
   ResponseHolder response2;
@@ -2004,27 +2000,17 @@
 TEST_F(OnDeviceModelServiceControllerTest,
        ShutsDownServiceAfterPerformanceCheck) {
   Initialize(standard_assets_);
-  base::test::TestFuture<OnDeviceModelPerformanceClass> result_future;
-  OnDeviceModelServiceController::GetEstimatedPerformanceClass(
-      test_controller_, result_future.GetCallback());
-  EXPECT_EQ(OnDeviceModelPerformanceClass::kVeryHigh, result_future.Get());
+  base::HistogramTester histogram_tester;
+  base::RunLoop run_loop;
+  test_controller_->EnsurePerformanceClassAvailable(run_loop.QuitClosure());
+  run_loop.Run();
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.ModelExecution.OnDeviceModelPerformanceClass",
+      OnDeviceModelPerformanceClass::kVeryHigh, 1);
   task_environment_.RunUntilIdle();
   EXPECT_FALSE(fake_launcher_.is_service_running());
 }
 
-TEST_F(OnDeviceModelServiceControllerTest,
-       PerformanceCheckKeepsControllerAlive) {
-  Initialize(standard_assets_);
-  auto weak_controller = test_controller_->GetWeakPtr();
-  access_controller_ = nullptr;  // Avoid dangling pointer
-  base::test::TestFuture<OnDeviceModelPerformanceClass> result_future;
-  OnDeviceModelServiceController::GetEstimatedPerformanceClass(
-      std::move(test_controller_), result_future.GetCallback());
-  EXPECT_EQ(OnDeviceModelPerformanceClass::kVeryHigh, result_future.Get());
-  // Verify there wasn't something else keeping the controller alive.
-  EXPECT_FALSE(weak_controller);
-}
-
 TEST_F(OnDeviceModelServiceControllerTest, RedactedField) {
   auto config = SimpleComposeConfig();
   config.set_can_skip_text_safety(true);
@@ -3155,10 +3141,9 @@
   Initialize({.base_model = &base_model, .adaptations = {&compose_asset}});
   task_environment_.RunUntilIdle();
 
-  base::test::TestFuture<OnDeviceModelPerformanceClass> result_future;
-  OnDeviceModelServiceController::GetEstimatedPerformanceClass(
-      test_controller_, result_future.GetCallback());
-  EXPECT_EQ(OnDeviceModelPerformanceClass::kVeryHigh, result_future.Get());
+  base::RunLoop run_loop;
+  test_controller_->EnsurePerformanceClassAvailable(run_loop.QuitClosure());
+  run_loop.Run();
   task_environment_.RunUntilIdle();
 
   // Performance check sh;ould not shut down service.
@@ -3187,9 +3172,8 @@
   Initialize({.base_model = &base_model, .adaptations = {&compose_asset}});
   task_environment_.RunUntilIdle();
 
-  base::test::TestFuture<OnDeviceModelPerformanceClass> result_future;
-  OnDeviceModelServiceController::GetEstimatedPerformanceClass(
-      test_controller_, result_future.GetCallback());
+  base::RunLoop run_loop;
+  test_controller_->EnsurePerformanceClassAvailable(run_loop.QuitClosure());
 
   task_environment_.FastForwardBy(base::Seconds(1) + base::Milliseconds(1));
   task_environment_.RunUntilIdle();
@@ -3200,8 +3184,8 @@
       "OptimizationGuide.ModelExecution.OnDeviceModelValidationResult",
       OnDeviceModelValidationResult::kSuccess, 1);
 
-  EXPECT_FALSE(result_future.IsReady());
-  EXPECT_EQ(OnDeviceModelPerformanceClass::kVeryHigh, result_future.Get());
+  EXPECT_FALSE(run_loop.AnyQuitCalled());
+  run_loop.Run();
   task_environment_.RunUntilIdle();
   EXPECT_FALSE(fake_launcher_.is_service_running());
 }
@@ -3672,6 +3656,29 @@
   EXPECT_EQ(*response.value(), "Context: execute:bar max:1024\n");
 }
 
+TEST_F(OnDeviceModelServiceControllerTest,
+       BrokerCreateSessionRunsPerformanceClassCheck) {
+  base::HistogramTester histogram_tester;
+  mojo::PendingReceiver<mojom::ModelBroker> pending_broker;
+
+  ModelBrokerClient broker_client(
+      pending_broker.InitWithNewPipeAndPassRemote(),
+      CreateSessionArgs(logger_.GetWeakPtr(), FailOnRemoteFallback()));
+  base::test::TestFuture<
+      std::unique_ptr<OptimizationGuideModelExecutor::Session>>
+      session_future;
+  broker_client.CreateSession(mojom::ModelBasedCapabilityKey::kCompose,
+                              std::nullopt, session_future.GetCallback());
+
+  Initialize(standard_assets_);
+  test_controller_->BindBroker(std::move(pending_broker));
+
+  ASSERT_TRUE(session_future.Take());
+  histogram_tester.ExpectUniqueSample(
+      "OptimizationGuide.ModelExecution.OnDeviceModelPerformanceClass",
+      OnDeviceModelPerformanceClass::kVeryHigh, 1);
+}
+
 TEST_F(OnDeviceModelServiceControllerTest, Priority) {
   Initialize(standard_assets_);
 
@@ -3790,4 +3797,84 @@
   EXPECT_EQ(response_.output_token_count(), strlen("execute:foo max:1024"));
 }
 
+TEST_F(OnDeviceModelServiceControllerTest, ResponseConstraintOnExecute) {
+  Initialize(standard_assets_);
+  auto session = test_controller_->CreateSession(
+      kFeature, FailOnRemoteFallback(), logger_.GetWeakPtr(),
+      /*config_params=*/std::nullopt);
+  ASSERT_TRUE(session);
+  session->ExecuteModelWithResponseConstraint(
+      PageUrlRequest("input"),
+      on_device_model::mojom::ResponseConstraint::NewRegex("[A-Z]*"),
+      response_.GetStreamingCallback());
+  ASSERT_TRUE(response_.GetFinalStatus());
+  EXPECT_EQ(response_.value(),
+            "Constraint: regex [A-Z]*\n"
+            "Context: execute:input max:1024\n");
+}
+
+TEST_F(OnDeviceModelServiceControllerTest, ResponseConstraintConfigJson) {
+  FakeAdaptationAsset test_asset({
+      .config =
+          []() {
+            auto config = SimpleComposeConfig();
+            config.mutable_output_config()
+                ->mutable_response_constraint()
+                ->set_json_schema("{ type: \"object\"}");
+            return config;
+          }(),
+  });
+
+  Initialize({
+      .base_model = &standard_assets_.base_model,
+      .safety = &standard_assets_.safety,
+      .language = &standard_assets_.language,
+      .adaptations = {&test_asset},
+  });
+
+  auto session = test_controller_->CreateSession(
+      kFeature, FailOnRemoteFallback(), logger_.GetWeakPtr(),
+      /*config_params=*/std::nullopt);
+  ASSERT_TRUE(session);
+
+  session->ExecuteModel(PageUrlRequest("input"),
+                        response_.GetStreamingCallback());
+  ASSERT_TRUE(response_.GetFinalStatus());
+  EXPECT_EQ(response_.value(),
+            "Constraint: json { type: \"object\"}\n"
+            "Context: execute:input max:1024\n");
+}
+
+TEST_F(OnDeviceModelServiceControllerTest, ResponseConstraintConfigRegex) {
+  FakeAdaptationAsset test_asset({
+      .config =
+          []() {
+            auto config = SimpleComposeConfig();
+            config.mutable_output_config()
+                ->mutable_response_constraint()
+                ->set_regex("[A-Z]*");
+            return config;
+          }(),
+  });
+
+  Initialize({
+      .base_model = &standard_assets_.base_model,
+      .safety = &standard_assets_.safety,
+      .language = &standard_assets_.language,
+      .adaptations = {&test_asset},
+  });
+
+  auto session = test_controller_->CreateSession(
+      kFeature, FailOnRemoteFallback(), logger_.GetWeakPtr(),
+      /*config_params=*/std::nullopt);
+  ASSERT_TRUE(session);
+
+  session->ExecuteModel(PageUrlRequest("input"),
+                        response_.GetStreamingCallback());
+  ASSERT_TRUE(response_.GetFinalStatus());
+  EXPECT_EQ(response_.value(),
+            "Constraint: regex [A-Z]*\n"
+            "Context: execute:input max:1024\n");
+}
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal
index d9be8fd..448e864 160000
--- a/components/optimization_guide/internal
+++ b/components/optimization_guide/internal
@@ -1 +1 @@
-Subproject commit d9be8fda5a8b047287677a35f2b3a6111f5883be
+Subproject commit 448e864469af5010a1dbb83b15d0cbfa1dcb5f55
diff --git a/components/optimization_guide/proto/on_device_model_execution_config.proto b/components/optimization_guide/proto/on_device_model_execution_config.proto
index 82df47a..9a5a1e9d 100644
--- a/components/optimization_guide/proto/on_device_model_execution_config.proto
+++ b/components/optimization_guide/proto/on_device_model_execution_config.proto
@@ -101,6 +101,18 @@
   optional uint32 max_execute_tokens = 6;
 }
 
+// Rules that generated output must follow.
+message ResponseConstraint {
+  oneof format {
+    // A JSON Schema describing an llguidance constraint on the output. See
+    // https://github.com/guidance-ai/llguidance/blob/main/docs/json_schema.md
+    string json_schema = 1;
+    // A regex describing the llguidance constraint on the output.
+    // See https://docs.rs/regex/latest/regex/#syntax for syntax.
+    string regex = 2;
+  }
+}
+
 message OnDeviceModelExecutionOutputConfig {
   reserved 7;
 
@@ -121,6 +133,9 @@
 
   // The maximum number of tokens that can be generated as output.
   optional uint32 max_output_tokens = 6;
+
+  // The response constraint to use when generating output.
+  optional ResponseConstraint response_constraint = 8;
 }
 
 message TextSafetyFallbackConfig {
diff --git a/components/page_load_metrics/browser/metrics_navigation_throttle.cc b/components/page_load_metrics/browser/metrics_navigation_throttle.cc
index d722748..1a9f0dc 100644
--- a/components/page_load_metrics/browser/metrics_navigation_throttle.cc
+++ b/components/page_load_metrics/browser/metrics_navigation_throttle.cc
@@ -11,9 +11,10 @@
 namespace page_load_metrics {
 
 // static
-std::unique_ptr<content::NavigationThrottle> MetricsNavigationThrottle::Create(
-    content::NavigationHandle* handle) {
-  return base::WrapUnique(new MetricsNavigationThrottle(handle));
+void MetricsNavigationThrottle::CreateAndAdd(
+    content::NavigationThrottleRegistry& registry) {
+  registry.AddThrottle(
+      base::WrapUnique(new MetricsNavigationThrottle(registry)));
 }
 
 MetricsNavigationThrottle::~MetricsNavigationThrottle() = default;
@@ -23,8 +24,9 @@
   MetricsWebContentsObserver* observer =
       MetricsWebContentsObserver::FromWebContents(
           navigation_handle()->GetWebContents());
-  if (observer)
+  if (observer) {
     observer->WillStartNavigationRequest(navigation_handle());
+  }
   return content::NavigationThrottle::PROCEED;
 }
 
@@ -33,8 +35,9 @@
   MetricsWebContentsObserver* observer =
       MetricsWebContentsObserver::FromWebContents(
           navigation_handle()->GetWebContents());
-  if (observer)
+  if (observer) {
     observer->WillProcessNavigationResponse(navigation_handle());
+  }
   return content::NavigationThrottle::PROCEED;
 }
 
@@ -43,7 +46,7 @@
 }
 
 MetricsNavigationThrottle::MetricsNavigationThrottle(
-    content::NavigationHandle* handle)
-    : content::NavigationThrottle(handle) {}
+    content::NavigationThrottleRegistry& registry)
+    : content::NavigationThrottle(registry) {}
 
 }  // namespace page_load_metrics
diff --git a/components/page_load_metrics/browser/metrics_navigation_throttle.h b/components/page_load_metrics/browser/metrics_navigation_throttle.h
index fc29e03..93cdcff9 100644
--- a/components/page_load_metrics/browser/metrics_navigation_throttle.h
+++ b/components/page_load_metrics/browser/metrics_navigation_throttle.h
@@ -5,20 +5,17 @@
 #ifndef COMPONENTS_PAGE_LOAD_METRICS_BROWSER_METRICS_NAVIGATION_THROTTLE_H_
 #define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_METRICS_NAVIGATION_THROTTLE_H_
 
-#include <memory>
-
 #include "content/public/browser/navigation_throttle.h"
+#include "content/public/browser/navigation_throttle_registry.h"
 
 namespace page_load_metrics {
 
 // This class is used to forward calls to the MetricsWebContentsObserver.
 // Namely, WillStartRequest() is called on NavigationThrottles, but not on
-// WebContentsObservers. Data from the NavigationHandle accessed at this point
-// is used to obtain more reliable abort metrics (like page transition type).
+// WebContentsObservers.
 class MetricsNavigationThrottle : public content::NavigationThrottle {
  public:
-  static std::unique_ptr<content::NavigationThrottle> Create(
-      content::NavigationHandle* handle);
+  static void CreateAndAdd(content::NavigationThrottleRegistry& registry);
 
   MetricsNavigationThrottle(const MetricsNavigationThrottle&) = delete;
   MetricsNavigationThrottle& operator=(const MetricsNavigationThrottle&) =
@@ -33,7 +30,8 @@
   const char* GetNameForLogging() override;
 
  private:
-  explicit MetricsNavigationThrottle(content::NavigationHandle* handle);
+  explicit MetricsNavigationThrottle(
+      content::NavigationThrottleRegistry& registry);
 };
 
 }  // namespace page_load_metrics
diff --git a/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc b/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
index b30ced27..55dad9f2 100644
--- a/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
+++ b/components/page_load_metrics/browser/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
@@ -885,11 +885,8 @@
   // SubresourceFilterTestHarness::
   void AppendCustomNavigationThrottles(
       content::NavigationThrottleRegistry& registry) override {
-    content::NavigationHandle& navigation_handle =
-        registry.GetNavigationHandle();
-    if (navigation_handle.IsInMainFrame()) {
-      registry.AddThrottle(
-          MetricsNavigationThrottle::Create(&navigation_handle));
+    if (registry.GetNavigationHandle().IsInMainFrame()) {
+      MetricsNavigationThrottle::CreateAndAdd(registry);
     }
   }
 
diff --git a/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.cc b/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.cc
index 3349573..d379577a 100644
--- a/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.cc
+++ b/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.cc
@@ -21,10 +21,8 @@
 
 void PageLoadMetricsTestContentBrowserClient::CreateThrottlesForNavigation(
     content::NavigationThrottleRegistry& registry) {
-  content::NavigationHandle& navigation_handle = registry.GetNavigationHandle();
-  if (navigation_handle.IsInMainFrame()) {
-    registry.AddThrottle(page_load_metrics::MetricsNavigationThrottle::Create(
-        &navigation_handle));
+  if (registry.GetNavigationHandle().IsInMainFrame()) {
+    page_load_metrics::MetricsNavigationThrottle::CreateAndAdd(registry);
   }
 }
 
diff --git a/components/payments/content/android/BUILD.gn b/components/payments/content/android/BUILD.gn
index 28bc62f..9e00160 100644
--- a/components/payments/content/android/BUILD.gn
+++ b/components/payments/content/android/BUILD.gn
@@ -178,6 +178,7 @@
     "java/src/org/chromium/components/payments/PaymentHandlerNavigationThrottle.java",
     "java/src/org/chromium/components/payments/PaymentManifestDownloader.java",
     "java/src/org/chromium/components/payments/PaymentManifestParser.java",
+    "java/src/org/chromium/components/payments/PaymentManifestResolver.java",
     "java/src/org/chromium/components/payments/PaymentManifestVerifier.java",
     "java/src/org/chromium/components/payments/PaymentManifestWebDataService.java",
     "java/src/org/chromium/components/payments/PaymentNotShownError.java",
@@ -395,6 +396,7 @@
     "junit/src/org/chromium/components/payments/BrowserGlobalPaymentFlowManagerUnitTest.java",
     "junit/src/org/chromium/components/payments/DeduplicatePaymentAppsUnitTest.java",
     "junit/src/org/chromium/components/payments/PaymentDetailsUpdateConnectionTest.java",
+    "junit/src/org/chromium/components/payments/PaymentManifestResolverTest.java",
     "junit/src/org/chromium/components/payments/PaymentRequestServiceTest.java",
     "junit/src/org/chromium/components/payments/intent/WebPaymentIntentHelperTypeConverterTest.java",
   ]
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentAppFinder.java b/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentAppFinder.java
index 3c2ad97..7552348 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentAppFinder.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/AndroidPaymentAppFinder.java
@@ -36,8 +36,11 @@
  * Finds installed native Android payment apps and verifies their signatures according to the
  * payment method manifests. The manifests are located based on the payment method name, which is a
  * URL that starts with "https://" (localhosts can be "http://", however). The W3C-published non-URL
- * payment method names are exceptions: these are common payment method names that do not have a
+ * payment method names[1] are exceptions: these are common payment method names that do not have a
  * manifest and can be used by any payment app.
+ *
+ * <p>[1] <a href="https://w3c.github.io/payment-method-id/#registry">Registry of standardized
+ * payment methods</a>.
  */
 @NullMarked
 public class AndroidPaymentAppFinder implements ManifestVerifyCallback {
@@ -358,9 +361,8 @@
         // ("https://bobpay.com/personal", "https://alicepay.com/webpay")
         //
         // Manifests from all of these URLs will be downloaded for verification of app package
-        // names, versions, and signatures.
-        Set<GURL> urlMethodsForManifestDownload =
-                new HashSet<>(mMerchantRequestedUrlPaymentMethods);
+        // names, versions, signatures, and "supported_origins".
+        Set<GURL> urlMethodsForManifestDownload = new HashSet<>();
 
         // A mapping from all known payment method names to the corresponding payment apps that
         // claim to support these payment methods. Example contents:
@@ -433,8 +435,6 @@
                 methodToAppsMapping.get(defaultMethod).add(app);
 
                 if (UrlUtil.isURLValid(defaultUrlMethod)) {
-                    urlMethodsForManifestDownload.add(defaultUrlMethod);
-
                     if (!urlMethodToDefaultAppsMapping.containsKey(defaultUrlMethod)) {
                         urlMethodToDefaultAppsMapping.put(
                                 defaultUrlMethod, new HashSet<ResolveInfo>());
@@ -468,6 +468,7 @@
                         String.join(", ", supportedMethods));
             }
 
+            Set<GURL> supportedUrlMethods = new HashSet<>();
             for (String supportedMethod : supportedMethods) {
                 GURL supportedUrlMethod = new GURL(supportedMethod);
                 if (!UrlUtil.isURLValid(supportedUrlMethod)) supportedUrlMethod = null;
@@ -494,6 +495,8 @@
                     continue;
                 }
 
+                supportedUrlMethods.add(supportedUrlMethod);
+
                 if (!mMethodToSupportedAppsMapping.containsKey(supportedUrlMethod)) {
                     mMethodToSupportedAppsMapping.put(
                             supportedUrlMethod, new HashSet<ResolveInfo>());
@@ -508,6 +511,12 @@
                 urlMethodToSupportedOriginsMapping.get(supportedUrlMethod).add(appOrigin);
             }
 
+            urlMethodsForManifestDownload.addAll(
+                    PaymentManifestResolver.getManifestsToDownload(
+                            defaultUrlMethod,
+                            supportedUrlMethods,
+                            mMerchantRequestedUrlPaymentMethods));
+
             // Record the total number of payment methods that this activity `ResolveInfo app`
             // declares to support in its metadata.
             if (!TextUtils.isEmpty(defaultMethod)) supportedMethods.add(defaultMethod);
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestResolver.java b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestResolver.java
new file mode 100644
index 0000000..29b88af8
--- /dev/null
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/PaymentManifestResolver.java
@@ -0,0 +1,152 @@
+// 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.payments;
+
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.url.GURL;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Helper class for determining the set of URL payment methods whose manifests should be downloaded.
+ *
+ * <p>Websites specify the methods (URLs) they want to use in their Payment Request call.
+ *
+ * <p>Installed apps can declare both a 'default' method (URL) for their app, which acts a source of
+ * truth for its identity, as well as support for methods (URLs) from another origin.
+ *
+ * <p>For a given request and given app, there are then three possibilities - either the request is
+ * for the app itself directly (the default payment method), it is for a payment method that the app
+ * claims to support, or it has no overlap at all.
+ *
+ * <h2>Default payment methods</h2>
+ *
+ * <p>Use case: the merchant supports the app's default payment method.
+ *
+ * <p>Download:
+ *
+ * <ul>
+ *   <li>The payment method and web app manifests for the app's default payment method.
+ * </ul>
+ *
+ * <p>Verify:
+ *
+ * <ul>
+ *   <li>App identity (package name, version, signature).
+ * </ul>
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * Merchant Website                             Alice Server
+ * Supported method: https://alice.example ---&gt; https://alice.example/payment_manifest.json
+ *                                                  "default_applications"
+ *                                                             |
+ *                                                             v
+ *                                              https://alice.example/web_manifest.json
+ *                                                  "related_applications"
+ *                                                   |         |        |
+ *                                                   v         v        v
+ * Alice App &lt;----------Verify--------------- package name, version, signature
+ * Default method: https://alice.example
+ * </pre>
+ *
+ * <h2>Other payment methods</h2>
+ *
+ * <p>Use case: the merchant supports a payment method that is one of this app's other (non-default)
+ * payment methods.
+ *
+ * <p>Download:
+ *
+ * <ul>
+ *   <li>The payment method and web app manifests for the app's default payment method.
+ *   <li>The payment method manifests for the payment methods that both the merchant and the app
+ *       support.
+ * </ul>
+ *
+ * <p>Verify:
+ *
+ * <ul>
+ *   <li>App identity (package name, version, signature).
+ *   <li>The {@code "supported_origins"} field is either {@code "*"} or a list of origins that
+ *       includes the origin of the app's default payment method.
+ * </ul>
+ *
+ * <p>Example:
+ *
+ * <pre>
+ * Merchant Website                             Alice Server
+ * Supported method: https://alice.example ---&gt; https://alice.example/method_manifest.json
+ *                                              "supported_origins"
+ *                                                   |
+ *                                                   v
+ *                                              Bob Server
+ *                                              https://bob.example/web_manifest.json
+ *                                                  "related_applications"
+ *                                                   |         |        |
+ *                                                   v         v        v
+ * Bob App &lt;------------Verify--------------- package name, version, signature
+ * Default method: https://bob.example
+ * Other method: https://alice.example
+ * </pre>
+ *
+ * <p>Learn more:
+ *
+ * <ul>
+ *   <li><a href="https://web.dev/articles/android-payment-apps-developers-guide">Android payment
+ *       apps</a>.
+ *   <li><a href="https://web.dev/articles/setting-up-a-payment-method">Setting up a payment
+ *       method</a>.
+ *   <li><a href="https://w3c.github.io/payment-method-id/">Payment method identifier</a>.
+ *   <li><a href="https://w3c.github.io/payment-method-manifest/">Payment method manifest</a>.
+ * </ul>
+ */
+@NullMarked
+/* package */ class PaymentManifestResolver {
+    /**
+     * Determines the set of the URL payment methods whose manifests should be downloaded for
+     * verification of package name, version, signature, and "supported_origins" fields.
+     *
+     * @param appDefaultPaymentMethod The default payment method for the Android payment app. Leads
+     *     to the web app manifest with {@code "related_applications"}, which contains this app's
+     *     package name, version, and signature.
+     * @param appOtherPaymentMethods Other (non-default) payment methods of this Android payment
+     *     app. Leads to the payment method manifest with {@code "supported_origins"}, which can be
+     *     either {@code "*"} or a list of origins.
+     * @param merchantSupportedPaymentMethods The set of URI payment methods that the merchant has
+     *     declared in the {@code "supportedMethods"} fields in the Payment Request JavaScript API
+     *     call.
+     * @return The set of URI payment methods whose manifests should be downloaded for verification.
+     */
+    /* package */ static Set<GURL> getManifestsToDownload(
+            @Nullable GURL appDefaultPaymentMethod,
+            Set<GURL> appOtherPaymentMethods,
+            Set<GURL> merchantSupportedPaymentMethods) {
+        // Find the intersection of all payment methods supported by the app, and the payment
+        // methods requested by the website.
+        Set<GURL> manifestsToDownload = new HashSet<>(appOtherPaymentMethods);
+        manifestsToDownload.add(appDefaultPaymentMethod);
+        manifestsToDownload.retainAll(merchantSupportedPaymentMethods);
+        manifestsToDownload.removeIf(url -> !UrlUtil.isURLValid(url));
+
+        if (manifestsToDownload.isEmpty()) {
+            return manifestsToDownload;
+        }
+
+        // If the merchant requested an 'other' payment method and not the default payment method,
+        // then the default method will not be included in the intersection, but the default method
+        // still has to be downloaded to verify the app.
+        if (UrlUtil.isURLValid(appDefaultPaymentMethod)) {
+            manifestsToDownload.add(appDefaultPaymentMethod);
+        }
+
+        return manifestsToDownload;
+    }
+
+    // Do not instantiate.
+    private PaymentManifestResolver() {}
+}
diff --git a/components/payments/content/android/java/src/org/chromium/components/payments/UrlUtil.java b/components/payments/content/android/java/src/org/chromium/components/payments/UrlUtil.java
index 9a6a759..88d46b5a 100644
--- a/components/payments/content/android/java/src/org/chromium/components/payments/UrlUtil.java
+++ b/components/payments/content/android/java/src/org/chromium/components/payments/UrlUtil.java
@@ -8,6 +8,7 @@
 import org.jni_zero.NativeMethods;
 
 import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.url.GURL;
 
@@ -21,7 +22,7 @@
      * @param url The payment method name.
      * @return TRUE if given url is valid and not a relative URI.
      */
-    public static boolean isURLValid(GURL url) {
+    public static boolean isURLValid(@Nullable GURL url) {
         return url != null
                 && url.isValid()
                 && !url.getScheme().isEmpty()
diff --git a/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentManifestResolverTest.java b/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentManifestResolverTest.java
new file mode 100644
index 0000000..d4b6552
--- /dev/null
+++ b/components/payments/content/android/junit/src/org/chromium/components/payments/PaymentManifestResolverTest.java
@@ -0,0 +1,165 @@
+// 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.payments;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseRobolectricTestRunner;
+import org.chromium.base.test.util.Feature;
+import org.chromium.url.GURL;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/** Tests for determining the set of URL payment methods whose manifests should be downloaded. */
+@RunWith(BaseRobolectricTestRunner.class)
+public class PaymentManifestResolverTest {
+    /**
+     * Download the manifest for an app's default payment method that is supported by the merchant.
+     */
+    @Test
+    @Feature({"Payments"})
+    public void testDownloadDefaultPaymentMethodSupportedByMerchant() throws Exception {
+        Set<GURL> actualManifestsToDownload =
+                PaymentManifestResolver.getManifestsToDownload(
+                        /* appDefaultPaymentMethod= */ new GURL("https://alice.example"),
+                        /* appOtherPaymentMethods= */ Set.of(),
+                        /* merchantSupportedPaymentMethods= */ Set.of(
+                                new GURL("https://alice.example")));
+
+        assertEquals(Set.of(new GURL("https://alice.example")), actualManifestsToDownload);
+    }
+
+    /**
+     * Do not download the manifest for an app's default payment method that is not supported by the
+     * merchant.
+     */
+    @Test
+    @Feature({"Payments"})
+    public void testNoDownloadDefaultPaymentMethodNotSupportedByMerchant() throws Exception {
+        Set<GURL> actualManifestsToDownload =
+                PaymentManifestResolver.getManifestsToDownload(
+                        /* appDefaultPaymentMethod= */ new GURL("https://bob.example"),
+                        /* appOtherPaymentMethods= */ Set.of(),
+                        /* merchantSupportedPaymentMethods= */ Set.of(
+                                new GURL("https://alice.example")));
+
+        assertTrue(actualManifestsToDownload.isEmpty());
+    }
+
+    /**
+     * Download the manifests both for an app's default payment method (to verify package name,
+     * version, and signature) and an other (non-default) payment method, when the merchant supports
+     * only the other (non-default) payment method.
+     */
+    @Test
+    @Feature({"Payments"})
+    public void testDownloadDefaultAndOtherPaymentMethodWhenMerchantSupportsOnlyOther()
+            throws Exception {
+        Set<GURL> actualManifestsToDownload =
+                PaymentManifestResolver.getManifestsToDownload(
+                        /* appDefaultPaymentMethod= */ new GURL("https://bob.example"),
+                        /* appOtherPaymentMethods= */ Set.of(
+                                new GURL("https://alice.example"),
+                                new GURL("https://charlie.example")),
+                        /* merchantSupportedPaymentMethods= */ Set.of(
+                                new GURL("https://alice.example"),
+                                new GURL("https://david.example")));
+
+        assertEquals(
+                Set.of(new GURL("https://alice.example"), new GURL("https://bob.example")),
+                actualManifestsToDownload);
+    }
+
+    /**
+     * Do not download the manifest for an app's other (non-default) payment method that is not
+     * supported by the merchant.
+     */
+    @Test
+    @Feature({"Payments"})
+    public void testNoDownloadOtherPaymentMethodNotSupportedByMerchant() throws Exception {
+        Set<GURL> actualManifestsToDownload =
+                PaymentManifestResolver.getManifestsToDownload(
+                        /* appDefaultPaymentMethod= */ new GURL("https://bob.example"),
+                        /* appOtherPaymentMethods= */ Set.of(
+                                new GURL("https://charlie.example"),
+                                new GURL("https://david.example")),
+                        /* merchantSupportedPaymentMethods= */ Set.of(
+                                new GURL("https://alice.example"),
+                                new GURL("https://evan.example")));
+
+        assertTrue(actualManifestsToDownload.isEmpty());
+    }
+
+    /** Do not download the manifest for a null default payment method. */
+    @Test
+    @Feature({"Payments"})
+    public void testNoDownloadNullDefaultPaymentMethod() throws Exception {
+        Set<GURL> merchantSupportedPaymentMethods = new HashSet<>();
+        merchantSupportedPaymentMethods.add(new GURL("https://alice.example"));
+        merchantSupportedPaymentMethods.add(null);
+
+        Set<GURL> actualManifestsToDownload =
+                PaymentManifestResolver.getManifestsToDownload(
+                        /* appDefaultPaymentMethod= */ null,
+                        /* appOtherPaymentMethods= */ Set.of(new GURL("https://charlie.example")),
+                        merchantSupportedPaymentMethods);
+
+        assertTrue(actualManifestsToDownload.isEmpty());
+    }
+
+    /** Do not download the manifest for a default payment method that is an invalid URL. */
+    @Test
+    @Feature({"Payments"})
+    public void testNoDownloadInvalidDefaultPaymentMethod() throws Exception {
+        Set<GURL> actualManifestsToDownload =
+                PaymentManifestResolver.getManifestsToDownload(
+                        /* appDefaultPaymentMethod= */ new GURL("basic-card"),
+                        /* appOtherPaymentMethods= */ Set.of(new GURL("https://charlie.example")),
+                        /* merchantSupportedPaymentMethods= */ Set.of(
+                                new GURL("https://alice.example"), new GURL("basic-card")));
+
+        assertTrue(actualManifestsToDownload.isEmpty());
+    }
+
+    /** Do not download the manifest for a null other (non-default) payment method. */
+    @Test
+    @Feature({"Payments"})
+    public void testNoDownloadNullOtherPaymentMethod() throws Exception {
+        Set<GURL> appOtherPaymentMethods = new HashSet<>();
+        appOtherPaymentMethods.add(new GURL("https://david.example"));
+        appOtherPaymentMethods.add(null);
+        Set<GURL> merchantSupportedPaymentMethods = new HashSet<>();
+        merchantSupportedPaymentMethods.add(new GURL("https://alice.example"));
+        merchantSupportedPaymentMethods.add(null);
+
+        Set<GURL> actualManifestsToDownload =
+                PaymentManifestResolver.getManifestsToDownload(
+                        /* appDefaultPaymentMethod= */ new GURL("https://bob.example"),
+                        appOtherPaymentMethods,
+                        merchantSupportedPaymentMethods);
+
+        assertTrue(actualManifestsToDownload.isEmpty());
+    }
+
+    /** Do not download the manifest for an other (non-default) payment method that is invalid. */
+    @Test
+    @Feature({"Payments"})
+    public void testNoDownloadInvalidOtherPaymentMethod() throws Exception {
+        Set<GURL> actualManifestsToDownload =
+                PaymentManifestResolver.getManifestsToDownload(
+                        /* appDefaultPaymentMethod= */ new GURL("https://bob.example"),
+                        /* appOtherPaymentMethods= */ Set.of(
+                                new GURL("basic-card"), new GURL("https://charlie.example")),
+                        /* merchantSupportedPaymentMethods= */ Set.of(
+                                new GURL("https://alice.example"), new GURL("basic-card")));
+
+        assertTrue(actualManifestsToDownload.isEmpty());
+    }
+}
diff --git a/components/performance_manager/decorators/site_data_node_data.cc b/components/performance_manager/decorators/site_data_node_data.cc
index 9554f7ba..a80d33c 100644
--- a/components/performance_manager/decorators/site_data_node_data.cc
+++ b/components/performance_manager/decorators/site_data_node_data.cc
@@ -168,13 +168,13 @@
     return false;
   }
   CHECK(!loaded_idle_time_.is_null());
-  return heuristics.IsOutsideLoadingGracePeriod(
-             page_node_, feature_type,
-             base::TimeTicks::Now() - loaded_idle_time_) &&
+  const base::TimeTicks now = base::TimeTicks::Now();
+  return heuristics.IsOutsideLoadingGracePeriod(page_node_, feature_type,
+                                                now - loaded_idle_time_) &&
          heuristics.IsInBackground(page_node_) &&
          heuristics.IsOutsideBackgroundingGracePeriod(
              page_node_, feature_type,
-             page_node_->GetTimeSinceLastVisibilityChange());
+             now - page_node_->GetLastVisibilityChangeTime());
 }
 
 void SiteDataNodeData::MaybeNotifyBackgroundFeatureUsage(
diff --git a/components/performance_manager/graph/page_node_impl.cc b/components/performance_manager/graph/page_node_impl.cc
index ed5441f..7801cdd 100644
--- a/components/performance_manager/graph/page_node_impl.cc
+++ b/components/performance_manager/graph/page_node_impl.cc
@@ -116,9 +116,9 @@
   return is_visible_.value();
 }
 
-base::TimeDelta PageNodeImpl::GetTimeSinceLastVisibilityChange() const {
+base::TimeTicks PageNodeImpl::GetLastVisibilityChangeTime() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return base::TimeTicks::Now() - visibility_change_time_;
+  return visibility_change_time_;
 }
 
 bool PageNodeImpl::IsAudible() const {
diff --git a/components/performance_manager/graph/page_node_impl.h b/components/performance_manager/graph/page_node_impl.h
index 47387de..5db306b8 100644
--- a/components/performance_manager/graph/page_node_impl.h
+++ b/components/performance_manager/graph/page_node_impl.h
@@ -86,7 +86,7 @@
   PageType GetType() const override;
   bool IsFocused() const override;
   bool IsVisible() const override;
-  base::TimeDelta GetTimeSinceLastVisibilityChange() const override;
+  base::TimeTicks GetLastVisibilityChangeTime() const override;
   bool IsAudible() const override;
   std::optional<base::TimeDelta> GetTimeSinceLastAudibleChange() const override;
   bool HasPictureInPicture() const override;
diff --git a/components/performance_manager/graph/page_node_impl_unittest.cc b/components/performance_manager/graph/page_node_impl_unittest.cc
index 5de4972f..3c8f4e9 100644
--- a/components/performance_manager/graph/page_node_impl_unittest.cc
+++ b/components/performance_manager/graph/page_node_impl_unittest.cc
@@ -91,19 +91,19 @@
   EXPECT_EQ(0u, GraphImplOperations::GetFrameNodes(page_node.get()).size());
 }
 
-TEST_F(PageNodeImplTest, GetTimeSinceLastVisibilityChange) {
+TEST_F(PageNodeImplTest, GetLastVisibilityChangeTime) {
   MockSinglePageInSingleProcessGraph mock_graph(graph());
 
+  base::TimeTicks t0 = base::TimeTicks::Now();
   mock_graph.page->SetIsVisible(true);
   EXPECT_TRUE(mock_graph.page->IsVisible());
   AdvanceClock(base::Seconds(42));
-  EXPECT_EQ(base::Seconds(42),
-            mock_graph.page->GetTimeSinceLastVisibilityChange());
+  EXPECT_EQ(t0, mock_graph.page->GetLastVisibilityChangeTime());
 
+  base::TimeTicks t1 = base::TimeTicks::Now();
   mock_graph.page->SetIsVisible(false);
   AdvanceClock(base::Seconds(23));
-  EXPECT_EQ(base::Seconds(23),
-            mock_graph.page->GetTimeSinceLastVisibilityChange());
+  EXPECT_EQ(t1, mock_graph.page->GetLastVisibilityChangeTime());
   EXPECT_FALSE(mock_graph.page->IsVisible());
 }
 
diff --git a/components/performance_manager/metrics/page_resource_monitor.cc b/components/performance_manager/metrics/page_resource_monitor.cc
index 499768b..4b236f2 100644
--- a/components/performance_manager/metrics/page_resource_monitor.cc
+++ b/components/performance_manager/metrics/page_resource_monitor.cc
@@ -47,17 +47,19 @@
 
 PageMeasurementBackgroundState GetBackgroundStateForMeasurementPeriod(
     const PageNode* page_node,
-    base::TimeDelta time_since_last_measurement) {
-  if (page_node->GetTimeSinceLastVisibilityChange() <
-      time_since_last_measurement) {
+    base::TimeTicks now,
+    base::TimeTicks time_of_last_resource_usage) {
+  if (time_of_last_resource_usage < page_node->GetLastVisibilityChangeTime()) {
     return PageMeasurementBackgroundState::kMixedForegroundBackground;
   }
   if (page_node->IsVisible()) {
     return PageMeasurementBackgroundState::kForeground;
   }
   // Check if the page was audible for the entire measurement period.
+  const base::TimeDelta time_since_last_resource_usage =
+      now - time_of_last_resource_usage;
   if (page_node->GetTimeSinceLastAudibleChange().value_or(
-          base::TimeDelta::Max()) < time_since_last_measurement) {
+          base::TimeDelta::Max()) < time_since_last_resource_usage) {
     return PageMeasurementBackgroundState::kBackgroundMixedAudible;
   }
   if (page_node->IsAudible()) {
@@ -167,7 +169,7 @@
     auto ukm = ukm::builders::PerformanceManager_PageResourceUsage2(source_id);
     ukm.SetBackgroundState(
         static_cast<int64_t>(GetBackgroundStateForMeasurementPeriod(
-            page_node, now - time_of_last_resource_usage_)));
+            page_node, now, time_of_last_resource_usage_)));
     ukm.SetMeasurementAlgorithm(
         static_cast<int64_t>(PageMeasurementAlgorithm::kEvenSplitAndAggregate));
     // Add CPU usage, if this page included it.
diff --git a/components/performance_manager/public/graph/page_node.h b/components/performance_manager/public/graph/page_node.h
index dc3e686..a1d3450 100644
--- a/components/performance_manager/public/graph/page_node.h
+++ b/components/performance_manager/public/graph/page_node.h
@@ -111,18 +111,18 @@
   // See PageNodeObserver::OnIsVisibleChanged.
   virtual bool IsVisible() const = 0;
 
-  // Returns the time since the last visibility change. It is always well
-  // defined as the visibility property is set at node creation.
-  virtual base::TimeDelta GetTimeSinceLastVisibilityChange() const = 0;
+  // Returns the time of the last visibility change. It is always well defined
+  // as the visibility property is set at node creation.
+  virtual base::TimeTicks GetLastVisibilityChangeTime() const = 0;
 
   // Returns true if this page is currently audible, false otherwise.
   // See PageNodeObserver::OnIsAudibleChanged.
   virtual bool IsAudible() const = 0;
 
   // Returns the time since the last audible change. Unlike
-  // GetTimeSinceLastVisibilityChange(), this returns nullopt for a node which
-  // has never been audible. If a node is audible when created, it is considered
-  // to change from inaudible to audible at that point.
+  // GetLastVisibilityChangeTime(), this returns nullopt for a node which has
+  // never been audible. If a node is audible when created, it is considered to
+  // change from inaudible to audible at that point.
   virtual std::optional<base::TimeDelta> GetTimeSinceLastAudibleChange()
       const = 0;
 
@@ -295,7 +295,7 @@
 
   // Invoked when the IsVisible property changes.
   //
-  // GetTimeSinceLastVisibilityChange() will return the time since the previous
+  // GetLastVisibilityChangeTime() will return the time of the previous
   // IsVisible change. After all observers have fired it will return the time of
   // this property change.
   virtual void OnIsVisibleChanged(const PageNode* page_node) {}
diff --git a/components/performance_manager/scenario_api/BUILD.gn b/components/performance_manager/scenario_api/BUILD.gn
index 97e71414..3d426c7 100644
--- a/components/performance_manager/scenario_api/BUILD.gn
+++ b/components/performance_manager/scenario_api/BUILD.gn
@@ -18,6 +18,20 @@
   public_deps = [ "//base" ]
 }
 
+source_set("test_support") {
+  testonly = true
+
+  sources = [
+    "performance_scenario_test_support.cc",
+    "performance_scenario_test_support.h",
+  ]
+
+  deps = [
+    ":scenario_api",
+    "//testing/gtest",
+  ]
+}
+
 source_set("unit_tests") {
   testonly = true
 
@@ -28,6 +42,7 @@
 
   deps = [
     ":scenario_api",
+    ":test_support",
     "//base/test:test_support",
     "//testing/gmock",
     "//testing/gtest",
diff --git a/components/performance_manager/scenario_api/performance_scenario_observer_unittest.cc b/components/performance_manager/scenario_api/performance_scenario_observer_unittest.cc
index a8d1132f..e202d0f 100644
--- a/components/performance_manager/scenario_api/performance_scenario_observer_unittest.cc
+++ b/components/performance_manager/scenario_api/performance_scenario_observer_unittest.cc
@@ -5,17 +5,16 @@
 #include "components/performance_manager/scenario_api/performance_scenario_observer.h"
 
 #include <atomic>
-#include <optional>
+#include <memory>
 
 #include "base/barrier_closure.h"
 #include "base/containers/enum_set.h"
 #include "base/memory/read_only_shared_memory_region.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/memory/structured_shared_memory.h"
 #include "base/scoped_multi_source_observation.h"
 #include "base/test/gmock_callback_support.h"
 #include "base/test/task_environment.h"
 #include "components/performance_manager/scenario_api/performance_scenario_memory.h"
+#include "components/performance_manager/scenario_api/performance_scenario_test_support.h"
 #include "components/performance_manager/scenario_api/performance_scenarios.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -59,18 +58,12 @@
 class PerformanceScenarioObserverTest : public ::testing::Test {
  public:
   void SetUp() override {
-    ASSERT_TRUE(process_shared_memory_.has_value());
-    ASSERT_TRUE(global_shared_memory_.has_value());
+    test_helper_ = PerformanceScenarioTestHelper::CreateWithoutMapping();
+    ASSERT_TRUE(test_helper_);
   }
 
  protected:
-  // Writable shared memory regions for the scenario state.
-  std::optional<base::StructuredSharedMemory<ScenarioState>>
-      process_shared_memory_ =
-          base::StructuredSharedMemory<ScenarioState>::Create();
-  std::optional<base::StructuredSharedMemory<ScenarioState>>
-      global_shared_memory_ =
-          base::StructuredSharedMemory<ScenarioState>::Create();
+  std::unique_ptr<PerformanceScenarioTestHelper> test_helper_;
 
   base::test::TaskEnvironment task_env_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
@@ -84,8 +77,8 @@
 
   {
     ScopedReadOnlyScenarioMemory scoped_process_memory(
-        ScenarioScope::kCurrentProcess,
-        process_shared_memory_->DuplicateReadOnlyRegion());
+        ScenarioScope::kCurrentProcess, test_helper_->GetReadOnlyScenarioRegion(
+                                            ScenarioScope::kCurrentProcess));
     EXPECT_TRUE(PerformanceScenarioObserverList::GetForScope(
         ScenarioScope::kCurrentProcess));
     EXPECT_FALSE(
@@ -94,7 +87,7 @@
     {
       ScopedReadOnlyScenarioMemory scoped_global_memory(
           ScenarioScope::kGlobal,
-          global_shared_memory_->DuplicateReadOnlyRegion());
+          test_helper_->GetReadOnlyScenarioRegion(ScenarioScope::kGlobal));
       EXPECT_TRUE(PerformanceScenarioObserverList::GetForScope(
           ScenarioScope::kCurrentProcess));
       EXPECT_TRUE(
@@ -117,15 +110,16 @@
   // Update the process scenario state before creating the ObserverList, to
   // make sure the state tracking doesn't depend on the state starting at
   // kNoPageLoading.
-  process_shared_memory_->WritableRef().loading.store(
-      LoadingScenario::kFocusedPageLoading, std::memory_order_relaxed);
+  test_helper_->SetLoadingScenario(ScenarioScope::kCurrentProcess,
+                                   LoadingScenario::kFocusedPageLoading);
 
   // Map in scenario memory.
   ScopedReadOnlyScenarioMemory scoped_process_memory(
       ScenarioScope::kCurrentProcess,
-      process_shared_memory_->DuplicateReadOnlyRegion());
+      test_helper_->GetReadOnlyScenarioRegion(ScenarioScope::kCurrentProcess));
   ScopedReadOnlyScenarioMemory scoped_global_memory(
-      ScenarioScope::kGlobal, global_shared_memory_->DuplicateReadOnlyRegion());
+      ScenarioScope::kGlobal,
+      test_helper_->GetReadOnlyScenarioRegion(ScenarioScope::kGlobal));
 
   EXPECT_FALSE(CurrentScenariosMatch(ScenarioScope::kCurrentProcess,
                                      kDefaultIdleScenarios));
@@ -159,12 +153,11 @@
     input_only_observation.AddObservation(observer_list.get());
   }
 
-  // Utility function that notifies observers of a change and waits for all mock
-  // expectations to be filled. The test should invoke `task_env_.QuitClosure()`
-  // when all expected observer methods are called.
-  auto notify_and_wait_for_expectations = [&] {
+  // Utility function that waits for all mock expectations to be filled. The
+  // test should invoke `task_env_.QuitClosure()` when all expected observer
+  // methods are called.
+  auto wait_for_expectations = [&] {
     using ::testing::Mock;
-    PerformanceScenarioObserverList::NotifyAllScopes();
     task_env_.RunUntilQuit();
     EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_observer));
     EXPECT_TRUE(Mock::VerifyAndClearExpectations(&mock_idle_observer));
@@ -197,11 +190,11 @@
               OnScenarioMatchChanged(ScenarioScope::kGlobal, false))
       .WillOnce(base::test::RunClosure(quit_closure));
 
-  process_shared_memory_->WritableRef().loading.store(
-      LoadingScenario::kBackgroundPageLoading, std::memory_order_relaxed);
-  global_shared_memory_->WritableRef().loading.store(
-      LoadingScenario::kVisiblePageLoading, std::memory_order_relaxed);
-  notify_and_wait_for_expectations();
+  test_helper_->SetLoadingScenario(ScenarioScope::kCurrentProcess,
+                                   LoadingScenario::kBackgroundPageLoading);
+  test_helper_->SetLoadingScenario(ScenarioScope::kGlobal,
+                                   LoadingScenario::kVisiblePageLoading);
+  wait_for_expectations();
 
   // Toggle process scenario again without changing global scenario.
   // kBackgroundPageLoading (idle) -> kFocusedPageLoading (non-idle).
@@ -215,9 +208,9 @@
               OnScenarioMatchChanged(ScenarioScope::kCurrentProcess, false))
       .WillOnce(base::test::RunClosure(quit_closure));
 
-  process_shared_memory_->WritableRef().loading.store(
-      LoadingScenario::kFocusedPageLoading, std::memory_order_relaxed);
-  notify_and_wait_for_expectations();
+  test_helper_->SetLoadingScenario(ScenarioScope::kCurrentProcess,
+                                   LoadingScenario::kFocusedPageLoading);
+  wait_for_expectations();
 
   // Stop observing the process scenario, then toggle both scenarios again.
   //
@@ -249,11 +242,11 @@
           ScenarioScope::kCurrentProcess)
           .get());
 
-  process_shared_memory_->WritableRef().loading.store(
-      LoadingScenario::kBackgroundPageLoading, std::memory_order_relaxed);
-  global_shared_memory_->WritableRef().loading.store(
-      LoadingScenario::kNoPageLoading, std::memory_order_relaxed);
-  notify_and_wait_for_expectations();
+  test_helper_->SetLoadingScenario(ScenarioScope::kCurrentProcess,
+                                   LoadingScenario::kBackgroundPageLoading);
+  test_helper_->SetLoadingScenario(ScenarioScope::kGlobal,
+                                   LoadingScenario::kNoPageLoading);
+  wait_for_expectations();
 
   // Update global scenario from kNoPageLoading to kBackgroundPageLoading. The
   // idle observer shouldn't be notified because the new scenario is still idle.
@@ -263,9 +256,9 @@
                                        LoadingScenario::kBackgroundPageLoading))
       .WillOnce(base::test::RunClosure(task_env_.QuitClosure()));
 
-  global_shared_memory_->WritableRef().loading.store(
-      LoadingScenario::kBackgroundPageLoading, std::memory_order_relaxed);
-  notify_and_wait_for_expectations();
+  test_helper_->SetLoadingScenario(ScenarioScope::kGlobal,
+                                   LoadingScenario::kBackgroundPageLoading);
+  wait_for_expectations();
 
   // Update the global input scenario. All 3 observers will now be notified.
   quit_closure = base::BarrierClosure(3, task_env_.QuitClosure());
@@ -280,9 +273,9 @@
               OnScenarioMatchChanged(ScenarioScope::kGlobal, false))
       .WillOnce(base::test::RunClosure(quit_closure));
 
-  global_shared_memory_->WritableRef().input.store(InputScenario::kTyping,
-                                                   std::memory_order_relaxed);
-  notify_and_wait_for_expectations();
+  test_helper_->SetInputScenario(ScenarioScope::kGlobal,
+                                 InputScenario::kTyping);
+  wait_for_expectations();
 }
 
 }  // namespace
diff --git a/components/performance_manager/scenario_api/performance_scenario_test_support.cc b/components/performance_manager/scenario_api/performance_scenario_test_support.cc
new file mode 100644
index 0000000..bd7f5f3
--- /dev/null
+++ b/components/performance_manager/scenario_api/performance_scenario_test_support.cc
@@ -0,0 +1,113 @@
+// 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/performance_manager/scenario_api/performance_scenario_test_support.h"
+
+#include <atomic>
+#include <memory>
+#include <optional>
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/structured_shared_memory.h"
+#include "base/notreached.h"
+#include "components/performance_manager/scenario_api/performance_scenario_memory.h"
+#include "components/performance_manager/scenario_api/performance_scenario_observer.h"
+#include "components/performance_manager/scenario_api/performance_scenarios.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace performance_scenarios {
+
+// static
+std::unique_ptr<PerformanceScenarioTestHelper>
+PerformanceScenarioTestHelper::Create() {
+  auto test_helper = CreateWithoutMapping();
+  if (!test_helper) {
+    return nullptr;
+  }
+  auto global_region =
+      test_helper->GetReadOnlyScenarioRegion(ScenarioScope::kGlobal);
+  auto process_region =
+      test_helper->GetReadOnlyScenarioRegion(ScenarioScope::kCurrentProcess);
+  EXPECT_TRUE(global_region.IsValid());
+  EXPECT_TRUE(process_region.IsValid());
+  if (!global_region.IsValid() || !process_region.IsValid()) {
+    return nullptr;
+  }
+  test_helper->global_read_only_memory_.emplace(ScenarioScope::kGlobal,
+                                                std::move(global_region));
+  test_helper->process_read_only_memory_.emplace(ScenarioScope::kCurrentProcess,
+                                                 std::move(process_region));
+  return test_helper;
+}
+
+// static
+std::unique_ptr<PerformanceScenarioTestHelper>
+PerformanceScenarioTestHelper::CreateWithoutMapping() {
+  auto global_state = base::StructuredSharedMemory<ScenarioState>::Create();
+  auto process_state = base::StructuredSharedMemory<ScenarioState>::Create();
+  EXPECT_TRUE(global_state.has_value());
+  EXPECT_TRUE(process_state.has_value());
+  if (!global_state.has_value() || !process_state.has_value()) {
+    return nullptr;
+  }
+  return base::WrapUnique(new PerformanceScenarioTestHelper(
+      std::move(global_state.value()), std::move(process_state.value())));
+}
+
+PerformanceScenarioTestHelper::PerformanceScenarioTestHelper(
+    base::StructuredSharedMemory<ScenarioState> global_state,
+    base::StructuredSharedMemory<ScenarioState> process_state)
+    : global_state_(std::move(global_state)),
+      process_state_(std::move(process_state)) {}
+
+PerformanceScenarioTestHelper::~PerformanceScenarioTestHelper() = default;
+
+base::ReadOnlySharedMemoryRegion
+PerformanceScenarioTestHelper::GetReadOnlyScenarioRegion(
+    ScenarioScope scope) const {
+  return ScenarioStateForScope(scope).DuplicateReadOnlyRegion();
+}
+
+void PerformanceScenarioTestHelper::SetLoadingScenario(
+    ScenarioScope scope,
+    LoadingScenario scenario) {
+  ScenarioStateForScope(scope).WritableRef().loading.store(
+      scenario, std::memory_order_relaxed);
+  if (auto observer_list =
+          PerformanceScenarioObserverList::GetForScope(scope)) {
+    observer_list->NotifyIfScenarioChanged();
+  }
+}
+
+void PerformanceScenarioTestHelper::SetInputScenario(ScenarioScope scope,
+                                                     InputScenario scenario) {
+  ScenarioStateForScope(scope).WritableRef().input.store(
+      scenario, std::memory_order_relaxed);
+  if (auto observer_list =
+          PerformanceScenarioObserverList::GetForScope(scope)) {
+    observer_list->NotifyIfScenarioChanged();
+  }
+}
+
+base::StructuredSharedMemory<ScenarioState>&
+PerformanceScenarioTestHelper::ScenarioStateForScope(ScenarioScope scope) {
+  switch (scope) {
+    case ScenarioScope::kGlobal:
+      return global_state_;
+    case ScenarioScope::kCurrentProcess:
+      return process_state_;
+  }
+  NOTREACHED();
+}
+
+const base::StructuredSharedMemory<ScenarioState>&
+PerformanceScenarioTestHelper::ScenarioStateForScope(
+    ScenarioScope scope) const {
+  return const_cast<PerformanceScenarioTestHelper*>(this)
+      ->ScenarioStateForScope(scope);
+}
+
+}  // namespace performance_scenarios
diff --git a/components/performance_manager/scenario_api/performance_scenario_test_support.h b/components/performance_manager/scenario_api/performance_scenario_test_support.h
new file mode 100644
index 0000000..6dd7e5f
--- /dev/null
+++ b/components/performance_manager/scenario_api/performance_scenario_test_support.h
@@ -0,0 +1,122 @@
+// 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_PERFORMANCE_MANAGER_SCENARIO_API_PERFORMANCE_SCENARIO_TEST_SUPPORT_H_
+#define COMPONENTS_PERFORMANCE_MANAGER_SCENARIO_API_PERFORMANCE_SCENARIO_TEST_SUPPORT_H_
+
+#include <memory>
+#include <optional>
+
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/structured_shared_memory.h"
+#include "components/performance_manager/scenario_api/performance_scenario_memory.h"
+#include "components/performance_manager/scenario_api/performance_scenarios.h"
+
+namespace performance_scenarios {
+
+// Helper to create and access performance scenario state from unit tests.
+// In production the writable state is created automatically in the browser
+// process, and read-only state is created automatically in child processes when
+// they're launched.
+//
+// Usage:
+//
+// TEST(SomeTestSuite, TestWithNoScenarios) {
+//   // Observers not available.
+//   EXPECT_FALSE(PerformanceScenarioObserverList::GetForScope(
+//       ScenarioScope::kGlobal));
+//   // Scenarios all return default values.
+//   EXPECT_EQ(GetInputScenario(ScenarioScope::kGlobal).load(
+//       std::memory_order_relaxed), InputScenario::kNoInput);
+// }
+//
+// TEST(SomeTestSuite, TestWithScenarios) {
+//   auto test_helper = PerformanceScenarioTestHelper::Create();
+//   ASSERT_TRUE(test_helper);
+//
+//   // Observers are available.
+//   EXPECT_TRUE(PerformanceScenarioObserverList::GetForScope(
+//       ScenarioScope::kGlobal));
+//
+//   // Scenarios can be updated.
+//   test_helper->SetInputScenario(ScenarioScope::kGlobal,
+//       InputScenario::kTyping);
+//   EXPECT_EQ(GetInputScenario(ScenarioScope::kGlobal, InputScenario::kTyping);
+// }
+//
+// TEST(SomeTestSuite, TestWithManualMapping) {
+//   auto test_helper = PerformanceScenarioTestHelper::CreateWithoutMapping();
+//   ASSERT_TRUE(test_helper);
+//   test_helper->SetInputScenario(ScenarioScope::kGlobal,
+//       InputScenario::kTyping);
+//
+//   // Observers aren't available until read-only memory is mapped.
+//   EXPECT_FALSE(PerformanceScenarioObserverList::GetForScope(
+//       ScenarioScope::kGlobal));
+//
+//   // Scenarios all return default values until read-only memory is mapped.
+//   EXPECT_EQ(GetInputScenario(ScenarioScope::kGlobal).load(
+//       std::memory_order_relaxed), InputScenario::kNoInput);
+//
+//   ScopedReadOnlyScenarioMemory global_memory(ScenarioScope::kGlobal,
+//       test_helper->GetReadOnlyScenarioRegion(ScenarioScope::kGlobal));
+//
+//   EXPECT_TRUE(PerformanceScenarioObserverList::GetForScope(
+//       ScenarioScope::kGlobal));
+//   EXPECT_EQ(GetInputScenario(ScenarioScope::kGlobal).load(
+//       std::memory_order_relaxed), InputScenario::kTyping);
+// }
+class PerformanceScenarioTestHelper {
+ public:
+  // Creates a PerformanceScenarioTestHelper holding writable performance
+  // scenario memory, along with read-only memory for all ScenarioScopes.
+  // Returns null if the shared memory couldn't be created or mapped.
+  static std::unique_ptr<PerformanceScenarioTestHelper> Create();
+
+  // Creates a PerformanceScenarioTestHelper holding writable performance
+  // scenario memory, but doesn't map any read-only memory. Tests can map
+  // read-only memory by passing the result of GetReadOnlyScenarioRegion() to a
+  // ScopedReadOnlyScenarioMemory object. Returns null if the shared memory
+  // couldn't be created.
+  static std::unique_ptr<PerformanceScenarioTestHelper> CreateWithoutMapping();
+
+  ~PerformanceScenarioTestHelper();
+
+  PerformanceScenarioTestHelper(const PerformanceScenarioTestHelper&) = delete;
+  PerformanceScenarioTestHelper& operator=(
+      const PerformanceScenarioTestHelper&) = delete;
+
+  // Returns a read-only memory handle for the given `scope`, for tests that
+  // create ScopedReadOnlyScenarioMemory manually.
+  base::ReadOnlySharedMemoryRegion GetReadOnlyScenarioRegion(
+      ScenarioScope scope) const;
+
+  // Updates the LoadingScenario for the given `scope`, and notifies all
+  // observers.
+  void SetLoadingScenario(ScenarioScope scope, LoadingScenario scenario);
+
+  // Updates the InputScenario for the given `scope`, and notifies all
+  // observers.
+  void SetInputScenario(ScenarioScope scope, InputScenario scenario);
+
+ private:
+  PerformanceScenarioTestHelper(
+      base::StructuredSharedMemory<ScenarioState> global_state,
+      base::StructuredSharedMemory<ScenarioState> process_state);
+
+  base::StructuredSharedMemory<ScenarioState>& ScenarioStateForScope(
+      ScenarioScope scope);
+  const base::StructuredSharedMemory<ScenarioState>& ScenarioStateForScope(
+      ScenarioScope scope) const;
+
+  base::StructuredSharedMemory<ScenarioState> global_state_;
+  base::StructuredSharedMemory<ScenarioState> process_state_;
+
+  std::optional<ScopedReadOnlyScenarioMemory> global_read_only_memory_;
+  std::optional<ScopedReadOnlyScenarioMemory> process_read_only_memory_;
+};
+
+}  // namespace performance_scenarios
+
+#endif  // COMPONENTS_PERFORMANCE_MANAGER_SCENARIO_API_PERFORMANCE_SCENARIO_TEST_SUPPORT_H_
diff --git a/components/performance_manager/scenario_api/performance_scenarios_unittest.cc b/components/performance_manager/scenario_api/performance_scenarios_unittest.cc
index 33cfbcd..60d9464 100644
--- a/components/performance_manager/scenario_api/performance_scenarios_unittest.cc
+++ b/components/performance_manager/scenario_api/performance_scenarios_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/structured_shared_memory.h"
 #include "components/performance_manager/scenario_api/performance_scenario_memory.h"
+#include "components/performance_manager/scenario_api/performance_scenario_test_support.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace performance_scenarios {
@@ -60,8 +61,8 @@
                          ::testing::ValuesIn(InputScenarios::All()));
 
 TEST(PerformanceScenariosTest, MappedScenarioState) {
-  auto shared_memory = base::StructuredSharedMemory<ScenarioState>::Create();
-  ASSERT_TRUE(shared_memory.has_value());
+  auto test_helper = PerformanceScenarioTestHelper::CreateWithoutMapping();
+  ASSERT_TRUE(test_helper);
 
   // Before the shared memory is mapped in, GetLoadingScenario should return
   // default values.
@@ -75,14 +76,15 @@
   {
     // Map the shared memory as the global state.
     ScopedReadOnlyScenarioMemory mapped_global_memory(
-        ScenarioScope::kGlobal, shared_memory->DuplicateReadOnlyRegion());
+        ScenarioScope::kGlobal,
+        test_helper->GetReadOnlyScenarioRegion(ScenarioScope::kGlobal));
     EXPECT_EQ(GetLoadingScenario(ScenarioScope::kGlobal)
                   ->load(std::memory_order_relaxed),
               LoadingScenario::kNoPageLoading);
 
     // Updates should be visible in the global state only.
-    shared_memory->WritableRef().loading.store(
-        LoadingScenario::kFocusedPageLoading, std::memory_order_relaxed);
+    test_helper->SetLoadingScenario(ScenarioScope::kGlobal,
+                                    LoadingScenario::kFocusedPageLoading);
     EXPECT_EQ(GetLoadingScenario(ScenarioScope::kGlobal)
                   ->load(std::memory_order_relaxed),
               LoadingScenario::kFocusedPageLoading);
@@ -93,14 +95,14 @@
     // Map the same shared memory as the per-process state.
     ScopedReadOnlyScenarioMemory mapped_current_memory(
         ScenarioScope::kCurrentProcess,
-        shared_memory->DuplicateReadOnlyRegion());
+        test_helper->GetReadOnlyScenarioRegion(ScenarioScope::kGlobal));
     EXPECT_EQ(GetLoadingScenario(ScenarioScope::kCurrentProcess)
                   ->load(std::memory_order_relaxed),
               LoadingScenario::kFocusedPageLoading);
 
     // Updates should be visible in both mappings.
-    shared_memory->WritableRef().loading.store(
-        LoadingScenario::kVisiblePageLoading, std::memory_order_relaxed);
+    test_helper->SetLoadingScenario(ScenarioScope::kGlobal,
+                                    LoadingScenario::kVisiblePageLoading);
     EXPECT_EQ(GetLoadingScenario(ScenarioScope::kGlobal)
                   ->load(std::memory_order_relaxed),
               LoadingScenario::kVisiblePageLoading);
@@ -121,11 +123,11 @@
 
 TEST(PerformanceScenariosTest, SharedAtomicRef) {
   // Create and map shared memory.
-  auto shared_memory = base::StructuredSharedMemory<ScenarioState>::Create();
-  ASSERT_TRUE(shared_memory.has_value());
+  auto test_helper = PerformanceScenarioTestHelper::CreateWithoutMapping();
+  ASSERT_TRUE(test_helper);
   auto read_only_mapping =
       base::StructuredSharedMemory<ScenarioState>::MapReadOnlyRegion(
-          shared_memory->DuplicateReadOnlyRegion());
+          test_helper->GetReadOnlyScenarioRegion(ScenarioScope::kGlobal));
   ASSERT_TRUE(read_only_mapping.has_value());
 
   // Store pointers to the atomics in the shared memory for later comparison.
@@ -145,10 +147,9 @@
 
   // The SharedAtomicRef's should keep the mapping alive.
   mapping_ptr.reset();
-  shared_memory->WritableRef().loading.store(
-      LoadingScenario::kBackgroundPageLoading, std::memory_order_relaxed);
-  shared_memory->WritableRef().input.store(InputScenario::kNoInput,
-                                           std::memory_order_relaxed);
+  test_helper->SetLoadingScenario(ScenarioScope::kGlobal,
+                                  LoadingScenario::kBackgroundPageLoading);
+  test_helper->SetInputScenario(ScenarioScope::kGlobal, InputScenario::kTyping);
 
   // get()
   EXPECT_EQ(loading_ref.get(), loading_ptr);
@@ -161,8 +162,7 @@
   // operator->
   EXPECT_EQ(loading_ref->load(std::memory_order_relaxed),
             LoadingScenario::kBackgroundPageLoading);
-  EXPECT_EQ(input_ref->load(std::memory_order_relaxed),
-            InputScenario::kNoInput);
+  EXPECT_EQ(input_ref->load(std::memory_order_relaxed), InputScenario::kTyping);
 }
 
 TEST_P(PerformanceScenariosAllLoadingScenariosTest, EmptyScenarioPattern) {
diff --git a/components/permissions/permission_actions_history.h b/components/permissions/permission_actions_history.h
index 0b3a151..e8d5e61 100644
--- a/components/permissions/permission_actions_history.h
+++ b/components/permissions/permission_actions_history.h
@@ -32,11 +32,7 @@
     PermissionAction action;
     base::Time time;
 
-    bool operator==(const Entry that) const {
-      return std::tie(this->action, this->time) ==
-             std::tie(that.action, that.time);
-    }
-    bool operator!=(const Entry that) const { return !(*this == that); }
+    friend bool operator==(const Entry&, const Entry&) = default;
   };
 
   enum class EntryFilter {
diff --git a/components/permissions/permission_request_id.cc b/components/permissions/permission_request_id.cc
index e92a1b3..1e8654f 100644
--- a/components/permissions/permission_request_id.cc
+++ b/components/permissions/permission_request_id.cc
@@ -29,15 +29,6 @@
 PermissionRequestID& PermissionRequestID::operator=(
     const PermissionRequestID&) = default;
 
-bool PermissionRequestID::operator==(const PermissionRequestID& other) const {
-  return global_render_frame_host_id_ == other.global_render_frame_host_id_ &&
-         request_local_id_ == other.request_local_id_;
-}
-
-bool PermissionRequestID::operator!=(const PermissionRequestID& other) const {
-  return !operator==(other);
-}
-
 std::string PermissionRequestID::ToString() const {
   return base::StringPrintf(
       "%d,%d,%" PRId64, global_render_frame_host_id_.child_id,
diff --git a/components/permissions/permission_request_id.h b/components/permissions/permission_request_id.h
index 661bd4d1..3c071ba 100644
--- a/components/permissions/permission_request_id.h
+++ b/components/permissions/permission_request_id.h
@@ -50,8 +50,8 @@
     return request_local_id_;
   }
 
-  bool operator==(const PermissionRequestID& other) const;
-  bool operator!=(const PermissionRequestID& other) const;
+  friend bool operator==(const PermissionRequestID&,
+                         const PermissionRequestID&) = default;
 
   std::string ToString() const;
 
diff --git a/components/permissions/permission_usage_session.cc b/components/permissions/permission_usage_session.cc
index bea926ce..4a45ac5 100644
--- a/components/permissions/permission_usage_session.cc
+++ b/components/permissions/permission_usage_session.cc
@@ -8,20 +8,6 @@
 
 namespace permissions {
 
-bool PermissionUsageSession::operator==(
-    const PermissionUsageSession& other) const {
-  return std::tie(origin, type, usage_start, usage_end, had_user_activation,
-                  was_foreground, had_focus) ==
-         std::tie(other.origin, other.type, other.usage_start, other.usage_end,
-                  other.had_user_activation, other.was_foreground,
-                  other.had_focus);
-}
-
-bool PermissionUsageSession::operator!=(
-    const PermissionUsageSession& other) const {
-  return !(*this == other);
-}
-
 bool PermissionUsageSession::IsValid() const {
   return !(origin.opaque() || usage_start.is_null() || usage_end.is_null() ||
            usage_end < usage_start);
diff --git a/components/permissions/permission_usage_session.h b/components/permissions/permission_usage_session.h
index 7d3a0f5..d4566db 100644
--- a/components/permissions/permission_usage_session.h
+++ b/components/permissions/permission_usage_session.h
@@ -38,8 +38,8 @@
   // usage started.
   bool had_focus;
 
-  bool operator==(const PermissionUsageSession& other) const;
-  bool operator!=(const PermissionUsageSession& other) const;
+  friend bool operator==(const PermissionUsageSession&,
+                         const PermissionUsageSession&) = default;
 
   // Checks if the session satisfies the following constraints:
   // 1) `origin` is not opaque;
diff --git a/components/permissions/prediction_service/prediction_common.h b/components/permissions/prediction_service/prediction_common.h
index 3990fad..418e74e 100644
--- a/components/permissions/prediction_service/prediction_common.h
+++ b/components/permissions/prediction_service/prediction_common.h
@@ -21,10 +21,6 @@
 
 constexpr int kCountBuckets[] = {20, 15, 12, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
 
-// Thresholds of the likelihood that triggers the CPSS prompts.
-constexpr float kNotificationPredictionsThreshold = 0.81;
-constexpr float kGeolocationPredictionsThreshold = 0.95;
-
 // Returns the ratio rounded to the nearest 10%. It returns a value between 0
 // and 1 in steps of 0.1
 float GetRoundedRatio(int numerator, int denominator);
diff --git a/components/permissions/prediction_service/prediction_model_executor.cc b/components/permissions/prediction_service/prediction_model_executor.cc
index 9f73604..36740808 100644
--- a/components/permissions/prediction_service/prediction_model_executor.cc
+++ b/components/permissions/prediction_service/prediction_model_executor.cc
@@ -137,29 +137,21 @@
     const std::vector<const TfLiteTensor*>& output_tensors) {
   DCHECK(request_type_ == RequestType::kNotifications ||
          request_type_ == RequestType::kGeolocation);
+
+  if (!model_metadata_ || !model_metadata_->has_not_grant_thresholds()) {
+    LOG(WARNING)
+        << "[CPSS] Failed to read model thresholds from metadata";
+    return std::nullopt;
+  }
+
   std::vector<float> data;
   if (!tflite::task::core::PopulateVector<float>(output_tensors[0], &data)
            .ok()) {
     return std::nullopt;
   }
 
-  float threshold = request_type_ == RequestType::kNotifications
-                        ? kNotificationPredictionsThreshold
-                        : kGeolocationPredictionsThreshold;
-
-  // If the model has a metadata which contains a threshold value,
-  // use that threshold value.
-  if (model_metadata_ && model_metadata_->has_not_grant_thresholds()) {
-    // max_likely represents very likely to not grant
-    threshold = model_metadata_->not_grant_thresholds().max_likely();
-    base::UmaHistogramEnumeration(
-        "Permissions.PredictionService.PredictionThresholdSource",
-        PermissionPredictionThresholdSource::MODEL_METADATA);
-  } else {
-    base::UmaHistogramEnumeration(
-        "Permissions.PredictionService.PredictionThresholdSource",
-        PermissionPredictionThresholdSource::HARDCODED_FALLBACK);
-  }
+  // max_likely represents very likely to not grant
+  float threshold = model_metadata_->not_grant_thresholds().max_likely();
 
   GeneratePredictionsResponse response;
   response.mutable_prediction()
diff --git a/components/permissions/prediction_service/prediction_signature_model_executor.cc b/components/permissions/prediction_service/prediction_signature_model_executor.cc
index cd511eb6..cda2b5dc6 100644
--- a/components/permissions/prediction_service/prediction_signature_model_executor.cc
+++ b/components/permissions/prediction_service/prediction_signature_model_executor.cc
@@ -214,6 +214,13 @@
     const std::map<std::string, const TfLiteTensor*>& output_tensors) {
   DCHECK(request_type_ == RequestType::kNotifications ||
          request_type_ == RequestType::kGeolocation);
+
+  if (!model_metadata_ || !model_metadata_->has_not_grant_thresholds()) {
+    LOG(WARNING)
+        << "[CPSS] Failed to read signature model thresholds from metadata";
+    return std::nullopt;
+  }
+
   auto itr = output_tensors.find("outputs");
   if (itr == output_tensors.end()) {
     LOG(WARNING) << "[CPSS] Failed to find outputs tensor";
@@ -225,23 +232,9 @@
     return std::nullopt;
   }
 
-  float threshold = request_type_ == RequestType::kNotifications
-                        ? kNotificationPredictionsThreshold
-                        : kGeolocationPredictionsThreshold;
+  // max_likely represents very likely to not grant
+  float threshold = model_metadata_->not_grant_thresholds().max_likely();
 
-  // If the model has a metadata which contains a threshold value,
-  // use that threshold value.
-  if (model_metadata_ && model_metadata_->has_not_grant_thresholds()) {
-    // max_likely represents very likely to not grant
-    threshold = model_metadata_->not_grant_thresholds().max_likely();
-    base::UmaHistogramEnumeration(
-        "Permissions.PredictionService.PredictionThresholdSource",
-        PermissionPredictionThresholdSource::MODEL_METADATA);
-  } else {
-    base::UmaHistogramEnumeration(
-        "Permissions.PredictionService.PredictionThresholdSource",
-        PermissionPredictionThresholdSource::HARDCODED_FALLBACK);
-  }
   GeneratePredictionsResponse response;
   response.mutable_prediction()
       ->Add()
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index 9c67657..70c43a0 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -2358,6 +2358,13 @@
   // A boolean indicating whether the Site Isolation (a.k.a Site Per Process)
   // setting is enabled.
   optional bool site_isolation_enabled = 7;
+
+  // Providers for various analysis connectors.
+  repeated string file_downloaded_providers = 8;
+  repeated string file_attached_providers = 9;
+  repeated string bulk_data_entry_providers = 10;
+  repeated string print_providers = 11;
+  repeated string security_event_providers = 12;
 }
 
 // Anti virus product information.
diff --git a/components/policy/resources/templates/policy_definitions/Arc/ArcEnabled.yaml b/components/policy/resources/templates/policy_definitions/Arc/ArcEnabled.yaml
index d329122..e932142 100644
--- a/components/policy/resources/templates/policy_definitions/Arc/ArcEnabled.yaml
+++ b/components/policy/resources/templates/policy_definitions/Arc/ArcEnabled.yaml
@@ -4,8 +4,6 @@
 desc: Unless Ephemeral mode or multiple sign-in is on during the user's session, setting
   ArcEnabled to True turns ARC on for the user. Setting the policy to False or leaving
   it unset means enterprise users can't use ARC.
-
-  This policy only controls <ph name="ARC_VM">ArcVM</ph> on <ph name="PRODUCT_NAME">$2<ex>Google ChromeOS</ex></ph>. For <ph name="PRODUCT_OS_FLEX_NAME">Google ChromeOS Flex</ph>, please see the <ph name="DEVICE_FLEX_ARC_PRELOAD_ENABLED_POLICY_NAME">DeviceFlexArcPreloadEnabled</ph> policy for more details.
 example_value: false
 features:
   dynamic_refresh: true
diff --git a/components/safe_browsing/core/common/proto/BUILD.gn b/components/safe_browsing/core/common/proto/BUILD.gn
index f73e0ee8..73a4a84 100644
--- a/components/safe_browsing/core/common/proto/BUILD.gn
+++ b/components/safe_browsing/core/common/proto/BUILD.gn
@@ -24,6 +24,7 @@
 
 proto_to_value("csd_proto_to_value") {
   sources = [ "csd.proto" ]
+  deps = [ ":csd_proto" ]
 }
 
 proto_library("webui_proto") {
@@ -44,6 +45,7 @@
   deps = [
     ":csd_proto",
     ":csd_proto_to_value",
+    ":realtimeapi_proto",
     "//components/enterprise/common/proto:connectors_proto",
     "//components/enterprise/common/proto:connectors_proto_to_value",
   ]
@@ -61,4 +63,5 @@
 
 proto_to_value("safebrowsingv5_proto_to_value") {
   sources = [ "safebrowsingv5.proto" ]
+  deps = [ ":safebrowsingv5_proto" ]
 }
diff --git a/components/safe_browsing/core/common/proto_to_value/proto_to_value.gni b/components/safe_browsing/core/common/proto_to_value/proto_to_value.gni
index 47b6c44..1beb010 100644
--- a/components/safe_browsing/core/common/proto_to_value/proto_to_value.gni
+++ b/components/safe_browsing/core/common/proto_to_value/proto_to_value.gni
@@ -3,13 +3,18 @@
 # found in the LICENSE file.
 
 # Generate serializers that turn a protobuf into a base::Value. This
-# does not directly generate the protobuf bindings for any language.
+# does not directly generate the protobuf bindings for any language, so
+# callers must include the build target for C++ bindings in the deps.
 #
 # Example:
 #   proto_to_value("mylib_to_value") {
 #     sources = [
 #       "mylib.proto",
 #     ]
+#
+#     deps = [
+#       "mylib_proto_target"
+#     ]
 #   }
 
 import("//third_party/protobuf/proto_library.gni")
diff --git a/components/safe_browsing/core/common/proto_to_value/test_proto/BUILD.gn b/components/safe_browsing/core/common/proto_to_value/test_proto/BUILD.gn
index 359afaa74..646606f 100644
--- a/components/safe_browsing/core/common/proto_to_value/test_proto/BUILD.gn
+++ b/components/safe_browsing/core/common/proto_to_value/test_proto/BUILD.gn
@@ -19,4 +19,6 @@
     "test_proto.proto",
     "test_proto_dependency.proto",
   ]
+
+  deps = [ ":test_proto" ]
 }
diff --git a/components/saved_tab_groups/public/android/java/src/org/chromium/components/tab_group_sync/EitherId.java b/components/saved_tab_groups/public/android/java/src/org/chromium/components/tab_group_sync/EitherId.java
index 1b47b9b..482a76d 100644
--- a/components/saved_tab_groups/public/android/java/src/org/chromium/components/tab_group_sync/EitherId.java
+++ b/components/saved_tab_groups/public/android/java/src/org/chromium/components/tab_group_sync/EitherId.java
@@ -67,14 +67,29 @@
             return new EitherGroupId(null, syncId);
         }
 
+        @EnsuresNonNullIf("mLocalId")
         public boolean isLocalId() {
             return mLocalId != null;
         }
 
-        public @Nullable LocalTabGroupId getLocalId() {
+        public LocalTabGroupId getLocalId() {
             assert isLocalId();
             return mLocalId;
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof EitherGroupId)) return false;
+            EitherGroupId eitherId = (EitherGroupId) o;
+
+            boolean localIdEqual =
+                    isLocalId()
+                            && eitherId.isLocalId()
+                            && getLocalId().equals(eitherId.getLocalId());
+            boolean syncIdEqual =
+                    isSyncId() && eitherId.isSyncId() && getSyncId().equals(eitherId.getSyncId());
+            return localIdEqual || syncIdEqual;
+        }
     }
 
     private final @Nullable String mSyncId;
diff --git a/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.cc b/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.cc
index a983e14..34ca8762 100644
--- a/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.cc
+++ b/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.cc
@@ -10,6 +10,7 @@
 #include "base/android/jni_string.h"
 #include "base/android/scoped_java_ref.h"
 #include "base/uuid.h"
+#include "components/saved_tab_groups/public/android/tab_group_sync_conversions_bridge.h"
 #include "components/saved_tab_groups/public/types.h"
 
 using base::android::ConvertJavaStringToUTF8;
@@ -42,4 +43,20 @@
   return base::Uuid::ParseLowercase(ConvertJavaStringToUTF8(env, j_uuid));
 }
 
+EitherGroupID JavaSyncOrLocalGroupIdToEitherGroupId(
+    JNIEnv* env,
+    const JavaParamRef<jstring>& j_sync_group_id,
+    const JavaParamRef<jobject>& j_local_group_id) {
+  if (j_local_group_id.is_null()) {
+    std::string sync_group_id_str =
+        ConvertJavaStringToUTF8(env, j_sync_group_id);
+    return base::Uuid::ParseLowercase(sync_group_id_str);
+  } else {
+    LocalTabGroupID local_group_id =
+        TabGroupSyncConversionsBridge::FromJavaTabGroupId(env,
+                                                          j_local_group_id);
+    return local_group_id;
+  }
+}
+
 }  // namespace tab_groups
diff --git a/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h b/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h
index 7efa39d..8c9a1fa 100644
--- a/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h
+++ b/components/saved_tab_groups/public/android/tab_group_sync_conversions_utils.h
@@ -31,6 +31,12 @@
 // Converts a Java string to base::Uuid.
 base::Uuid JavaStringToUuid(JNIEnv* env, const JavaParamRef<jstring>& j_uuid);
 
+// Converts a Java sync or local group ID to EitherGroupID.
+EitherGroupID JavaSyncOrLocalGroupIdToEitherGroupId(
+    JNIEnv* env,
+    const JavaParamRef<jstring>& j_sync_group_id,
+    const JavaParamRef<jobject>& j_local_group_id);
+
 }  // namespace tab_groups
 
 #endif  // COMPONENTS_SAVED_TAB_GROUPS_PUBLIC_ANDROID_TAB_GROUP_SYNC_CONVERSIONS_UTILS_H_
diff --git a/components/tabs/BUILD.gn b/components/tabs/BUILD.gn
index 36ac7829..505e08b 100644
--- a/components/tabs/BUILD.gn
+++ b/components/tabs/BUILD.gn
@@ -5,7 +5,10 @@
 source_set("public") {
   sources = [
     "public/pinned_tab_collection.h",
+    "public/split_tab_collection.h",
+    "public/split_tab_data.h",
     "public/split_tab_id.h",
+    "public/split_tab_visual_data.h",
     "public/supports_handles.h",
     "public/tab_collection.h",
     "public/tab_collection_node_interface.h",
@@ -25,6 +28,9 @@
 source_set("tabs") {
   sources = [
     "pinned_tab_collection.cc",
+    "split_tab_collection.cc",
+    "split_tab_data.cc",
+    "split_tab_visual_data.cc",
     "tab_collection.cc",
     "tab_collection_storage.cc",
   ]
diff --git a/chrome/browser/ui/tabs/split_tab_collection.h b/components/tabs/public/split_tab_collection.h
similarity index 83%
rename from chrome/browser/ui/tabs/split_tab_collection.h
rename to components/tabs/public/split_tab_collection.h
index 2663bd1..107fe31 100644
--- a/chrome/browser/ui/tabs/split_tab_collection.h
+++ b/components/tabs/public/split_tab_collection.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_TABS_SPLIT_TAB_COLLECTION_H_
-#define CHROME_BROWSER_UI_TABS_SPLIT_TAB_COLLECTION_H_
+#ifndef COMPONENTS_TABS_PUBLIC_SPLIT_TAB_COLLECTION_H_
+#define COMPONENTS_TABS_PUBLIC_SPLIT_TAB_COLLECTION_H_
 
 #include "components/tabs/public/split_tab_id.h"
 #include "components/tabs/public/tab_collection.h"
@@ -11,7 +11,7 @@
 namespace split_tabs {
 class SplitTabData;
 class SplitTabVisualData;
-}
+}  // namespace split_tabs
 
 namespace tabs {
 
@@ -35,4 +35,4 @@
 
 }  // namespace tabs
 
-#endif  // CHROME_BROWSER_UI_TABS_SPLIT_TAB_COLLECTION_H_
+#endif  // COMPONENTS_TABS_PUBLIC_SPLIT_TAB_COLLECTION_H_
diff --git a/chrome/browser/ui/tabs/split_tab_data.h b/components/tabs/public/split_tab_data.h
similarity index 77%
rename from chrome/browser/ui/tabs/split_tab_data.h
rename to components/tabs/public/split_tab_data.h
index 170fa5b6..2b5493d 100644
--- a/chrome/browser/ui/tabs/split_tab_data.h
+++ b/components/tabs/public/split_tab_data.h
@@ -2,12 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_TABS_SPLIT_TAB_DATA_H_
-#define CHROME_BROWSER_UI_TABS_SPLIT_TAB_DATA_H_
+#ifndef COMPONENTS_TABS_PUBLIC_SPLIT_TAB_DATA_H_
+#define COMPONENTS_TABS_PUBLIC_SPLIT_TAB_DATA_H_
 
-#include "chrome/browser/ui/tabs/split_tab_collection.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
+#include <vector>
+
+#include "components/tabs/public/split_tab_collection.h"
 #include "components/tabs/public/split_tab_id.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "components/tabs/public/tab_interface.h"
 
 namespace split_tabs {
@@ -34,4 +36,4 @@
 
 }  // namespace split_tabs
 
-#endif  // CHROME_BROWSER_UI_TABS_SPLIT_TAB_DATA_H_
+#endif  // COMPONENTS_TABS_PUBLIC_SPLIT_TAB_DATA_H_
diff --git a/chrome/browser/ui/tabs/split_tab_visual_data.h b/components/tabs/public/split_tab_visual_data.h
similarity index 89%
rename from chrome/browser/ui/tabs/split_tab_visual_data.h
rename to components/tabs/public/split_tab_visual_data.h
index 10213a90..1511364 100644
--- a/chrome/browser/ui/tabs/split_tab_visual_data.h
+++ b/components/tabs/public/split_tab_visual_data.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_TABS_SPLIT_TAB_VISUAL_DATA_H_
-#define CHROME_BROWSER_UI_TABS_SPLIT_TAB_VISUAL_DATA_H_
+#ifndef COMPONENTS_TABS_PUBLIC_SPLIT_TAB_VISUAL_DATA_H_
+#define COMPONENTS_TABS_PUBLIC_SPLIT_TAB_VISUAL_DATA_H_
 
 namespace split_tabs {
 
@@ -52,4 +52,4 @@
 
 }  // namespace split_tabs
 
-#endif  // CHROME_BROWSER_UI_TABS_SPLIT_TAB_VISUAL_DATA_H_
+#endif  // COMPONENTS_TABS_PUBLIC_SPLIT_TAB_VISUAL_DATA_H_
diff --git a/components/tabs/public/tab_collection.h b/components/tabs/public/tab_collection.h
index a3fc068..126924a 100644
--- a/components/tabs/public/tab_collection.h
+++ b/components/tabs/public/tab_collection.h
@@ -39,7 +39,7 @@
 
    public:
     using iterator_category = std::forward_iterator_tag;
-    using value_type = const tabs::TabInterface*;
+    using value_type = tabs::TabInterface*;
     using difference_type = ptrdiff_t;
     using pointer = value_type;
     using reference = value_type;
@@ -80,7 +80,7 @@
     };
 
     // Points to the currently accessed tab during iteration.
-    raw_ptr<const tabs::TabInterface> cur_;
+    raw_ptr<tabs::TabInterface> cur_;
 
     // Points to the root tab collection that the iterator is traversing.
     raw_ptr<const tabs::TabCollection> root_;
@@ -206,10 +206,6 @@
     return base::PassKey<TabCollection>();
   }
 
-  // Helper function for GetTabsRecursive that uses std::list, in order to take
-  // advantage of constant-time concatenation.
-  std::list<TabInterface*> GetTabsRecursiveAsList() const;
-
   const ChildrenVector& GetChildren() const { return impl_->GetChildren(); }
 
   // Total number of tabs in the collection.
diff --git a/components/tabs/public/tab_interface.h b/components/tabs/public/tab_interface.h
index b33c472..2b5c0ced 100644
--- a/components/tabs/public/tab_interface.h
+++ b/components/tabs/public/tab_interface.h
@@ -191,6 +191,7 @@
   // TabFeatures or BrowserWindowFeatures, you can safely assume that this is
   // always non-nullptr.
   virtual BrowserWindowInterface* GetBrowserWindowInterface() = 0;
+  virtual const BrowserWindowInterface* GetBrowserWindowInterface() const = 0;
 #endif  // !BUILDFLAG(IS_ANDROID)
 
   // Returns the feature controllers scoped to this tab.
diff --git a/chrome/browser/ui/tabs/split_tab_collection.cc b/components/tabs/split_tab_collection.cc
similarity index 79%
rename from chrome/browser/ui/tabs/split_tab_collection.cc
rename to components/tabs/split_tab_collection.cc
index 4cc63459..d3d8d7f 100644
--- a/chrome/browser/ui/tabs/split_tab_collection.cc
+++ b/components/tabs/split_tab_collection.cc
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/tabs/split_tab_collection.h"
+#include "components/tabs/public/split_tab_collection.h"
 
 #include <memory>
 #include <optional>
 
-#include "chrome/browser/ui/tabs/split_tab_data.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
-#include "chrome/browser/ui/tabs/tab_model.h"
+#include "components/tabs/public/split_tab_data.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "components/tabs/public/tab_collection_storage.h"
 
 namespace tabs {
diff --git a/chrome/browser/ui/tabs/split_tab_data.cc b/components/tabs/split_tab_data.cc
similarity index 76%
rename from chrome/browser/ui/tabs/split_tab_data.cc
rename to components/tabs/split_tab_data.cc
index 2c18bcb..1ef8019 100644
--- a/chrome/browser/ui/tabs/split_tab_data.cc
+++ b/components/tabs/split_tab_data.cc
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/tabs/split_tab_data.h"
+#include "components/tabs/public/split_tab_data.h"
 
 #include <memory>
 
-#include "chrome/browser/ui/tabs/split_tab_collection.h"
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
-#include "chrome/browser/ui/tabs/tab_model.h"
+#include "components/tabs/public/split_tab_collection.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 #include "components/tabs/public/tab_interface.h"
 
 namespace split_tabs {
diff --git a/chrome/browser/ui/tabs/split_tab_visual_data.cc b/components/tabs/split_tab_visual_data.cc
similarity index 90%
rename from chrome/browser/ui/tabs/split_tab_visual_data.cc
rename to components/tabs/split_tab_visual_data.cc
index 70b3f23..f2a8520 100644
--- a/chrome/browser/ui/tabs/split_tab_visual_data.cc
+++ b/components/tabs/split_tab_visual_data.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/tabs/split_tab_visual_data.h"
+#include "components/tabs/public/split_tab_visual_data.h"
 
 namespace split_tabs {
 
diff --git a/components/tabs/tab_collection.cc b/components/tabs/tab_collection.cc
index 4052e6c..6017260c 100644
--- a/components/tabs/tab_collection.cc
+++ b/components/tabs/tab_collection.cc
@@ -144,8 +144,13 @@
 }
 
 std::vector<TabInterface*> TabCollection::GetTabsRecursive() const {
-  std::list<TabInterface*> tabs = GetTabsRecursiveAsList();
-  return std::vector<TabInterface*>{tabs.begin(), tabs.end()};
+  std::vector<TabInterface*> tabs;
+  tabs.reserve(TabCountRecursive());
+  for (tabs::TabInterface* tab : *this) {
+    tabs.push_back(tab);
+  }
+
+  return tabs;
 }
 
 std::optional<size_t> TabCollection::GetIndexOfCollection(
@@ -257,23 +262,4 @@
   }
 }
 
-std::list<TabInterface*> TabCollection::GetTabsRecursiveAsList() const {
-  const auto& children = impl_->GetChildren();
-  std::list<TabInterface*> tabs;
-
-  for (const auto& child : children) {
-    if (std::holds_alternative<std::unique_ptr<TabInterface>>(child)) {
-      TabInterface* tab = std::get<std::unique_ptr<TabInterface>>(child).get();
-      tabs.push_back(tab);
-    } else {
-      std::list<TabInterface*> tabs_to_insert =
-          std::get<std::unique_ptr<TabCollection>>(child)
-              ->GetTabsRecursiveAsList();
-      tabs.splice(tabs.end(), tabs_to_insert);
-    }
-  }
-
-  return tabs;
-}
-
 }  // namespace tabs
diff --git a/components/tracing/common/tracing_switches.cc b/components/tracing/common/tracing_switches.cc
index f036de5..4ab3da6 100644
--- a/components/tracing/common/tracing_switches.cc
+++ b/components/tracing/common/tracing_switches.cc
@@ -18,10 +18,18 @@
 //  < {input txt config}.pbtxt > {output proto config}.pb
 const char kEnableBackgroundTracing[] = "enable-background-tracing";
 
-// Causes TRACE_EVENT flags to be recorded from startup.
-// This flag will be ignored if --trace-startup or --trace-shutdown is provided.
+// Enables startup tracing by passing a file path containing the chrome Json
+// tracing config as an argument. This flag will be ignored if --trace-startup
+// or --trace-shutdown is provided.
 const char kTraceConfigFile[]               = "trace-config-file";
 
+// Enables startup tracing by passing a file path containing the perfetto config
+// as an argument. The config is a serialized or base64 encoded proto
+// `perfetto.protos.TraceConfig` defined in
+// third_party/perfetto/protos/perfetto/config/trace_config.proto. This flag
+// will be ignored if --trace-startup or --trace-shutdown is provided.
+const char kTracePerfettoConfigFile[] = "trace-perfetto-config-file";
+
 // Causes TRACE_EVENT flags to be recorded from startup. Optionally, can
 // specify the specific trace categories to include (e.g.
 // --trace-startup=base,net) otherwise, all events are recorded. Setting this
diff --git a/components/tracing/common/tracing_switches.h b/components/tracing/common/tracing_switches.h
index 94711d8..e8b5ee3 100644
--- a/components/tracing/common/tracing_switches.h
+++ b/components/tracing/common/tracing_switches.h
@@ -11,6 +11,7 @@
 
 TRACING_EXPORT extern const char kEnableBackgroundTracing[];
 TRACING_EXPORT extern const char kTraceConfigFile[];
+TRACING_EXPORT extern const char kTracePerfettoConfigFile[];
 TRACING_EXPORT extern const char kTraceStartup[];
 TRACING_EXPORT extern const char kTraceConfigHandle[];
 TRACING_EXPORT extern const char kEnableTracing[];
diff --git a/components/trusted_vault/BUILD.gn b/components/trusted_vault/BUILD.gn
index 1d8ec41..cf151bf 100644
--- a/components/trusted_vault/BUILD.gn
+++ b/components/trusted_vault/BUILD.gn
@@ -5,6 +5,8 @@
 static_library("trusted_vault") {
   sources = [
     "trusted_vault_client.h",
+    "trusted_vault_server_constants.cc",
+    "trusted_vault_server_constants.h",
     "trusted_vault_service.cc",
     "trusted_vault_service.h",
   ]
@@ -42,6 +44,8 @@
       "standalone_trusted_vault_backend.h",
       "standalone_trusted_vault_client.cc",
       "standalone_trusted_vault_client.h",
+      "standalone_trusted_vault_server_constants.cc",
+      "standalone_trusted_vault_server_constants.h",
       "standalone_trusted_vault_storage.cc",
       "standalone_trusted_vault_storage.h",
       "trusted_vault_access_token_fetcher.h",
@@ -61,8 +65,6 @@
       "trusted_vault_histograms.h",
       "trusted_vault_request.cc",
       "trusted_vault_request.h",
-      "trusted_vault_server_constants.cc",
-      "trusted_vault_server_constants.h",
       "trusted_vault_throttling_connection.h",
       "trusted_vault_throttling_connection_impl.cc",
       "trusted_vault_throttling_connection_impl.h",
@@ -104,6 +106,7 @@
       "recovery_key_store_connection_unittest.cc",
       "securebox_unittest.cc",
       "standalone_trusted_vault_backend_unittest.cc",
+      "standalone_trusted_vault_server_constants_unittest.cc",
       "standalone_trusted_vault_storage_unittest.cc",
       "trusted_vault_access_token_fetcher_frontend_unittest.cc",
       "trusted_vault_connection_impl_unittest.cc",
diff --git a/components/trusted_vault/download_keys_response_handler.cc b/components/trusted_vault/download_keys_response_handler.cc
index cd21401a..d2f1957b 100644
--- a/components/trusted_vault/download_keys_response_handler.cc
+++ b/components/trusted_vault/download_keys_response_handler.cc
@@ -12,6 +12,7 @@
 #include "components/trusted_vault/proto/vault.pb.h"
 #include "components/trusted_vault/proto_string_bytes_conversion.h"
 #include "components/trusted_vault/securebox.h"
+#include "components/trusted_vault/standalone_trusted_vault_server_constants.h"
 #include "components/trusted_vault/trusted_vault_connection.h"
 #include "components/trusted_vault/trusted_vault_crypto.h"
 #include "components/trusted_vault/trusted_vault_server_constants.h"
diff --git a/components/trusted_vault/download_keys_response_handler_unittest.cc b/components/trusted_vault/download_keys_response_handler_unittest.cc
index aa34eeb..94a7829f 100644
--- a/components/trusted_vault/download_keys_response_handler_unittest.cc
+++ b/components/trusted_vault/download_keys_response_handler_unittest.cc
@@ -10,6 +10,7 @@
 #include "components/trusted_vault/proto/vault.pb.h"
 #include "components/trusted_vault/proto_string_bytes_conversion.h"
 #include "components/trusted_vault/securebox.h"
+#include "components/trusted_vault/standalone_trusted_vault_server_constants.h"
 #include "components/trusted_vault/trusted_vault_connection.h"
 #include "components/trusted_vault/trusted_vault_crypto.h"
 #include "components/trusted_vault/trusted_vault_server_constants.h"
diff --git a/components/trusted_vault/icloud_keychain_recovery_factor_unittest.mm b/components/trusted_vault/icloud_keychain_recovery_factor_unittest.mm
index 17667975..d6a21ab 100644
--- a/components/trusted_vault/icloud_keychain_recovery_factor_unittest.mm
+++ b/components/trusted_vault/icloud_keychain_recovery_factor_unittest.mm
@@ -20,6 +20,7 @@
 #include "components/trusted_vault/proto/local_trusted_vault.pb.h"
 #include "components/trusted_vault/proto_string_bytes_conversion.h"
 #include "components/trusted_vault/securebox.h"
+#include "components/trusted_vault/standalone_trusted_vault_server_constants.h"
 #include "components/trusted_vault/test/fake_file_access.h"
 #include "components/trusted_vault/test/mock_trusted_vault_throttling_connection.h"
 #include "components/trusted_vault/trusted_vault_connection.h"
diff --git a/components/trusted_vault/physical_device_recovery_factor_unittest.cc b/components/trusted_vault/physical_device_recovery_factor_unittest.cc
index f0e3a67..a6e4c4ba 100644
--- a/components/trusted_vault/physical_device_recovery_factor_unittest.cc
+++ b/components/trusted_vault/physical_device_recovery_factor_unittest.cc
@@ -18,6 +18,7 @@
 #include "components/trusted_vault/local_recovery_factor.h"
 #include "components/trusted_vault/proto_string_bytes_conversion.h"
 #include "components/trusted_vault/securebox.h"
+#include "components/trusted_vault/standalone_trusted_vault_server_constants.h"
 #include "components/trusted_vault/standalone_trusted_vault_storage.h"
 #include "components/trusted_vault/test/fake_file_access.h"
 #include "components/trusted_vault/test/mock_trusted_vault_throttling_connection.h"
diff --git a/components/trusted_vault/standalone_trusted_vault_backend.cc b/components/trusted_vault/standalone_trusted_vault_backend.cc
index cdb8ee2c..fdbe7b0 100644
--- a/components/trusted_vault/standalone_trusted_vault_backend.cc
+++ b/components/trusted_vault/standalone_trusted_vault_backend.cc
@@ -29,6 +29,7 @@
 #include "components/trusted_vault/proto_string_bytes_conversion.h"
 #include "components/trusted_vault/proto_time_conversion.h"
 #include "components/trusted_vault/securebox.h"
+#include "components/trusted_vault/standalone_trusted_vault_server_constants.h"
 #include "components/trusted_vault/standalone_trusted_vault_storage.h"
 #include "components/trusted_vault/trusted_vault_connection.h"
 #include "components/trusted_vault/trusted_vault_histograms.h"
diff --git a/components/trusted_vault/standalone_trusted_vault_backend_unittest.cc b/components/trusted_vault/standalone_trusted_vault_backend_unittest.cc
index 419cfd6..460ca51 100644
--- a/components/trusted_vault/standalone_trusted_vault_backend_unittest.cc
+++ b/components/trusted_vault/standalone_trusted_vault_backend_unittest.cc
@@ -29,6 +29,7 @@
 #include "components/trusted_vault/proto/local_trusted_vault.pb.h"
 #include "components/trusted_vault/proto_string_bytes_conversion.h"
 #include "components/trusted_vault/securebox.h"
+#include "components/trusted_vault/standalone_trusted_vault_server_constants.h"
 #include "components/trusted_vault/standalone_trusted_vault_storage.h"
 #include "components/trusted_vault/test/fake_file_access.h"
 #include "components/trusted_vault/test/mock_trusted_vault_throttling_connection.h"
diff --git a/components/trusted_vault/standalone_trusted_vault_server_constants.cc b/components/trusted_vault/standalone_trusted_vault_server_constants.cc
new file mode 100644
index 0000000..a4309bd
--- /dev/null
+++ b/components/trusted_vault/standalone_trusted_vault_server_constants.cc
@@ -0,0 +1,90 @@
+// 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 "components/trusted_vault/standalone_trusted_vault_server_constants.h"
+
+#include <string_view>
+
+#include "base/base64url.h"
+#include "base/containers/fixed_flat_map.h"
+#include "net/base/url_util.h"
+
+namespace trusted_vault {
+
+std::vector<uint8_t> GetConstantTrustedVaultKey() {
+  return std::vector<uint8_t>(16, 0);
+}
+
+GURL GetGetSecurityDomainMembersURL(const GURL& server_url) {
+  // View three is `SECURITY_DOMAIN_MEMBER_METADATA`.
+  return GURL(server_url.spec() + kSecurityDomainMemberNamePrefix + "?view=3");
+}
+
+GURL GetGetSecurityDomainMemberURL(const GURL& server_url,
+                                   base::span<const uint8_t> public_key) {
+  std::string encoded_public_key;
+  base::Base64UrlEncode(std::string(public_key.begin(), public_key.end()),
+                        base::Base64UrlEncodePolicy::OMIT_PADDING,
+                        &encoded_public_key);
+  return GURL(server_url.spec() + kSecurityDomainMemberNamePrefix +
+              encoded_public_key + "?view=2" +
+              "&request_header.force_master_read=true");
+}
+
+GURL GetGetSecurityDomainURL(const GURL& server_url,
+                             SecurityDomainId security_domain) {
+  return GURL(server_url.spec() + GetSecurityDomainPath(security_domain) +
+              "?view=2");
+}
+
+GURL GetJoinSecurityDomainURL(const GURL& server_url,
+                              SecurityDomainId security_domain) {
+  return GURL(server_url.spec() + GetSecurityDomainPath(security_domain) +
+              ":join");
+}
+
+GURL GetGetSecurityDomainMembersURLForTesting(
+    const std::optional<std::string>& next_page_token,
+    const GURL& server_url) {
+  GURL url = GetGetSecurityDomainMembersURL(server_url);
+  if (next_page_token) {
+    url = net::AppendQueryParameter(url, "page_token", *next_page_token);
+  }
+  return net::AppendQueryParameter(url, kQueryParameterAlternateOutputKey,
+                                   kQueryParameterAlternateOutputProto);
+}
+
+GURL GetFullJoinSecurityDomainsURLForTesting(const GURL& server_url,
+                                             SecurityDomainId security_domain) {
+  return net::AppendQueryParameter(
+      GetJoinSecurityDomainURL(server_url, security_domain),
+      kQueryParameterAlternateOutputKey, kQueryParameterAlternateOutputProto);
+}
+
+GURL GetFullGetSecurityDomainMemberURLForTesting(
+    const GURL& server_url,
+    base::span<const uint8_t> public_key) {
+  return net::AppendQueryParameter(
+      GetGetSecurityDomainMemberURL(server_url, public_key),
+      kQueryParameterAlternateOutputKey, kQueryParameterAlternateOutputProto);
+}
+
+GURL GetFullGetSecurityDomainURLForTesting(const GURL& server_url,
+                                           SecurityDomainId security_domain) {
+  return net::AppendQueryParameter(
+      GetGetSecurityDomainURL(server_url, security_domain),
+      kQueryParameterAlternateOutputKey, kQueryParameterAlternateOutputProto);
+}
+
+std::string GetSecurityDomainPath(SecurityDomainId domain) {
+  switch (domain) {
+    case SecurityDomainId::kChromeSync:
+      return std::string(kSecurityDomainPathPrefix) + kSyncSecurityDomainName;
+    case SecurityDomainId::kPasskeys:
+      return std::string(kSecurityDomainPathPrefix) +
+             kPasskeysSecurityDomainName;
+  }
+}
+
+}  // namespace trusted_vault
diff --git a/components/trusted_vault/standalone_trusted_vault_server_constants.h b/components/trusted_vault/standalone_trusted_vault_server_constants.h
new file mode 100644
index 0000000..84e706f
--- /dev/null
+++ b/components/trusted_vault/standalone_trusted_vault_server_constants.h
@@ -0,0 +1,54 @@
+// 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.
+
+#ifndef COMPONENTS_TRUSTED_VAULT_STANDALONE_TRUSTED_VAULT_SERVER_CONSTANTS_H_
+#define COMPONENTS_TRUSTED_VAULT_STANDALONE_TRUSTED_VAULT_SERVER_CONSTANTS_H_
+
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "base/containers/span.h"
+#include "components/trusted_vault/trusted_vault_server_constants.h"
+#include "url/gurl.h"
+
+namespace trusted_vault {
+
+inline constexpr int kUnknownConstantKeyVersion = 0;
+
+inline constexpr char kSecurityDomainPathPrefix[] = "users/me/securitydomains/";
+inline constexpr char kSecurityDomainMemberNamePrefix[] = "users/me/members/";
+inline constexpr char kJoinSecurityDomainsErrorDetailTypeURL[] =
+    "type.googleapis.com/"
+    "google.internal.identity.securitydomain.v1.JoinSecurityDomainErrorDetail";
+
+inline constexpr char kQueryParameterAlternateOutputKey[] = "alt";
+inline constexpr char kQueryParameterAlternateOutputProto[] = "proto";
+
+std::vector<uint8_t> GetConstantTrustedVaultKey();
+GURL GetGetSecurityDomainMembersURL(const GURL& server_url);
+GURL GetGetSecurityDomainMemberURL(const GURL& server_url,
+                                   base::span<const uint8_t> public_key);
+GURL GetGetSecurityDomainURL(const GURL& server_url,
+                             SecurityDomainId security_domain);
+GURL GetJoinSecurityDomainURL(const GURL& server_url,
+                              SecurityDomainId security_domain);
+
+// Computes full URL, including alternate proto param.
+GURL GetGetSecurityDomainMembersURLForTesting(
+    const std::optional<std::string>& next_page_token,
+    const GURL& server_url);
+GURL GetFullJoinSecurityDomainsURLForTesting(const GURL& server_url,
+                                             SecurityDomainId security_domain);
+GURL GetFullGetSecurityDomainMemberURLForTesting(
+    const GURL& server_url,
+    base::span<const uint8_t> public_key);
+GURL GetFullGetSecurityDomainURLForTesting(const GURL& server_url,
+                                           SecurityDomainId security_domain);
+
+std::string GetSecurityDomainPath(SecurityDomainId domain);
+
+}  // namespace trusted_vault
+
+#endif  // COMPONENTS_TRUSTED_VAULT_STANDALONE_TRUSTED_VAULT_SERVER_CONSTANTS_H_
diff --git a/components/trusted_vault/standalone_trusted_vault_server_constants_unittest.cc b/components/trusted_vault/standalone_trusted_vault_server_constants_unittest.cc
new file mode 100644
index 0000000..39d194a
--- /dev/null
+++ b/components/trusted_vault/standalone_trusted_vault_server_constants_unittest.cc
@@ -0,0 +1,51 @@
+// 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/trusted_vault/standalone_trusted_vault_server_constants.h"
+
+#include <cstdint>
+#include <vector>
+
+#include "components/trusted_vault/securebox.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace trusted_vault {
+
+namespace {
+
+using testing::Eq;
+
+TEST(StandaloneTrustedVaultServerConstantsTest,
+     ShouldGetGetSecurityDomainMemberURL) {
+  const GURL kTestUrl("https://example.com/v1/");
+
+  // Arbitrary key, with an appropriate length.
+  const std::vector<uint8_t> kPublicKey{
+      0x4,  0xF2, 0x4C, 0x45, 0xBA, 0xF4, 0xF8, 0x6C, 0xF9, 0x73, 0xCE,
+      0x75, 0xC,  0xC9, 0xD4, 0xF,  0x4A, 0x53, 0xB7, 0x85, 0x46, 0x41,
+      0xFB, 0x31, 0x17, 0xF,  0xEB, 0xB,  0x45, 0xE4, 0x29, 0x69, 0x9B,
+      0xB2, 0x7,  0x12, 0xC1, 0x9,  0x3D, 0xEF, 0xBB, 0x57, 0xDC, 0x56,
+      0x12, 0x29, 0xF2, 0x73, 0xE1, 0xC5, 0x99, 0x1C, 0x49, 0x3A, 0xA2,
+      0x30, 0xF9, 0xBA, 0x3B, 0xB1, 0x83, 0xCF, 0x1B, 0x5D, 0xE8};
+
+  // Guard against future code changes, in case the key length changes.
+  ASSERT_THAT(kPublicKey.size(), Eq(SecureBoxKeyPair::GenerateRandom()
+                                        ->public_key()
+                                        .ExportToBytes()
+                                        .size()));
+
+  // Note that production code (TrustedVaultRequest::CreateURLLoader) will
+  // append &alt=proto to the URL.
+  EXPECT_THAT(GetGetSecurityDomainMemberURL(kTestUrl, kPublicKey).spec(),
+              Eq("https://example.com/v1/users/me/members/"
+                 "BPJMRbr0-Gz5c851DMnUD0pTt4VGQfsxFw_"
+                 "rC0XkKWmbsgcSwQk977tX3FYSKfJz4cWZHEk6ojD5ujuxg88bXeg"
+                 "?view=2"
+                 "&request_header.force_master_read=true"));
+}
+
+}  // namespace
+
+}  // namespace trusted_vault
diff --git a/components/trusted_vault/standalone_trusted_vault_storage.cc b/components/trusted_vault/standalone_trusted_vault_storage.cc
index f3c85e2..109c66d 100644
--- a/components/trusted_vault/standalone_trusted_vault_storage.cc
+++ b/components/trusted_vault/standalone_trusted_vault_storage.cc
@@ -13,6 +13,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram_functions.h"
 #include "components/trusted_vault/proto_string_bytes_conversion.h"
+#include "components/trusted_vault/standalone_trusted_vault_server_constants.h"
 #include "components/trusted_vault/trusted_vault_histograms.h"
 #include "components/trusted_vault/trusted_vault_server_constants.h"
 
diff --git a/components/trusted_vault/standalone_trusted_vault_storage_unittest.cc b/components/trusted_vault/standalone_trusted_vault_storage_unittest.cc
index d9bf7f0f..bc82f57d 100644
--- a/components/trusted_vault/standalone_trusted_vault_storage_unittest.cc
+++ b/components/trusted_vault/standalone_trusted_vault_storage_unittest.cc
@@ -17,6 +17,7 @@
 #include "components/trusted_vault/proto/local_trusted_vault.pb.h"
 #include "components/trusted_vault/proto_string_bytes_conversion.h"
 #include "components/trusted_vault/securebox.h"
+#include "components/trusted_vault/standalone_trusted_vault_server_constants.h"
 #include "components/trusted_vault/trusted_vault_histograms.h"
 #include "components/trusted_vault/trusted_vault_server_constants.h"
 #include "google_apis/gaia/gaia_id.h"
diff --git a/components/trusted_vault/test/fake_security_domains_server.cc b/components/trusted_vault/test/fake_security_domains_server.cc
index 9b3ade5..673402b4 100644
--- a/components/trusted_vault/test/fake_security_domains_server.cc
+++ b/components/trusted_vault/test/fake_security_domains_server.cc
@@ -14,6 +14,7 @@
 #include "base/rand_util.h"
 #include "components/trusted_vault/proto_string_bytes_conversion.h"
 #include "components/trusted_vault/securebox.h"
+#include "components/trusted_vault/standalone_trusted_vault_server_constants.h"
 #include "components/trusted_vault/trusted_vault_crypto.h"
 #include "components/trusted_vault/trusted_vault_server_constants.h"
 #include "net/http/http_status_code.h"
diff --git a/components/trusted_vault/trusted_vault_connection_impl.cc b/components/trusted_vault/trusted_vault_connection_impl.cc
index aa0caee..a07d46a 100644
--- a/components/trusted_vault/trusted_vault_connection_impl.cc
+++ b/components/trusted_vault/trusted_vault_connection_impl.cc
@@ -21,6 +21,7 @@
 #include "components/trusted_vault/proto/vault.pb.h"
 #include "components/trusted_vault/proto_string_bytes_conversion.h"
 #include "components/trusted_vault/securebox.h"
+#include "components/trusted_vault/standalone_trusted_vault_server_constants.h"
 #include "components/trusted_vault/trusted_vault_access_token_fetcher.h"
 #include "components/trusted_vault/trusted_vault_connection.h"
 #include "components/trusted_vault/trusted_vault_crypto.h"
diff --git a/components/trusted_vault/trusted_vault_connection_impl_unittest.cc b/components/trusted_vault/trusted_vault_connection_impl_unittest.cc
index 1c19f0d..4d29a0cf 100644
--- a/components/trusted_vault/trusted_vault_connection_impl_unittest.cc
+++ b/components/trusted_vault/trusted_vault_connection_impl_unittest.cc
@@ -23,6 +23,7 @@
 #include "components/trusted_vault/proto/vault.pb.h"
 #include "components/trusted_vault/proto_string_bytes_conversion.h"
 #include "components/trusted_vault/securebox.h"
+#include "components/trusted_vault/standalone_trusted_vault_server_constants.h"
 #include "components/trusted_vault/test/fake_trusted_vault_access_token_fetcher.h"
 #include "components/trusted_vault/trusted_vault_access_token_fetcher.h"
 #include "components/trusted_vault/trusted_vault_connection.h"
diff --git a/components/trusted_vault/trusted_vault_request.cc b/components/trusted_vault/trusted_vault_request.cc
index d8037f02..cb1bf2b 100644
--- a/components/trusted_vault/trusted_vault_request.cc
+++ b/components/trusted_vault/trusted_vault_request.cc
@@ -10,6 +10,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/time/time.h"
 #include "components/signin/public/identity_manager/access_token_info.h"
+#include "components/trusted_vault/standalone_trusted_vault_server_constants.h"
 #include "components/trusted_vault/trusted_vault_access_token_fetcher.h"
 #include "components/trusted_vault/trusted_vault_server_constants.h"
 #include "google_apis/credentials_mode.h"
diff --git a/components/trusted_vault/trusted_vault_server_constants.cc b/components/trusted_vault/trusted_vault_server_constants.cc
index 0dc4cfb..b24a28cd 100644
--- a/components/trusted_vault/trusted_vault_server_constants.cc
+++ b/components/trusted_vault/trusted_vault_server_constants.cc
@@ -4,89 +4,10 @@
 
 #include "components/trusted_vault/trusted_vault_server_constants.h"
 
-#include <string_view>
-
-#include "base/base64url.h"
 #include "base/containers/fixed_flat_map.h"
-#include "net/base/url_util.h"
 
 namespace trusted_vault {
 
-std::vector<uint8_t> GetConstantTrustedVaultKey() {
-  return std::vector<uint8_t>(16, 0);
-}
-
-GURL GetGetSecurityDomainMembersURL(const GURL& server_url) {
-  // View three is `SECURITY_DOMAIN_MEMBER_METADATA`.
-  return GURL(server_url.spec() + kSecurityDomainMemberNamePrefix + "?view=3");
-}
-
-GURL GetGetSecurityDomainMemberURL(const GURL& server_url,
-                                   base::span<const uint8_t> public_key) {
-  std::string encoded_public_key;
-  base::Base64UrlEncode(std::string(public_key.begin(), public_key.end()),
-                        base::Base64UrlEncodePolicy::OMIT_PADDING,
-                        &encoded_public_key);
-  return GURL(server_url.spec() + kSecurityDomainMemberNamePrefix +
-              encoded_public_key + "?view=2" +
-              "&request_header.force_master_read=true");
-}
-
-GURL GetGetSecurityDomainURL(const GURL& server_url,
-                             SecurityDomainId security_domain) {
-  return GURL(server_url.spec() + GetSecurityDomainPath(security_domain) +
-              "?view=2");
-}
-
-GURL GetJoinSecurityDomainURL(const GURL& server_url,
-                              SecurityDomainId security_domain) {
-  return GURL(server_url.spec() + GetSecurityDomainPath(security_domain) +
-              ":join");
-}
-
-GURL GetGetSecurityDomainMembersURLForTesting(
-    const std::optional<std::string>& next_page_token,
-    const GURL& server_url) {
-  GURL url = GetGetSecurityDomainMembersURL(server_url);
-  if (next_page_token) {
-    url = net::AppendQueryParameter(url, "page_token", *next_page_token);
-  }
-  return net::AppendQueryParameter(url, kQueryParameterAlternateOutputKey,
-                                   kQueryParameterAlternateOutputProto);
-}
-
-GURL GetFullJoinSecurityDomainsURLForTesting(const GURL& server_url,
-                                             SecurityDomainId security_domain) {
-  return net::AppendQueryParameter(
-      GetJoinSecurityDomainURL(server_url, security_domain),
-      kQueryParameterAlternateOutputKey, kQueryParameterAlternateOutputProto);
-}
-
-GURL GetFullGetSecurityDomainMemberURLForTesting(
-    const GURL& server_url,
-    base::span<const uint8_t> public_key) {
-  return net::AppendQueryParameter(
-      GetGetSecurityDomainMemberURL(server_url, public_key),
-      kQueryParameterAlternateOutputKey, kQueryParameterAlternateOutputProto);
-}
-
-GURL GetFullGetSecurityDomainURLForTesting(const GURL& server_url,
-                                           SecurityDomainId security_domain) {
-  return net::AppendQueryParameter(
-      GetGetSecurityDomainURL(server_url, security_domain),
-      kQueryParameterAlternateOutputKey, kQueryParameterAlternateOutputProto);
-}
-
-std::string GetSecurityDomainPath(SecurityDomainId domain) {
-  switch (domain) {
-    case SecurityDomainId::kChromeSync:
-      return std::string(kSecurityDomainPathPrefix) + kSyncSecurityDomainName;
-    case SecurityDomainId::kPasskeys:
-      return std::string(kSecurityDomainPathPrefix) +
-             kPasskeysSecurityDomainName;
-  }
-}
-
 std::optional<SecurityDomainId> GetSecurityDomainByName(std::string_view name) {
   static_assert(static_cast<int>(SecurityDomainId::kMaxValue) == 1,
                 "Update GetSecurityDomainByName and its unit tests when adding "
diff --git a/components/trusted_vault/trusted_vault_server_constants.h b/components/trusted_vault/trusted_vault_server_constants.h
index 3153e7467..6b330067 100644
--- a/components/trusted_vault/trusted_vault_server_constants.h
+++ b/components/trusted_vault/trusted_vault_server_constants.h
@@ -5,30 +5,15 @@
 #ifndef COMPONENTS_TRUSTED_VAULT_TRUSTED_VAULT_SERVER_CONSTANTS_H_
 #define COMPONENTS_TRUSTED_VAULT_TRUSTED_VAULT_SERVER_CONSTANTS_H_
 
-#include <cstdint>
 #include <optional>
-#include <string>
 #include <string_view>
-#include <vector>
 
 #include "base/containers/fixed_flat_set.h"
-#include "base/containers/span.h"
-#include "url/gurl.h"
 
 namespace trusted_vault {
 
-inline constexpr int kUnknownConstantKeyVersion = 0;
-
-inline constexpr char kSecurityDomainPathPrefix[] = "users/me/securitydomains/";
 inline constexpr char kSyncSecurityDomainName[] = "chromesync";
 inline constexpr char kPasskeysSecurityDomainName[] = "hw_protected";
-inline constexpr char kSecurityDomainMemberNamePrefix[] = "users/me/members/";
-inline constexpr char kJoinSecurityDomainsErrorDetailTypeURL[] =
-    "type.googleapis.com/"
-    "google.internal.identity.securitydomain.v1.JoinSecurityDomainErrorDetail";
-
-inline constexpr char kQueryParameterAlternateOutputKey[] = "alt";
-inline constexpr char kQueryParameterAlternateOutputProto[] = "proto";
 
 // Identifies a particular security domain.
 //
@@ -48,28 +33,6 @@
               "Update kAllSecurityDomainIdValues when adding SecurityDomainId "
               "enum values");
 
-std::vector<uint8_t> GetConstantTrustedVaultKey();
-GURL GetGetSecurityDomainMembersURL(const GURL& server_url);
-GURL GetGetSecurityDomainMemberURL(const GURL& server_url,
-                                   base::span<const uint8_t> public_key);
-GURL GetGetSecurityDomainURL(const GURL& server_url,
-                             SecurityDomainId security_domain);
-GURL GetJoinSecurityDomainURL(const GURL& server_url,
-                              SecurityDomainId security_domain);
-
-// Computes full URL, including alternate proto param.
-GURL GetGetSecurityDomainMembersURLForTesting(
-    const std::optional<std::string>& next_page_token,
-    const GURL& server_url);
-GURL GetFullJoinSecurityDomainsURLForTesting(const GURL& server_url,
-                                             SecurityDomainId security_domain);
-GURL GetFullGetSecurityDomainMemberURLForTesting(
-    const GURL& server_url,
-    base::span<const uint8_t> public_key);
-GURL GetFullGetSecurityDomainURLForTesting(const GURL& server_url,
-                                           SecurityDomainId security_domain);
-
-std::string GetSecurityDomainPath(SecurityDomainId domain);
 std::optional<SecurityDomainId> GetSecurityDomainByName(
     std::string_view domain);
 std::string_view GetSecurityDomainName(SecurityDomainId id);
diff --git a/components/trusted_vault/trusted_vault_server_constants_unittest.cc b/components/trusted_vault/trusted_vault_server_constants_unittest.cc
index a5a2f89f..2e0c030 100644
--- a/components/trusted_vault/trusted_vault_server_constants_unittest.cc
+++ b/components/trusted_vault/trusted_vault_server_constants_unittest.cc
@@ -4,10 +4,6 @@
 
 #include "components/trusted_vault/trusted_vault_server_constants.h"
 
-#include <cstdint>
-#include <vector>
-
-#include "components/trusted_vault/securebox.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -17,34 +13,6 @@
 
 using testing::Eq;
 
-TEST(TrustedVaultServerConstantsTest, ShouldGetGetSecurityDomainMemberURL) {
-  const GURL kTestUrl("https://example.com/v1/");
-
-  // Arbitrary key, with an appropriate length.
-  const std::vector<uint8_t> kPublicKey{
-      0x4,  0xF2, 0x4C, 0x45, 0xBA, 0xF4, 0xF8, 0x6C, 0xF9, 0x73, 0xCE,
-      0x75, 0xC,  0xC9, 0xD4, 0xF,  0x4A, 0x53, 0xB7, 0x85, 0x46, 0x41,
-      0xFB, 0x31, 0x17, 0xF,  0xEB, 0xB,  0x45, 0xE4, 0x29, 0x69, 0x9B,
-      0xB2, 0x7,  0x12, 0xC1, 0x9,  0x3D, 0xEF, 0xBB, 0x57, 0xDC, 0x56,
-      0x12, 0x29, 0xF2, 0x73, 0xE1, 0xC5, 0x99, 0x1C, 0x49, 0x3A, 0xA2,
-      0x30, 0xF9, 0xBA, 0x3B, 0xB1, 0x83, 0xCF, 0x1B, 0x5D, 0xE8};
-
-  // Guard against future code changes, in case the key length changes.
-  ASSERT_THAT(kPublicKey.size(), Eq(SecureBoxKeyPair::GenerateRandom()
-                                        ->public_key()
-                                        .ExportToBytes()
-                                        .size()));
-
-  // Note that production code (TrustedVaultRequest::CreateURLLoader) will
-  // append &alt=proto to the URL.
-  EXPECT_THAT(GetGetSecurityDomainMemberURL(kTestUrl, kPublicKey).spec(),
-              Eq("https://example.com/v1/users/me/members/"
-                 "BPJMRbr0-Gz5c851DMnUD0pTt4VGQfsxFw_"
-                 "rC0XkKWmbsgcSwQk977tX3FYSKfJz4cWZHEk6ojD5ujuxg88bXeg"
-                 "?view=2"
-                 "&request_header.force_master_read=true"));
-}
-
 TEST(TrustedVaultServerConstantsTest, GetSecurityDomainByName) {
   EXPECT_THAT(GetSecurityDomainByName("chromesync"),
               Eq(SecurityDomainId::kChromeSync));
diff --git a/components/user_education/views/help_bubble_view.cc b/components/user_education/views/help_bubble_view.cc
index d3c9bee5..1f5f361d 100644
--- a/components/user_education/views/help_bubble_view.cc
+++ b/components/user_education/views/help_bubble_view.cc
@@ -341,7 +341,7 @@
           true),
       delegate_(delegate),
       event_relay_(std::move(event_relay)) {
-  set_background_color(delegate_->GetHelpBubbleBackgroundColorId());
+  SetBackgroundColor(delegate_->GetHelpBubbleBackgroundColorId());
 
   if (anchor.rect.has_value()) {
     SetForceAnchorRect(anchor.rect.value());
diff --git a/components/viz/common/quads/compositor_frame_transition_directive.cc b/components/viz/common/quads/compositor_frame_transition_directive.cc
index c1b32463..e3dbaaf 100644
--- a/components/viz/common/quads/compositor_frame_transition_directive.cc
+++ b/components/viz/common/quads/compositor_frame_transition_directive.cc
@@ -87,16 +87,4 @@
 CompositorFrameTransitionDirective::SharedElement::operator=(SharedElement&&) =
     default;
 
-bool CompositorFrameTransitionDirective::SharedElement::operator==(
-    const SharedElement& other) const {
-  return render_pass_id == other.render_pass_id &&
-         view_transition_element_resource_id ==
-             other.view_transition_element_resource_id;
-}
-
-bool CompositorFrameTransitionDirective::SharedElement::operator!=(
-    const SharedElement& other) const {
-  return !(other == *this);
-}
-
 }  // namespace viz
diff --git a/components/viz/common/quads/compositor_frame_transition_directive.h b/components/viz/common/quads/compositor_frame_transition_directive.h
index 9e7b551..94b7f37 100644
--- a/components/viz/common/quads/compositor_frame_transition_directive.h
+++ b/components/viz/common/quads/compositor_frame_transition_directive.h
@@ -49,8 +49,8 @@
     SharedElement(SharedElement&&);
     SharedElement& operator=(SharedElement&&);
 
-    bool operator==(const SharedElement& other) const;
-    bool operator!=(const SharedElement& other) const;
+    friend bool operator==(const SharedElement&,
+                           const SharedElement&) = default;
 
     // The render pass corresponding to a DOM element. The id is scoped to the
     // same frame that the directive corresponds to.
diff --git a/components/viz/common/quads/selection.h b/components/viz/common/quads/selection.h
index 0af57c9..b0ff04122 100644
--- a/components/viz/common/quads/selection.h
+++ b/components/viz/common/quads/selection.h
@@ -20,20 +20,11 @@
     return base::StringPrintf("Selection(%s, %s)", start.ToString().c_str(),
                               end.ToString().c_str());
   }
+
+  friend bool operator==(const Selection<BoundType>&,
+                         const Selection<BoundType>&) = default;
 };
 
-template <typename BoundType>
-inline bool operator==(const Selection<BoundType>& lhs,
-                       const Selection<BoundType>& rhs) {
-  return lhs.start == rhs.start && lhs.end == rhs.end;
-}
-
-template <typename BoundType>
-inline bool operator!=(const Selection<BoundType>& lhs,
-                       const Selection<BoundType>& rhs) {
-  return !(lhs == rhs);
-}
-
 }  // namespace viz
 
 #endif  // COMPONENTS_VIZ_COMMON_QUADS_SELECTION_H_
diff --git a/components/viz/common/resources/transferable_resource.h b/components/viz/common/resources/transferable_resource.h
index e249529..3484c3f2 100644
--- a/components/viz/common/resources/transferable_resource.h
+++ b/components/viz/common/resources/transferable_resource.h
@@ -164,7 +164,6 @@
            synchronization_type == o.synchronization_type &&
            resource_source == o.resource_source;
   }
-  bool operator!=(const TransferableResource& o) const { return !(*this == o); }
 
   // 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).
diff --git a/components/viz/common/surfaces/region_capture_bounds.cc b/components/viz/common/surfaces/region_capture_bounds.cc
index 454a38a..09fdff74 100644
--- a/components/viz/common/surfaces/region_capture_bounds.cc
+++ b/components/viz/common/surfaces/region_capture_bounds.cc
@@ -38,13 +38,6 @@
   bounds_.clear();
 }
 
-bool RegionCaptureBounds::operator==(const RegionCaptureBounds& rhs) const {
-  return bounds_ == rhs.bounds_;
-}
-bool RegionCaptureBounds::operator!=(const RegionCaptureBounds& rhs) const {
-  return !(*this == rhs);
-}
-
 std::string RegionCaptureBounds::ToString() const {
   std::ostringstream ss;
   ss << "{";
diff --git a/components/viz/common/surfaces/region_capture_bounds.h b/components/viz/common/surfaces/region_capture_bounds.h
index 414fe92..a788d8f 100644
--- a/components/viz/common/surfaces/region_capture_bounds.h
+++ b/components/viz/common/surfaces/region_capture_bounds.h
@@ -51,8 +51,8 @@
     return bounds_;
   }
 
-  bool operator==(const RegionCaptureBounds& rhs) const;
-  bool operator!=(const RegionCaptureBounds& rhs) const;
+  friend bool operator==(const RegionCaptureBounds&,
+                         const RegionCaptureBounds&) = default;
   std::string ToString() const;
 
  private:
diff --git a/components/viz/service/layers/layer_context_impl.cc b/components/viz/service/layers/layer_context_impl.cc
index 8dbdbe67..000792de 100644
--- a/components/viz/service/layers/layer_context_impl.cc
+++ b/components/viz/service/layers/layer_context_impl.cc
@@ -1308,6 +1308,8 @@
   layers.set_source_frame_number(update->source_frame_number);
   layers.set_trace_id(
       cc::BeginMainFrameTraceId::FromUnsafeValue(update->trace_id));
+  layers.set_primary_main_frame_item_sequence_number(
+      update->primary_main_frame_item_sequence_number);
   layers.SetDeviceViewportRect(update->device_viewport);
 
   if (update->page_scale_factor <= 0 || update->min_page_scale_factor <= 0 ||
diff --git a/components/viz/service/surfaces/surface_dependency_deadline.h b/components/viz/service/surfaces/surface_dependency_deadline.h
index cebe44be..c04b2dc1 100644
--- a/components/viz/service/surfaces/surface_dependency_deadline.h
+++ b/components/viz/service/surfaces/surface_dependency_deadline.h
@@ -48,9 +48,6 @@
   }
 
   bool operator==(const SurfaceDependencyDeadline& other) const;
-  bool operator!=(const SurfaceDependencyDeadline& other) const {
-    return !(*this == other);
-  }
 
  private:
   raw_ptr<const base::TickClock> tick_clock_;
diff --git a/components/viz/test/begin_frame_args_test.h b/components/viz/test/begin_frame_args_test.h
index 6cdb0016..495b5d3 100644
--- a/components/viz/test/begin_frame_args_test.h
+++ b/components/viz/test/begin_frame_args_test.h
@@ -59,8 +59,6 @@
 // operate on.
 
 // Allow "EXPECT_EQ(args1, args2);"
-// We don't define operator!= because EXPECT_NE(args1, args2) isn't all that
-// sensible.
 bool operator==(const BeginFrameArgs& lhs, const BeginFrameArgs& rhs);
 
 // Allow gtest to pretty print begin frame args.
diff --git a/components/webui/version/resources/about_version.html b/components/webui/version/resources/about_version.html
index bd2914d8..e4ad426e 100644
--- a/components/webui/version/resources/about_version.html
+++ b/components/webui/version/resources/about_version.html
@@ -99,10 +99,6 @@
           <td class="label">APK targetSdkVersion</td>
           <td class="version">$i18n{target_sdk_version}</td>
         </tr>
-         <tr>
-          <td class="label">isAtLeastU/targetsAtLeastU</td>
-          <td class="version">$i18n{targets_u}</td>
-        </tr>
         <tr>
           <td class="label">$i18n{gms_name}</td>
           <td class="version" id="gms_version">
diff --git a/components/webui/version/version_ui_constants.cc b/components/webui/version/version_ui_constants.cc
index 7c9bd75..3828e8e 100644
--- a/components/webui/version/version_ui_constants.cc
+++ b/components/webui/version/version_ui_constants.cc
@@ -57,8 +57,6 @@
 const char kVersionCode[] = "version_code";
 const char kTargetSdkVersionName[] = "target_sdk_version_name";
 const char kTargetSdkVersion[] = "target_sdk_version";
-const char kTargetsUName[] = "targets_u_name";
-const char kTargetsU[] = "targets_u";
 const char kGmsName[] = "gms_name";
 const char kGmsVersion[] = "gms_version";
 #endif
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 4008b7bd..d8896295 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -261,6 +261,28 @@
   base::test::ScopedFeatureList feature_list_;
 };
 
+class SelectNestedInputDumpAccessibilityTreeTest
+    : public DumpAccessibilityTreeTest {
+ protected:
+  SelectNestedInputDumpAccessibilityTreeTest() {
+    feature_list_.InitWithFeatures(
+        {{blink::features::kCustomizableSelect,
+          blink::features::kSelectParserRelaxation,
+          blink::features::kSelectAccessibilityNestedInput}},
+        {/* disabled_features */});
+  }
+
+  ~SelectNestedInputDumpAccessibilityTreeTest() override {
+    // Ensure that the feature lists are destroyed in the same order they
+    // were created in.
+    scoped_feature_list_.Reset();
+    feature_list_.Reset();
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 class OnScreenModeDumpAccessibilityTreeTest : public DumpAccessibilityTreeTest {
 };
 
@@ -310,6 +332,12 @@
 
 INSTANTIATE_TEST_SUITE_P(
     All,
+    SelectNestedInputDumpAccessibilityTreeTest,
+    ::testing::ValuesIn(DumpAccessibilityTestBase::TreeTestPasses()),
+    DumpAccessibilityTreeTestPassToString());
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
     OnScreenModeDumpAccessibilityTreeTest,
     ::testing::ValuesIn(DumpAccessibilityTestBase::TreeTestPasses()),
     DumpAccessibilityTreeTestPassToString());
@@ -2316,6 +2344,11 @@
   RunHtmlTest(FILE_PATH_LITERAL("select-with-input.html"));
 }
 
+IN_PROC_BROWSER_TEST_P(SelectNestedInputDumpAccessibilityTreeTest,
+                       AccessibilityCustomSelectWithInput) {
+  RunHtmlTest(FILE_PATH_LITERAL("select-with-input-2.html"));
+}
+
 IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeTest, AccessibilityDd) {
   RunHtmlTest(FILE_PATH_LITERAL("dd.html"));
 }
diff --git a/content/browser/back_forward_cache_internal_browsertest.cc b/content/browser/back_forward_cache_internal_browsertest.cc
index 85a599d..b2e29c8 100644
--- a/content/browser/back_forward_cache_internal_browsertest.cc
+++ b/content/browser/back_forward_cache_internal_browsertest.cc
@@ -3948,472 +3948,6 @@
   }
 }
 
-class BackgroundForegroundProcessLimitBackForwardCacheBrowserTest
-    : public BackForwardCacheBrowserTest {
- protected:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    EnableFeatureAndSetParams(features::kBackForwardCache, "cache_size",
-                              base::NumberToString(kBackForwardCacheSize));
-    EnableFeatureAndSetParams(
-        features::kBackForwardCache, "foreground_cache_size",
-        base::NumberToString(kForegroundBackForwardCacheSize));
-    BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
-  }
-
-  void ExpectCached(const RenderFrameHostImplWrapper& rfh,
-                    bool cached,
-                    bool backgrounded) {
-    EXPECT_FALSE(rfh.IsDestroyed());
-    EXPECT_EQ(cached, rfh->IsInBackForwardCache());
-    EXPECT_EQ(backgrounded, rfh->GetProcess()->GetPriority() ==
-                                base::Process::Priority::kBestEffort);
-  }
-  // The number of pages the BackForwardCache can hold per tab.
-  const size_t kBackForwardCacheSize = 4;
-  const size_t kForegroundBackForwardCacheSize = 2;
-  const size_t kPruneSize = 1u;
-  const NotRestoredReason kPruneReason =
-      NotRestoredReason::kCacheLimitPrunedOnModerateMemoryPressure;
-};
-
-// Test that a series of same-site navigations (which use the same process)
-// uses the foreground limit.
-IN_PROC_BROWSER_TEST_F(
-    BackgroundForegroundProcessLimitBackForwardCacheBrowserTest,
-    CacheEvictionSameSite) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  std::vector<RenderFrameHostImplWrapper> rfhs;
-
-  for (size_t i = 0; i <= kBackForwardCacheSize * 2; ++i) {
-    SCOPED_TRACE(i);
-    GURL url(embedded_test_server()->GetURL(
-        "a.com", base::StringPrintf("/title1.html?i=%zu", i)));
-    ASSERT_TRUE(NavigateToURL(shell(), url));
-    rfhs.emplace_back(current_frame_host());
-    EXPECT_NE(rfhs.back()->GetProcess()->GetPriority(),
-              base::Process::Priority::kBestEffort);
-
-    for (size_t j = 0; j <= i; ++j) {
-      SCOPED_TRACE(j);
-      // The last page is active, the previous |kForegroundBackForwardCacheSize|
-      // should be in the cache, any before that should be deleted.
-      if (i - j <= kForegroundBackForwardCacheSize) {
-        // All of the processes should be in the foreground.
-        ExpectCached(rfhs[j], /*cached=*/i != j,
-                     /*backgrounded=*/false);
-      } else {
-        ASSERT_TRUE(rfhs[j].WaitUntilRenderFrameDeleted());
-      }
-    }
-  }
-
-  // Navigate back but not to the initial about:blank.
-  for (size_t i = 0; i <= kBackForwardCacheSize * 2 - 1; ++i) {
-    SCOPED_TRACE(i);
-    ASSERT_TRUE(HistoryGoBack(web_contents()));
-    // The first |kBackForwardCacheSize| navigations should be restored from the
-    // cache. The rest should not.
-    if (i < kForegroundBackForwardCacheSize) {
-      ExpectRestored(FROM_HERE);
-    } else {
-      ExpectNotRestored({NotRestoredReason::kForegroundCacheLimit}, {}, {}, {},
-                        {}, FROM_HERE);
-    }
-  }
-}
-
-// Test that a series of cross-site navigations (which use different processes)
-// use the background limit.
-//
-// TODO(crbug.com/40179515): This test is flaky. It has been reenabled with
-// improved failure output (https://crrev.com/c/2862346). It's OK to disable it
-// again when it fails.
-IN_PROC_BROWSER_TEST_F(
-    BackgroundForegroundProcessLimitBackForwardCacheBrowserTest,
-    CacheEvictionCrossSite) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  std::vector<RenderFrameHostImplWrapper> rfhs;
-
-  for (size_t i = 0; i <= kBackForwardCacheSize * 2; ++i) {
-    SCOPED_TRACE(i);
-    // Note: do NOT use .com domains here because a4.com is on the HSTS preload
-    // list, which will cause our test requests to timeout.
-    GURL url(embedded_test_server()->GetURL(base::StringPrintf("a%zu.test", i),
-                                            "/title1.html"));
-    ASSERT_TRUE(NavigateToURL(shell(), url));
-    rfhs.emplace_back(current_frame_host());
-    EXPECT_NE(rfhs.back()->GetProcess()->GetPriority(),
-              base::Process::Priority::kBestEffort);
-
-    for (size_t j = 0; j <= i; ++j) {
-      SCOPED_TRACE(j);
-      // The last page is active, the previous |kBackgroundBackForwardCacheSize|
-      // should be in the cache, any before that should be deleted.
-      if (i - j <= kBackForwardCacheSize) {
-        EXPECT_FALSE(rfhs[j].IsDestroyed());
-        // Pages except the active one should be cached and in the background.
-        ExpectCached(rfhs[j], /*cached=*/i != j,
-                     /*backgrounded=*/i != j);
-      } else {
-        ASSERT_TRUE(rfhs[j].WaitUntilRenderFrameDeleted());
-      }
-    }
-  }
-
-  // Navigate back but not to the initial about:blank.
-  for (size_t i = 0; i <= kBackForwardCacheSize * 2 - 1; ++i) {
-    SCOPED_TRACE(i);
-    ASSERT_TRUE(HistoryGoBack(web_contents()));
-    // The first |kBackForwardCacheSize| navigations should be restored from the
-    // cache. The rest should not.
-    if (i < kBackForwardCacheSize) {
-      ExpectRestored(FROM_HERE);
-    } else {
-      ExpectNotRestored({NotRestoredReason::kCacheLimit}, {}, {}, {}, {},
-                        FROM_HERE);
-    }
-  }
-}
-
-// Test that pruning a series of cross-site navigations (which use different
-// processes) evicts the right entries with the right reason.
-IN_PROC_BROWSER_TEST_F(
-    BackgroundForegroundProcessLimitBackForwardCacheBrowserTest,
-    PruneCrossSite) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  std::vector<RenderFrameHostImplWrapper> rfhs;
-
-  for (size_t i = 0; i < kBackForwardCacheSize; ++i) {
-    SCOPED_TRACE(i);
-    // Note: do NOT use .com domains here because a4.com is on the HSTS preload
-    // list, which will cause our test requests to timeout.
-    GURL url(embedded_test_server()->GetURL(base::StringPrintf("a%zu.test", i),
-                                            "/title1.html"));
-    ASSERT_TRUE(NavigateToURL(shell(), url));
-    rfhs.emplace_back(current_frame_host());
-    EXPECT_NE(rfhs.back()->GetProcess()->GetPriority(),
-              base::Process::Priority::kBestEffort);
-  }
-
-  CHECK_LE(kPruneSize, kBackForwardCacheSize);
-
-  // Prune the BFCache entries.
-  web_contents()->GetController().GetBackForwardCache().Prune(kPruneSize,
-                                                              kPruneReason);
-
-  for (int i = kBackForwardCacheSize - 1 - 1 - kPruneSize; i >= 0; --i) {
-    SCOPED_TRACE(i);
-    ASSERT_TRUE(rfhs[i].WaitUntilRenderFrameDeleted());
-  }
-
-  // Navigate back but not to the initial about:blank.
-  for (size_t i = 0; i < kBackForwardCacheSize - 1; ++i) {
-    SCOPED_TRACE(i);
-    ASSERT_TRUE(HistoryGoBack(web_contents()));
-    // The first `kPruneSize` navigation should be restored from the cache. The
-    // rest should not.
-    if (i < kPruneSize) {
-      ExpectRestored(FROM_HERE);
-    } else {
-      ExpectNotRestored({kPruneReason}, {}, {}, {}, {}, FROM_HERE);
-    }
-  }
-}
-
-namespace {
-
-const char kPrioritizedPageURL[] = "search.result";
-
-}  // namespace
-
-class BackForwardCacheLimitForPrioritizedPagesBrowserTest
-    : public BackgroundForegroundProcessLimitBackForwardCacheBrowserTest {
- protected:
-  // Mock subclass of ContentBrowserClient that will determine if the url is
-  // prioritized by checking against `kPrioritizedPageURL`.
-  class MockContentBrowserClientWithPrioritizedBackForwardCacheEntry
-      : public ContentBrowserTestContentBrowserClient {
-   public:
-    // ContentBrowserClient overrides:
-    bool ShouldPrioritizeForBackForwardCache(BrowserContext* browser_context,
-                                             const GURL& url) override {
-      return url.DomainIs(kPrioritizedPageURL);
-    }
-  };
-
-  void SetUpOnMainThread() override {
-    BackgroundForegroundProcessLimitBackForwardCacheBrowserTest::
-        SetUpOnMainThread();
-    test_client_ = std::make_unique<
-        MockContentBrowserClientWithPrioritizedBackForwardCacheEntry>();
-  }
-
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    BackgroundForegroundProcessLimitBackForwardCacheBrowserTest::
-        SetUpCommandLine(command_line);
-    feature_list_.InitAndEnableFeature(
-        content::kBackForwardCachePrioritizedEntry);
-  }
-
- private:
-  std::unique_ptr<MockContentBrowserClientWithPrioritizedBackForwardCacheEntry>
-      test_client_;
-  base::test::ScopedFeatureList feature_list_;
-};
-
-// Test that both prioritized entries and non-prioritized entries would be
-// evicted when pruning with size 0.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
-                       PruneToZero) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  // We need at least 4 entries in the BFCache list for this test.
-  CHECK_GE(kBackForwardCacheSize, 2u);
-
-  ASSERT_TRUE(NavigateToURL(
-      shell(), embedded_test_server()->GetURL("a.test", "/title1.html")));
-  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
-                                         kPrioritizedPageURL, "/title1.html")));
-  ASSERT_TRUE(NavigateToURL(
-      shell(), embedded_test_server()->GetURL("b.test", "/title1.html")));
-
-  // Now the BFCache entry list is: [a, pp, b].
-  // Prune the BFCache entries to 0.
-  web_contents()->GetController().GetBackForwardCache().Prune(0, kPruneReason);
-  // All the entries should be evicted
-  for (size_t i = 0; i < 2; ++i) {
-    ASSERT_TRUE(HistoryGoBack(web_contents()));
-    ExpectNotRestored({kPruneReason}, {}, {}, {}, {}, FROM_HERE);
-  }
-}
-
-// Test that when pruning with a positive number size, the last prioritized
-// entry outside the limit will not be evicted.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
-                       PruneToNonZero_PrioritizedEntryOutsideLimit) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  // We need at least 4 entries in the BFCache list for this test.
-  CHECK_GE(kBackForwardCacheSize, 4u);
-
-  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
-                                         kPrioritizedPageURL, "/title1.html")));
-  ASSERT_TRUE(NavigateToURL(
-      shell(), embedded_test_server()->GetURL("a.test", "/title1.html")));
-  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
-                                         kPrioritizedPageURL, "/title2.html")));
-  ASSERT_TRUE(NavigateToURL(
-      shell(), embedded_test_server()->GetURL("b.test", "/title1.html")));
-  ASSERT_TRUE(NavigateToURL(
-      shell(), embedded_test_server()->GetURL("c.test", "/title1.html")));
-
-  // Now the BFCache entry list is: [pe1, a, pe2, b].
-  // Prune the BFCache entries to 1, the result should be:
-  // [pe1(evicted), a(evicted), pe2(prioritized entry special rule), b].
-  web_contents()->GetController().GetBackForwardCache().Prune(1, kPruneReason);
-
-  // The last non-prioritized entry should be restored because it's within the
-  // cache limit.
-  ASSERT_TRUE(HistoryGoBack(web_contents()));
-  ExpectRestored(FROM_HERE);
-  // The last prioritized entry should be restored since it's the special
-  // prioritized entry.
-  ASSERT_TRUE(HistoryGoBack(web_contents()));
-  ExpectRestored(FROM_HERE);
-  // The other entries (including the prioritized one) should not be restored.
-  for (size_t i = 0; i < 2; ++i) {
-    ASSERT_TRUE(HistoryGoBack(web_contents()));
-    ExpectNotRestored({kPruneReason}, {}, {}, {}, {}, FROM_HERE);
-  }
-}
-
-// Test that when pruning with a positive number size, the last prioritized
-// entry inside the limit should be counted as the regular cache.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
-                       PruneToNonZero_PrioritizedEntryInsideLimit) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  // We need at least 2 entries in the BFCache list for this test.
-  CHECK_GE(kBackForwardCacheSize, 2u);
-
-  ASSERT_TRUE(NavigateToURL(
-      shell(), embedded_test_server()->GetURL("a.test", "/title1.html")));
-  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
-                                         kPrioritizedPageURL, "/title2.html")));
-  ASSERT_TRUE(NavigateToURL(
-      shell(), embedded_test_server()->GetURL("b.test", "/title1.html")));
-
-  // Now the BFCache entry list is: [a, pe].
-  // Prune the BFCache entries to 1, the result should be:
-  // [a(evicted), pe].
-  web_contents()->GetController().GetBackForwardCache().Prune(1, kPruneReason);
-  ASSERT_TRUE(HistoryGoBack(web_contents()));
-  ExpectRestored(FROM_HERE);
-  ASSERT_TRUE(HistoryGoBack(web_contents()));
-  ExpectNotRestored({kPruneReason}, {}, {}, {}, {}, FROM_HERE);
-}
-
-// Test that when pruning with a positive number size while there is already an
-// old prioritized entry kept in cache before, it will be replaced by the newer
-// prioritized entry.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
-                       PruneToNonZeroTwice) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  // We need at least 4 entries in the BFCache list for this test.
-  CHECK_LE(kBackForwardCacheSize, 4u);
-
-  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
-                                         kPrioritizedPageURL, "/title1.html")));
-  ASSERT_TRUE(NavigateToURL(
-      shell(), embedded_test_server()->GetURL("a.test", "/title1.html")));
-  ASSERT_TRUE(NavigateToURL(
-      shell(), embedded_test_server()->GetURL("b.test", "/title1.html")));
-
-  // Now the BFCache entry list is: [pe1, a].
-  // Prune the BFCache entries to 1, the result should still be
-  // [pe1(prioritized entry special rule), a].
-  web_contents()->GetController().GetBackForwardCache().Prune(1, kPruneReason);
-
-  // The last non-prioritized entry should be restored because it's within the
-  // cache limit.
-  ASSERT_TRUE(HistoryGoBack(web_contents()));
-  ExpectRestored(FROM_HERE);
-  // The last prioritized entry should be restored since it's the special
-  // prioritized entry.
-  ASSERT_TRUE(HistoryGoBack(web_contents()));
-  ExpectRestored(FROM_HERE);
-
-  ASSERT_TRUE(NavigateToURL(
-      shell(), embedded_test_server()->GetURL("c.test", "/title1.html")));
-  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
-                                         kPrioritizedPageURL, "/title2.html")));
-  ASSERT_TRUE(NavigateToURL(
-      shell(), embedded_test_server()->GetURL("d.test", "/title1.html")));
-  ASSERT_TRUE(NavigateToURL(
-      shell(), embedded_test_server()->GetURL("e.test", "/title1.html")));
-
-  // Now the BFCache entry list is: [pe1, c, pe2, d].
-  // Prune the BFCache entries to 1, the result should still be
-  // [pe1(evicted), a(evicted), pe2(prioritized entry special rule), d].
-  web_contents()->GetController().GetBackForwardCache().Prune(1, kPruneReason);
-
-  // The last non-prioritized entry should be restored because it's within the
-  // cache limit.
-  ASSERT_TRUE(HistoryGoBack(web_contents()));
-  ExpectRestored(FROM_HERE);
-  // The last prioritized entry should be restored since it's the special
-  // prioritized entry.
-  ASSERT_TRUE(HistoryGoBack(web_contents()));
-  ExpectRestored(FROM_HERE);
-  // The other entries (including the prioritized one) should not be restored.
-  for (size_t i = 0; i < 2; ++i) {
-    ASSERT_TRUE(HistoryGoBack(web_contents()));
-    ExpectNotRestored({kPruneReason}, {}, {}, {}, {}, FROM_HERE);
-  }
-}
-
-// Test that the prioritized BFCache entry will not be evicted even when another
-// entry is stored and exceeds the limit.
-IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
-                       CacheLimitReached) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  // We need at least 1 entry in the BFCache list for this test.
-  CHECK_GE(kBackForwardCacheSize, 1u);
-
-  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
-                                         kPrioritizedPageURL, "/title1.html")));
-
-  // Fill the BFCache with more entry and make it just exceeds the limit, the
-  // result should be:
-  // [pe(prioritized entry special rule), a0, a1, ...].
-  for (size_t i = 0; i <= kBackForwardCacheSize; ++i) {
-    ASSERT_TRUE(NavigateToURL(
-        shell(), embedded_test_server()->GetURL(
-                     base::StringPrintf("a%zu.test", i), "/title1.html")));
-  }
-  // For the entries within cache size limit, they should be restored.
-  for (size_t i = 0; i < kBackForwardCacheSize; ++i) {
-    ASSERT_TRUE(HistoryGoBack(web_contents()));
-    ExpectRestored(FROM_HERE);
-  }
-  // The prioritized entry should be restored as well even if it's outside the
-  // limit.
-  ASSERT_TRUE(HistoryGoBack(web_contents()));
-  ExpectRestored(FROM_HERE);
-}
-
-// Test that the cache responds to processes switching from background to
-// foreground. We set things up so that we have
-// Cached sites:
-//   a0.test
-//   a1.test
-//   a2.test
-//   a3.test
-// and the active page is a4.test. Then set the process for a[1-3] to
-// foregrounded so that there are 3 entries whose processes are foregrounded.
-// BFCache should evict the eldest (a1) leaving a0 because despite being older,
-// it is backgrounded. Setting the priority directly is not ideal but there is
-// no reliable way to cause the processes to go into the foreground just by
-// navigating because proactive browsing instance swap makes it impossible to
-// reliably create a new a1.test renderer in the same process as the old
-// a1.test.
-//
-// Note that we do NOT use .com domains because a4.com is on the HSTS preload
-// list.  Since our test server doesn't use HTTPS, using a4.com results in the
-// test timing out.
-IN_PROC_BROWSER_TEST_F(
-    BackgroundForegroundProcessLimitBackForwardCacheBrowserTest,
-    ChangeToForeground) {
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  std::vector<RenderFrameHostImplWrapper> rfhs;
-
-  // Navigate through a[0-3].com.
-  for (size_t i = 0; i < kBackForwardCacheSize; ++i) {
-    SCOPED_TRACE(i);
-    GURL url(embedded_test_server()->GetURL(base::StringPrintf("a%zu.test", i),
-                                            "/title1.html"));
-    ASSERT_TRUE(NavigateToURL(shell(), url));
-    rfhs.emplace_back(current_frame_host());
-    EXPECT_NE(rfhs.back()->GetProcess()->GetPriority(),
-              base::Process::Priority::kBestEffort);
-  }
-  // Check that a0-2 are cached and backgrounded.
-  for (size_t i = 0; i < kBackForwardCacheSize - 1; ++i) {
-    SCOPED_TRACE(i);
-    ExpectCached(rfhs[i], /*cached=*/true, /*backgrounded=*/true);
-  }
-
-  // Navigate to a page which causes the processes for a[1-3] to be
-  // foregrounded.
-  GURL url(embedded_test_server()->GetURL("a4.test", "/title1.html"));
-  ASSERT_TRUE(NavigateToURL(shell(), url));
-
-  // Assert that we really have set up the situation we want where the processes
-  // are shared and in the foreground.
-  RenderFrameHostImpl* rfh = current_frame_host();
-  ASSERT_NE(rfh->GetProcess()->GetPriority(),
-            base::Process::Priority::kBestEffort);
-
-  rfhs[1]->GetProcess()->OnMediaStreamAdded();
-  rfhs[2]->GetProcess()->OnMediaStreamAdded();
-  rfhs[3]->GetProcess()->OnMediaStreamAdded();
-
-  // The page should be evicted.
-  ASSERT_TRUE(rfhs[1].WaitUntilRenderFrameDeleted());
-
-  // Check that a0 is cached and backgrounded.
-  ExpectCached(rfhs[0], /*cached=*/true, /*backgrounded=*/true);
-  // Check that a2-3 are cached and foregrounded.
-  ExpectCached(rfhs[2], /*cached=*/true, /*backgrounded=*/false);
-  ExpectCached(rfhs[3], /*cached=*/true, /*backgrounded=*/false);
-}
-
 // Test that the BackForwardCacheTimeToLiveControl feature works and takes
 // precedence over the default value
 // `kDefaultTimeToLiveInBackForwardCacheInSeconds`.
diff --git a/content/browser/back_forward_cache_limit_browsertest.cc b/content/browser/back_forward_cache_limit_browsertest.cc
new file mode 100644
index 0000000..bb5a75c5
--- /dev/null
+++ b/content/browser/back_forward_cache_limit_browsertest.cc
@@ -0,0 +1,481 @@
+// 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/command_line.h"
+#include "base/functional/bind.h"
+#include "base/strings/string_number_conversions.h"
+#include "content/browser/back_forward_cache_browsertest.h"
+#include "content/public/common/content_features.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+
+namespace content {
+
+class BackgroundForegroundProcessLimitBackForwardCacheBrowserTest
+    : public BackForwardCacheBrowserTest {
+ protected:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    EnableFeatureAndSetParams(features::kBackForwardCache, "cache_size",
+                              base::NumberToString(kBackForwardCacheSize));
+    EnableFeatureAndSetParams(
+        features::kBackForwardCache, "foreground_cache_size",
+        base::NumberToString(kForegroundBackForwardCacheSize));
+    BackForwardCacheBrowserTest::SetUpCommandLine(command_line);
+  }
+
+  void ExpectCached(const RenderFrameHostImplWrapper& rfh,
+                    bool cached,
+                    bool backgrounded) {
+    EXPECT_FALSE(rfh.IsDestroyed());
+    EXPECT_EQ(cached, rfh->IsInBackForwardCache());
+    EXPECT_EQ(backgrounded, rfh->GetProcess()->GetPriority() ==
+                                base::Process::Priority::kBestEffort);
+  }
+  // The number of pages the BackForwardCache can hold per tab.
+  const size_t kBackForwardCacheSize = 4;
+  const size_t kForegroundBackForwardCacheSize = 2;
+  const size_t kPruneSize = 1u;
+  const NotRestoredReason kPruneReason =
+      NotRestoredReason::kCacheLimitPrunedOnModerateMemoryPressure;
+};
+
+// Test that a series of same-site navigations (which use the same process)
+// uses the foreground limit.
+IN_PROC_BROWSER_TEST_F(
+    BackgroundForegroundProcessLimitBackForwardCacheBrowserTest,
+    CacheEvictionSameSite) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  std::vector<RenderFrameHostImplWrapper> rfhs;
+
+  for (size_t i = 0; i <= kBackForwardCacheSize * 2; ++i) {
+    SCOPED_TRACE(i);
+    GURL url(embedded_test_server()->GetURL(
+        "a.com", base::StringPrintf("/title1.html?i=%zu", i)));
+    ASSERT_TRUE(NavigateToURL(shell(), url));
+    rfhs.emplace_back(current_frame_host());
+    EXPECT_NE(rfhs.back()->GetProcess()->GetPriority(),
+              base::Process::Priority::kBestEffort);
+
+    for (size_t j = 0; j <= i; ++j) {
+      SCOPED_TRACE(j);
+      // The last page is active, the previous |kForegroundBackForwardCacheSize|
+      // should be in the cache, any before that should be deleted.
+      if (i - j <= kForegroundBackForwardCacheSize) {
+        // All of the processes should be in the foreground.
+        ExpectCached(rfhs[j], /*cached=*/i != j,
+                     /*backgrounded=*/false);
+      } else {
+        ASSERT_TRUE(rfhs[j].WaitUntilRenderFrameDeleted());
+      }
+    }
+  }
+
+  // Navigate back but not to the initial about:blank.
+  for (size_t i = 0; i <= kBackForwardCacheSize * 2 - 1; ++i) {
+    SCOPED_TRACE(i);
+    ASSERT_TRUE(HistoryGoBack(web_contents()));
+    // The first |kBackForwardCacheSize| navigations should be restored from the
+    // cache. The rest should not.
+    if (i < kForegroundBackForwardCacheSize) {
+      ExpectRestored(FROM_HERE);
+    } else {
+      ExpectNotRestored({NotRestoredReason::kForegroundCacheLimit}, {}, {}, {},
+                        {}, FROM_HERE);
+    }
+  }
+}
+
+// Test that a series of cross-site navigations (which use different processes)
+// use the background limit.
+//
+// TODO(crbug.com/40179515): This test is flaky. It has been re-enabled with
+// improved failure output (https://crrev.com/c/2862346). It's OK to disable it
+// again when it fails.
+IN_PROC_BROWSER_TEST_F(
+    BackgroundForegroundProcessLimitBackForwardCacheBrowserTest,
+    CacheEvictionCrossSite) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  std::vector<RenderFrameHostImplWrapper> rfhs;
+
+  for (size_t i = 0; i <= kBackForwardCacheSize * 2; ++i) {
+    SCOPED_TRACE(i);
+    // Note: do NOT use .com domains here because a4.com is on the HSTS preload
+    // list, which will cause our test requests to timeout.
+    GURL url(embedded_test_server()->GetURL(base::StringPrintf("a%zu.test", i),
+                                            "/title1.html"));
+    ASSERT_TRUE(NavigateToURL(shell(), url));
+    rfhs.emplace_back(current_frame_host());
+    EXPECT_NE(rfhs.back()->GetProcess()->GetPriority(),
+              base::Process::Priority::kBestEffort);
+
+    for (size_t j = 0; j <= i; ++j) {
+      SCOPED_TRACE(j);
+      // The last page is active, the previous |kBackgroundBackForwardCacheSize|
+      // should be in the cache, any before that should be deleted.
+      if (i - j <= kBackForwardCacheSize) {
+        EXPECT_FALSE(rfhs[j].IsDestroyed());
+        // Pages except the active one should be cached and in the background.
+        ExpectCached(rfhs[j], /*cached=*/i != j,
+                     /*backgrounded=*/i != j);
+      } else {
+        ASSERT_TRUE(rfhs[j].WaitUntilRenderFrameDeleted());
+      }
+    }
+  }
+
+  // Navigate back but not to the initial about:blank.
+  for (size_t i = 0; i <= kBackForwardCacheSize * 2 - 1; ++i) {
+    SCOPED_TRACE(i);
+    ASSERT_TRUE(HistoryGoBack(web_contents()));
+    // The first |kBackForwardCacheSize| navigations should be restored from the
+    // cache. The rest should not.
+    if (i < kBackForwardCacheSize) {
+      ExpectRestored(FROM_HERE);
+    } else {
+      ExpectNotRestored({NotRestoredReason::kCacheLimit}, {}, {}, {}, {},
+                        FROM_HERE);
+    }
+  }
+}
+
+// Test that pruning a series of cross-site navigations (which use different
+// processes) evicts the right entries with the right reason.
+IN_PROC_BROWSER_TEST_F(
+    BackgroundForegroundProcessLimitBackForwardCacheBrowserTest,
+    PruneCrossSite) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  std::vector<RenderFrameHostImplWrapper> rfhs;
+
+  for (size_t i = 0; i < kBackForwardCacheSize; ++i) {
+    SCOPED_TRACE(i);
+    // Note: do NOT use .com domains here because a4.com is on the HSTS preload
+    // list, which will cause our test requests to timeout.
+    GURL url(embedded_test_server()->GetURL(base::StringPrintf("a%zu.test", i),
+                                            "/title1.html"));
+    ASSERT_TRUE(NavigateToURL(shell(), url));
+    rfhs.emplace_back(current_frame_host());
+    EXPECT_NE(rfhs.back()->GetProcess()->GetPriority(),
+              base::Process::Priority::kBestEffort);
+  }
+
+  CHECK_LE(kPruneSize, kBackForwardCacheSize);
+
+  // Prune the BFCache entries.
+  web_contents()->GetController().GetBackForwardCache().Prune(kPruneSize,
+                                                              kPruneReason);
+
+  for (int i = kBackForwardCacheSize - 1 - 1 - kPruneSize; i >= 0; --i) {
+    SCOPED_TRACE(i);
+    ASSERT_TRUE(rfhs[i].WaitUntilRenderFrameDeleted());
+  }
+
+  // Navigate back but not to the initial about:blank.
+  for (size_t i = 0; i < kBackForwardCacheSize - 1; ++i) {
+    SCOPED_TRACE(i);
+    ASSERT_TRUE(HistoryGoBack(web_contents()));
+    // The first `kPruneSize` navigation should be restored from the cache. The
+    // rest should not.
+    if (i < kPruneSize) {
+      ExpectRestored(FROM_HERE);
+    } else {
+      ExpectNotRestored({kPruneReason}, {}, {}, {}, {}, FROM_HERE);
+    }
+  }
+}
+
+namespace {
+
+const char kPrioritizedPageURL[] = "search.result";
+
+}  // namespace
+
+class BackForwardCacheLimitForPrioritizedPagesBrowserTest
+    : public BackgroundForegroundProcessLimitBackForwardCacheBrowserTest {
+ protected:
+  // Mock subclass of ContentBrowserClient that will determine if the url is
+  // prioritized by checking against `kPrioritizedPageURL`.
+  class MockContentBrowserClientWithPrioritizedBackForwardCacheEntry
+      : public ContentBrowserTestContentBrowserClient {
+   public:
+    // ContentBrowserClient overrides:
+    bool ShouldPrioritizeForBackForwardCache(BrowserContext* browser_context,
+                                             const GURL& url) override {
+      return url.DomainIs(kPrioritizedPageURL);
+    }
+  };
+
+  void SetUpOnMainThread() override {
+    BackgroundForegroundProcessLimitBackForwardCacheBrowserTest::
+        SetUpOnMainThread();
+    test_client_ = std::make_unique<
+        MockContentBrowserClientWithPrioritizedBackForwardCacheEntry>();
+  }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    BackgroundForegroundProcessLimitBackForwardCacheBrowserTest::
+        SetUpCommandLine(command_line);
+    feature_list_.InitAndEnableFeature(
+        content::kBackForwardCachePrioritizedEntry);
+  }
+
+ private:
+  std::unique_ptr<MockContentBrowserClientWithPrioritizedBackForwardCacheEntry>
+      test_client_;
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Test that both prioritized entries and non-prioritized entries would be
+// evicted when pruning with size 0.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
+                       PruneToZero) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // We need at least 4 entries in the BFCache list for this test.
+  CHECK_GE(kBackForwardCacheSize, 2u);
+
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("a.test", "/title1.html")));
+  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
+                                         kPrioritizedPageURL, "/title1.html")));
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("b.test", "/title1.html")));
+
+  // Now the BFCache entry list is: [a, pp, b].
+  // Prune the BFCache entries to 0.
+  web_contents()->GetController().GetBackForwardCache().Prune(0, kPruneReason);
+  // All the entries should be evicted
+  for (size_t i = 0; i < 2; ++i) {
+    ASSERT_TRUE(HistoryGoBack(web_contents()));
+    ExpectNotRestored({kPruneReason}, {}, {}, {}, {}, FROM_HERE);
+  }
+}
+
+// Test that when pruning with a positive number size, the last prioritized
+// entry outside the limit will not be evicted.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
+                       PruneToNonZero_PrioritizedEntryOutsideLimit) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // We need at least 4 entries in the BFCache list for this test.
+  CHECK_GE(kBackForwardCacheSize, 4u);
+
+  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
+                                         kPrioritizedPageURL, "/title1.html")));
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("a.test", "/title1.html")));
+  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
+                                         kPrioritizedPageURL, "/title2.html")));
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("b.test", "/title1.html")));
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("c.test", "/title1.html")));
+
+  // Now the BFCache entry list is: [pe1, a, pe2, b].
+  // Prune the BFCache entries to 1, the result should be:
+  // [pe1(evicted), a(evicted), pe2(prioritized entry special rule), b].
+  web_contents()->GetController().GetBackForwardCache().Prune(1, kPruneReason);
+
+  // The last non-prioritized entry should be restored because it's within the
+  // cache limit.
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+  ExpectRestored(FROM_HERE);
+  // The last prioritized entry should be restored since it's the special
+  // prioritized entry.
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+  ExpectRestored(FROM_HERE);
+  // The other entries (including the prioritized one) should not be restored.
+  for (size_t i = 0; i < 2; ++i) {
+    ASSERT_TRUE(HistoryGoBack(web_contents()));
+    ExpectNotRestored({kPruneReason}, {}, {}, {}, {}, FROM_HERE);
+  }
+}
+
+// Test that when pruning with a positive number size, the last prioritized
+// entry inside the limit should be counted as the regular cache.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
+                       PruneToNonZero_PrioritizedEntryInsideLimit) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // We need at least 2 entries in the BFCache list for this test.
+  CHECK_GE(kBackForwardCacheSize, 2u);
+
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("a.test", "/title1.html")));
+  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
+                                         kPrioritizedPageURL, "/title2.html")));
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("b.test", "/title1.html")));
+
+  // Now the BFCache entry list is: [a, pe].
+  // Prune the BFCache entries to 1, the result should be:
+  // [a(evicted), pe].
+  web_contents()->GetController().GetBackForwardCache().Prune(1, kPruneReason);
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+  ExpectRestored(FROM_HERE);
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+  ExpectNotRestored({kPruneReason}, {}, {}, {}, {}, FROM_HERE);
+}
+
+// Test that when pruning with a positive number size while there is already an
+// old prioritized entry kept in cache before, it will be replaced by the newer
+// prioritized entry.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
+                       PruneToNonZeroTwice) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // We need at least 4 entries in the BFCache list for this test.
+  CHECK_LE(kBackForwardCacheSize, 4u);
+
+  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
+                                         kPrioritizedPageURL, "/title1.html")));
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("a.test", "/title1.html")));
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("b.test", "/title1.html")));
+
+  // Now the BFCache entry list is: [pe1, a].
+  // Prune the BFCache entries to 1, the result should still be
+  // [pe1(prioritized entry special rule), a].
+  web_contents()->GetController().GetBackForwardCache().Prune(1, kPruneReason);
+
+  // The last non-prioritized entry should be restored because it's within the
+  // cache limit.
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+  ExpectRestored(FROM_HERE);
+  // The last prioritized entry should be restored since it's the special
+  // prioritized entry.
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+  ExpectRestored(FROM_HERE);
+
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("c.test", "/title1.html")));
+  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
+                                         kPrioritizedPageURL, "/title2.html")));
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("d.test", "/title1.html")));
+  ASSERT_TRUE(NavigateToURL(
+      shell(), embedded_test_server()->GetURL("e.test", "/title1.html")));
+
+  // Now the BFCache entry list is: [pe1, c, pe2, d].
+  // Prune the BFCache entries to 1, the result should still be
+  // [pe1(evicted), a(evicted), pe2(prioritized entry special rule), d].
+  web_contents()->GetController().GetBackForwardCache().Prune(1, kPruneReason);
+
+  // The last non-prioritized entry should be restored because it's within the
+  // cache limit.
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+  ExpectRestored(FROM_HERE);
+  // The last prioritized entry should be restored since it's the special
+  // prioritized entry.
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+  ExpectRestored(FROM_HERE);
+  // The other entries (including the prioritized one) should not be restored.
+  for (size_t i = 0; i < 2; ++i) {
+    ASSERT_TRUE(HistoryGoBack(web_contents()));
+    ExpectNotRestored({kPruneReason}, {}, {}, {}, {}, FROM_HERE);
+  }
+}
+
+// Test that the prioritized BFCache entry will not be evicted even when another
+// entry is stored and exceeds the limit.
+IN_PROC_BROWSER_TEST_F(BackForwardCacheLimitForPrioritizedPagesBrowserTest,
+                       CacheLimitReached) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // We need at least 1 entry in the BFCache list for this test.
+  CHECK_GE(kBackForwardCacheSize, 1u);
+
+  ASSERT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
+                                         kPrioritizedPageURL, "/title1.html")));
+
+  // Fill the BFCache with more entry and make it just exceeds the limit, the
+  // result should be:
+  // [pe(prioritized entry special rule), a0, a1, ...].
+  for (size_t i = 0; i <= kBackForwardCacheSize; ++i) {
+    ASSERT_TRUE(NavigateToURL(
+        shell(), embedded_test_server()->GetURL(
+                     base::StringPrintf("a%zu.test", i), "/title1.html")));
+  }
+  // For the entries within cache size limit, they should be restored.
+  for (size_t i = 0; i < kBackForwardCacheSize; ++i) {
+    ASSERT_TRUE(HistoryGoBack(web_contents()));
+    ExpectRestored(FROM_HERE);
+  }
+  // The prioritized entry should be restored as well even if it's outside the
+  // limit.
+  ASSERT_TRUE(HistoryGoBack(web_contents()));
+  ExpectRestored(FROM_HERE);
+}
+
+// Test that the cache responds to processes switching from background to
+// foreground. We set things up so that we have
+// Cached sites:
+//   a0.test
+//   a1.test
+//   a2.test
+//   a3.test
+// and the active page is a4.test. Then set the process for a[1-3] to
+// foregrounded so that there are 3 entries whose processes are foregrounded.
+// BFCache should evict the eldest (a1) leaving a0 because despite being older,
+// it is backgrounded. Setting the priority directly is not ideal but there is
+// no reliable way to cause the processes to go into the foreground just by
+// navigating because proactive browsing instance swap makes it impossible to
+// reliably create a new a1.test renderer in the same process as the old
+// a1.test.
+//
+// Note that we do NOT use .com domains because a4.com is on the HSTS preload
+// list.  Since our test server doesn't use HTTPS, using a4.com results in the
+// test timing out.
+IN_PROC_BROWSER_TEST_F(
+    BackgroundForegroundProcessLimitBackForwardCacheBrowserTest,
+    ChangeToForeground) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  std::vector<RenderFrameHostImplWrapper> rfhs;
+
+  // Navigate through a[0-3].com.
+  for (size_t i = 0; i < kBackForwardCacheSize; ++i) {
+    SCOPED_TRACE(i);
+    GURL url(embedded_test_server()->GetURL(base::StringPrintf("a%zu.test", i),
+                                            "/title1.html"));
+    ASSERT_TRUE(NavigateToURL(shell(), url));
+    rfhs.emplace_back(current_frame_host());
+    EXPECT_NE(rfhs.back()->GetProcess()->GetPriority(),
+              base::Process::Priority::kBestEffort);
+  }
+  // Check that a0-2 are cached and backgrounded.
+  for (size_t i = 0; i < kBackForwardCacheSize - 1; ++i) {
+    SCOPED_TRACE(i);
+    ExpectCached(rfhs[i], /*cached=*/true, /*backgrounded=*/true);
+  }
+
+  // Navigate to a page which causes the processes for a[1-3] to be
+  // foregrounded.
+  GURL url(embedded_test_server()->GetURL("a4.test", "/title1.html"));
+  ASSERT_TRUE(NavigateToURL(shell(), url));
+
+  // Assert that we really have set up the situation we want where the processes
+  // are shared and in the foreground.
+  RenderFrameHostImpl* rfh = current_frame_host();
+  ASSERT_NE(rfh->GetProcess()->GetPriority(),
+            base::Process::Priority::kBestEffort);
+
+  rfhs[1]->GetProcess()->OnMediaStreamAdded();
+  rfhs[2]->GetProcess()->OnMediaStreamAdded();
+  rfhs[3]->GetProcess()->OnMediaStreamAdded();
+
+  // The page should be evicted.
+  ASSERT_TRUE(rfhs[1].WaitUntilRenderFrameDeleted());
+
+  // Check that a0 is cached and backgrounded.
+  ExpectCached(rfhs[0], /*cached=*/true, /*backgrounded=*/true);
+  // Check that a2-3 are cached and foregrounded.
+  ExpectCached(rfhs[2], /*cached=*/true, /*backgrounded=*/false);
+  ExpectCached(rfhs[3], /*cached=*/true, /*backgrounded=*/false);
+}
+
+}  // namespace content
diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc
index f2d1e043..455c2b66 100644
--- a/content/browser/child_process_launcher.cc
+++ b/content/browser/child_process_launcher.cc
@@ -164,12 +164,12 @@
 void ChildProcessLauncher::SetProcessPriority(
     base::Process::Priority priority) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
-  base::Process to_pass = process_.process.Duplicate();
-  GetProcessLauncherTaskRunner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(
-          &ChildProcessLauncherHelper::SetProcessPriorityOnLauncherThread,
-          helper_, std::move(to_pass), priority));
+
+  if (priority == priority_) {
+    return;
+  }
+
+  SetProcessPriorityImpl(priority);
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
@@ -186,6 +186,18 @@
 
   if (process_.process.IsValid()) {
     process_start_time_ = base::TimeTicks::Now();
+
+#if BUILDFLAG(IS_MAC)
+    // On mac, the task port is required to change the priority of the child
+    // process.
+    auto* port_provider = ChildProcessTaskPortProvider::GetInstance();
+    CHECK(port_provider);
+    CHECK(port_provider->TaskForHandle(process_.process.Handle()) ==
+          MACH_PORT_NULL);
+    scoped_port_provider_observation_.Observe(port_provider);
+#endif
+
+    // Note:: May delete |this|.
     client_->OnProcessLaunched();
   } else {
     termination_info_.status = base::TERMINATION_STATUS_LAUNCH_FAILED;
@@ -199,6 +211,35 @@
   }
 }
 
+#if BUILDFLAG(IS_MAC)
+void ChildProcessLauncher::OnReceivedTaskPort(
+    base::ProcessHandle process_handle) {
+  if (!process_.process.IsValid()) {
+    // The process has died since. No need to keep observing for task ports.
+    scoped_port_provider_observation_.Reset();
+    return;
+  }
+
+  if (process_.process.Handle() == process_handle && priority_) {
+    SetProcessPriorityImpl(*priority_);
+    scoped_port_provider_observation_.Reset();
+  }
+}
+#endif
+
+#if !BUILDFLAG(IS_ANDROID)
+void ChildProcessLauncher::SetProcessPriorityImpl(
+    base::Process::Priority priority) {
+  priority_ = priority;
+  base::Process to_pass = process_.process.Duplicate();
+  GetProcessLauncherTaskRunner()->PostTask(
+      FROM_HERE,
+      base::BindOnce(
+          &ChildProcessLauncherHelper::SetProcessPriorityOnLauncherThread,
+          helper_, std::move(to_pass), priority));
+}
+#endif
+
 bool ChildProcessLauncher::IsStarting() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   return starting_;
diff --git a/content/browser/child_process_launcher.h b/content/browser/child_process_launcher.h
index ac6f72c9..4c3d991 100644
--- a/content/browser/child_process_launcher.h
+++ b/content/browser/child_process_launcher.h
@@ -39,6 +39,11 @@
 #include "base/files/scoped_file.h"
 #endif
 
+#if BUILDFLAG(IS_MAC)
+#include "base/process/port_provider_mac.h"
+#include "base/scoped_observation.h"
+#endif
+
 namespace base {
 class CommandLine;
 class UnsafeSharedMemoryRegion;
@@ -208,7 +213,14 @@
 // Launches a process asynchronously and notifies the client of the process
 // handle when it's available.  It's used to avoid blocking the calling thread
 // on the OS since often it can take > 100 ms to create the process.
-class CONTENT_EXPORT ChildProcessLauncher {
+
+// On MacOS, observes the PortProvider to allow re-setting the priority of the
+// process when its task port is available.
+class CONTENT_EXPORT ChildProcessLauncher
+#if BUILDFLAG(IS_MAC)
+    : public base::PortProvider::Observer
+#endif
+{
  public:
   class CONTENT_EXPORT Client {
    public:
@@ -255,7 +267,11 @@
   ChildProcessLauncher(const ChildProcessLauncher&) = delete;
   ChildProcessLauncher& operator=(const ChildProcessLauncher&) = delete;
 
-  ~ChildProcessLauncher();
+  ~ChildProcessLauncher()
+#if BUILDFLAG(IS_MAC)
+      override
+#endif
+      ;
 
   // True if the process is being launched and so the handle isn't available.
   bool IsStarting();
@@ -315,6 +331,15 @@
 #endif
               int error_code);
 
+#if BUILDFLAG(IS_MAC)
+  // base::PortProvider::Observer:
+  void OnReceivedTaskPort(base::ProcessHandle process_handle) override;
+#endif
+
+#if !BUILDFLAG(IS_ANDROID)
+  void SetProcessPriorityImpl(base::Process::Priority priority);
+#endif
+
   raw_ptr<Client> client_;
 
   // The process associated with this ChildProcessLauncher. Set in Notify by
@@ -335,6 +360,18 @@
 
   scoped_refptr<internal::ChildProcessLauncherHelper> helper_;
 
+  // The priority of the process. The state is stored to avoid changing the
+  // setting repeatedly.
+  //
+  // On MacOS, this is also used to re-set the priority when the task port of
+  // the process is available.
+  std::optional<base::Process::Priority> priority_;
+
+#if BUILDFLAG(IS_MAC)
+  base::ScopedObservation<base::PortProvider, base::PortProvider::Observer>
+      scoped_port_provider_observation_{this};
+#endif
+
   base::WeakPtrFactory<ChildProcessLauncher> weak_factory_{this};
 };
 
diff --git a/content/browser/child_process_launcher_helper.h b/content/browser/child_process_launcher_helper.h
index 9f387b3..e65d866 100644
--- a/content/browser/child_process_launcher_helper.h
+++ b/content/browser/child_process_launcher_helper.h
@@ -308,12 +308,6 @@
   std::optional<base::ProcessId> process_id_ = std::nullopt;
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
-#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
-  // The priority of the process. The state is stored to avoid changing the
-  // setting repeatedly.
-  std::optional<base::Process::Priority> priority_;
-#endif
-
   // The PlatformChannel that will be used to transmit an invitation to the
   // child process in most cases. Only used if the platform's helper
   // implementation doesn't return a server endpoint from
diff --git a/content/browser/child_process_launcher_helper_linux.cc b/content/browser/child_process_launcher_helper_linux.cc
index add20a2f..75cafbe 100644
--- a/content/browser/child_process_launcher_helper_linux.cc
+++ b/content/browser/child_process_launcher_helper_linux.cc
@@ -176,8 +176,7 @@
     base::Process process,
     base::Process::Priority priority) {
   DCHECK(CurrentlyOnProcessLauncherTaskRunner());
-  if (process.CanSetPriority() && priority_ != priority) {
-    priority_ = priority;
+  if (process.CanSetPriority()) {
     process.SetPriority(priority);
   }
 }
diff --git a/content/browser/child_process_launcher_helper_win.cc b/content/browser/child_process_launcher_helper_win.cc
index cb0e7d5..0791b531 100644
--- a/content/browser/child_process_launcher_helper_win.cc
+++ b/content/browser/child_process_launcher_helper_win.cc
@@ -209,8 +209,7 @@
     base::Process process,
     base::Process::Priority priority) {
   DCHECK(CurrentlyOnProcessLauncherTaskRunner());
-  if (process.CanSetPriority() && priority_ != priority) {
-    priority_ = priority;
+  if (process.CanSetPriority()) {
     process.SetPriority(priority);
   }
 }
diff --git a/content/browser/cookie_store/OWNERS b/content/browser/cookie_store/OWNERS
index 20d73976..baf338e 100644
--- a/content/browser/cookie_store/OWNERS
+++ b/content/browser/cookie_store/OWNERS
@@ -1,3 +1,4 @@
+dylancutler@google.com
 fergal@chromium.org
 horo@chromium.org
 leimy@chromium.org
diff --git a/content/browser/first_party_sets/database/first_party_sets_database.cc b/content/browser/first_party_sets/database/first_party_sets_database.cc
index ad02abc..d864ad4 100644
--- a/content/browser/first_party_sets/database/first_party_sets_database.cc
+++ b/content/browser/first_party_sets/database/first_party_sets_database.cc
@@ -448,8 +448,7 @@
       if (site.has_value() && primary.has_value() && site_type.has_value()) {
         entries.emplace_back(
             site.value(),
-            net::FirstPartySetEntry(primary.value(), site_type.value(),
-                                    /*site_index=*/std::nullopt));
+            net::FirstPartySetEntry(primary.value(), site_type.value()));
         validator.Update(site.value(), primary.value());
       }
     }
@@ -646,11 +645,10 @@
         entry_override =
             net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
                 maybe_primary_site.value(),
-                // TODO(crbug.com/40186153): May change to use the
-                // real site_type and site_index in the future, depending on
-                // the design details. Use kAssociated as default site type
-                // and null site index for now.
-                net::SiteType::kAssociated, std::nullopt));
+                // TODO(crbug.com/40186153): May change to use the real
+                // site_type in the future, depending on the design details. Use
+                // kAssociated as default site type for now.
+                net::SiteType::kAssociated));
       }
       results.emplace_back(std::move(site).value(), std::move(entry_override));
     }
@@ -725,11 +723,7 @@
       if (maybe_primary_site.has_value() && maybe_site_type.has_value()) {
         entry_override =
             net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                maybe_primary_site.value(),
-                // TODO(crbug.com/40186153): May change to use the
-                // real site_index in the future, depending on the design
-                // details. Use null site index for now.
-                maybe_site_type.value(), std::nullopt));
+                maybe_primary_site.value(), maybe_site_type.value()));
       }
       results.emplace_back(std::move(site).value(), std::move(entry_override));
     }
diff --git a/content/browser/first_party_sets/database/first_party_sets_database_unittest.cc b/content/browser/first_party_sets/database/first_party_sets_database_unittest.cc
index d825955..9682fd65 100644
--- a/content/browser/first_party_sets/database/first_party_sets_database_unittest.cc
+++ b/content/browser/first_party_sets/database/first_party_sets_database_unittest.cc
@@ -317,18 +317,18 @@
       /*entries=*/
       {{net::SchemefulSite(GURL(site)),
         net::FirstPartySetEntry(net::SchemefulSite(GURL(primary)),
-                                net::SiteType::kAssociated, std::nullopt)},
+                                net::SiteType::kAssociated)},
        {net::SchemefulSite(GURL(primary)),
         net::FirstPartySetEntry(net::SchemefulSite(GURL(primary)),
-                                net::SiteType::kPrimary, std::nullopt)}},
+                                net::SiteType::kPrimary)}},
       /*aliases=*/{});
   base::flat_map<net::SchemefulSite, net::FirstPartySetEntry> manual_sets = {
       {net::SchemefulSite(GURL(manual_site)),
        net::FirstPartySetEntry(net::SchemefulSite(GURL(manual_primary)),
-                               net::SiteType::kAssociated, std::nullopt)},
+                               net::SiteType::kAssociated)},
       {net::SchemefulSite(GURL(manual_primary)),
        net::FirstPartySetEntry(net::SchemefulSite(GURL(manual_primary)),
-                               net::SiteType::kPrimary, std::nullopt)}};
+                               net::SiteType::kPrimary)}};
   global_sets.ApplyManuallySpecifiedSet(
       net::LocalSetDeclaration::Create(/*set_entries=*/manual_sets,
                                        /*aliases=*/{})
@@ -337,9 +337,9 @@
   net::FirstPartySetsContextConfig config =
       net::FirstPartySetsContextConfig::Create(
           {{net::SchemefulSite(GURL(site_member1)),
-            net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                net::SchemefulSite(GURL(primary_site)),
-                net::SiteType::kAssociated, std::nullopt))},
+            net::FirstPartySetEntryOverride(
+                net::FirstPartySetEntry(net::SchemefulSite(GURL(primary_site)),
+                                        net::SiteType::kAssociated))},
            {net::SchemefulSite(GURL(site_member2)),
             net::FirstPartySetEntryOverride()}})
           .value();
@@ -435,19 +435,19 @@
       /*entries=*/
       {{net::SchemefulSite(GURL(site)),
         net::FirstPartySetEntry(net::SchemefulSite(GURL(primary)),
-                                net::SiteType::kAssociated, std::nullopt)},
+                                net::SiteType::kAssociated)},
        {net::SchemefulSite(GURL(primary)),
         net::FirstPartySetEntry(net::SchemefulSite(GURL(primary)),
-                                net::SiteType::kPrimary, std::nullopt)}},
+                                net::SiteType::kPrimary)}},
       /*aliases=*/{});
 
   base::flat_map<net::SchemefulSite, net::FirstPartySetEntry> manual_sets = {
       {net::SchemefulSite(GURL(manual_site)),
        net::FirstPartySetEntry(net::SchemefulSite(GURL(manual_primary)),
-                               net::SiteType::kAssociated, std::nullopt)},
+                               net::SiteType::kAssociated)},
       {net::SchemefulSite(GURL(manual_primary)),
        net::FirstPartySetEntry(net::SchemefulSite(GURL(manual_primary)),
-                               net::SiteType::kPrimary, std::nullopt)}};
+                               net::SiteType::kPrimary)}};
   global_sets.ApplyManuallySpecifiedSet(
       net::LocalSetDeclaration::Create(/*set_entries=*/manual_sets,
                                        /*aliases=*/{})
@@ -456,9 +456,9 @@
   net::FirstPartySetsContextConfig config =
       net::FirstPartySetsContextConfig::Create(
           {{net::SchemefulSite(GURL(site_member1)),
-            net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                net::SchemefulSite(GURL(primary_site)),
-                net::SiteType::kAssociated, std::nullopt))},
+            net::FirstPartySetEntryOverride(
+                net::FirstPartySetEntry(net::SchemefulSite(GURL(primary_site)),
+                                        net::SiteType::kAssociated))},
            {net::SchemefulSite(GURL(site_member2)),
             net::FirstPartySetEntryOverride()}})
           .value();
@@ -586,19 +586,19 @@
       /*entries=*/
       {{net::SchemefulSite(GURL(site)),
         net::FirstPartySetEntry(net::SchemefulSite(GURL(primary)),
-                                net::SiteType::kAssociated, std::nullopt)},
+                                net::SiteType::kAssociated)},
        {net::SchemefulSite(GURL(primary)),
         net::FirstPartySetEntry(net::SchemefulSite(GURL(primary)),
-                                net::SiteType::kPrimary, std::nullopt)}},
+                                net::SiteType::kPrimary)}},
       /*aliases=*/{});
 
   base::flat_map<net::SchemefulSite, net::FirstPartySetEntry> manual_sets = {
       {net::SchemefulSite(GURL(manual_site)),
        net::FirstPartySetEntry(net::SchemefulSite(GURL(manual_primary)),
-                               net::SiteType::kAssociated, std::nullopt)},
+                               net::SiteType::kAssociated)},
       {net::SchemefulSite(GURL(manual_primary)),
        net::FirstPartySetEntry(net::SchemefulSite(GURL(manual_primary)),
-                               net::SiteType::kPrimary, std::nullopt)}};
+                               net::SiteType::kPrimary)}};
   global_sets.ApplyManuallySpecifiedSet(
       net::LocalSetDeclaration::Create(/*set_entries=*/manual_sets,
                                        /*aliases=*/{})
@@ -607,9 +607,9 @@
   net::FirstPartySetsContextConfig config =
       net::FirstPartySetsContextConfig::Create(
           {{net::SchemefulSite(GURL(site_member1)),
-            net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                net::SchemefulSite(GURL(primary_site)),
-                net::SiteType::kAssociated, std::nullopt))},
+            net::FirstPartySetEntryOverride(
+                net::FirstPartySetEntry(net::SchemefulSite(GURL(primary_site)),
+                                        net::SiteType::kAssociated))},
            {net::SchemefulSite(GURL(site_member2)),
             net::FirstPartySetEntryOverride()}})
           .value();
@@ -720,10 +720,10 @@
       /*entries=*/
       {{net::SchemefulSite(GURL(site)),
         net::FirstPartySetEntry(net::SchemefulSite(GURL(primary)),
-                                net::SiteType::kAssociated, std::nullopt)},
+                                net::SiteType::kAssociated)},
        {net::SchemefulSite(GURL(primary)),
         net::FirstPartySetEntry(net::SchemefulSite(GURL(primary)),
-                                net::SiteType::kPrimary, std::nullopt)}},
+                                net::SiteType::kPrimary)}},
       /*aliases=*/{});
 
   OpenDatabase();
@@ -989,19 +989,15 @@
   net::GlobalFirstPartySets global_sets(
       base::Version(),
       /*entries=*/
-      {{site, net::FirstPartySetEntry(primary, net::SiteType::kAssociated,
-                                      std::nullopt)},
-       {primary, net::FirstPartySetEntry(primary, net::SiteType::kPrimary,
-                                         std::nullopt)}},
+      {{site, net::FirstPartySetEntry(primary, net::SiteType::kAssociated)},
+       {primary, net::FirstPartySetEntry(primary, net::SiteType::kPrimary)}},
       /*aliases=*/{});
 
   base::flat_map<net::SchemefulSite, net::FirstPartySetEntry> manual_sets = {
       {manual_site,
-       net::FirstPartySetEntry(manual_primary, net::SiteType::kAssociated,
-                               std::nullopt)},
+       net::FirstPartySetEntry(manual_primary, net::SiteType::kAssociated)},
       {manual_primary,
-       net::FirstPartySetEntry(manual_primary, net::SiteType::kPrimary,
-                               std::nullopt)}};
+       net::FirstPartySetEntry(manual_primary, net::SiteType::kPrimary)}};
   global_sets.ApplyManuallySpecifiedSet(
       net::LocalSetDeclaration::Create(/*set_entries=*/manual_sets,
                                        /*aliases=*/{})
@@ -1022,12 +1018,10 @@
       res->first.FindEntries({manual_site, manual_primary},
                              net::FirstPartySetsContextConfig()),
       UnorderedElementsAre(
-          Pair(manual_site,
-               net::FirstPartySetEntry(
-                   manual_primary, net::SiteType::kAssociated, std::nullopt)),
-          Pair(manual_primary,
-               net::FirstPartySetEntry(manual_primary, net::SiteType::kPrimary,
-                                       std::nullopt))));
+          Pair(manual_site, net::FirstPartySetEntry(
+                                manual_primary, net::SiteType::kAssociated)),
+          Pair(manual_primary, net::FirstPartySetEntry(
+                                   manual_primary, net::SiteType::kPrimary))));
   EXPECT_TRUE(res->second.empty());
 }
 
@@ -1054,13 +1048,12 @@
       res = db()->GetGlobalSetsAndConfig("b0");
   EXPECT_TRUE(res.has_value());
   // The singleton set should be deleted.
-  EXPECT_THAT(res->first.FindEntries({aaa, bbb, ccc, ddd},
-                                     net::FirstPartySetsContextConfig()),
-              UnorderedElementsAre(
-                  Pair(ccc, net::FirstPartySetEntry(
-                                ddd, net::SiteType::kAssociated, std::nullopt)),
-                  Pair(ddd, net::FirstPartySetEntry(
-                                ddd, net::SiteType::kPrimary, std::nullopt))));
+  EXPECT_THAT(
+      res->first.FindEntries({aaa, bbb, ccc, ddd},
+                             net::FirstPartySetsContextConfig()),
+      UnorderedElementsAre(
+          Pair(ccc, net::FirstPartySetEntry(ddd, net::SiteType::kAssociated)),
+          Pair(ddd, net::FirstPartySetEntry(ddd, net::SiteType::kPrimary))));
   EXPECT_EQ(res->second, net::FirstPartySetsContextConfig());
 }
 
@@ -1087,13 +1080,12 @@
       res = db()->GetGlobalSetsAndConfig("b0");
   EXPECT_TRUE(res.has_value());
   // The singleton set should be deleted.
-  EXPECT_THAT(res->first.FindEntries({aaa, bbb, ccc, ddd},
-                                     net::FirstPartySetsContextConfig()),
-              UnorderedElementsAre(
-                  Pair(ccc, net::FirstPartySetEntry(
-                                ddd, net::SiteType::kAssociated, std::nullopt)),
-                  Pair(ddd, net::FirstPartySetEntry(
-                                ddd, net::SiteType::kPrimary, std::nullopt))));
+  EXPECT_THAT(
+      res->first.FindEntries({aaa, bbb, ccc, ddd},
+                             net::FirstPartySetsContextConfig()),
+      UnorderedElementsAre(
+          Pair(ccc, net::FirstPartySetEntry(ddd, net::SiteType::kAssociated)),
+          Pair(ddd, net::FirstPartySetEntry(ddd, net::SiteType::kPrimary))));
   EXPECT_EQ(res->second, net::FirstPartySetsContextConfig());
 }
 
@@ -1119,17 +1111,14 @@
       std::pair<net::GlobalFirstPartySets, net::FirstPartySetsContextConfig>>
       res = db()->GetGlobalSetsAndConfig("b0");
   EXPECT_TRUE(res.has_value());
-  EXPECT_THAT(res->first.FindEntries({aaa, bbb, ccc, ddd},
-                                     net::FirstPartySetsContextConfig()),
-              UnorderedElementsAre(
-                  Pair(aaa, net::FirstPartySetEntry(
-                                bbb, net::SiteType::kAssociated, std::nullopt)),
-                  Pair(bbb, net::FirstPartySetEntry(
-                                bbb, net::SiteType::kPrimary, std::nullopt)),
-                  Pair(ccc, net::FirstPartySetEntry(
-                                ddd, net::SiteType::kAssociated, std::nullopt)),
-                  Pair(ddd, net::FirstPartySetEntry(
-                                ddd, net::SiteType::kPrimary, std::nullopt))));
+  EXPECT_THAT(
+      res->first.FindEntries({aaa, bbb, ccc, ddd},
+                             net::FirstPartySetsContextConfig()),
+      UnorderedElementsAre(
+          Pair(aaa, net::FirstPartySetEntry(bbb, net::SiteType::kAssociated)),
+          Pair(bbb, net::FirstPartySetEntry(bbb, net::SiteType::kPrimary)),
+          Pair(ccc, net::FirstPartySetEntry(ddd, net::SiteType::kAssociated)),
+          Pair(ddd, net::FirstPartySetEntry(ddd, net::SiteType::kPrimary))));
   EXPECT_EQ(res->second, net::FirstPartySetsContextConfig());
 }
 
@@ -1173,23 +1162,18 @@
       version,
       /*entries=*/
       {{associated_site,
-        net::FirstPartySetEntry(primary, net::SiteType::kAssociated,
-                                std::nullopt)},
-       {service_site, net::FirstPartySetEntry(primary, net::SiteType::kService,
-                                              std::nullopt)},
-       {primary, net::FirstPartySetEntry(primary, net::SiteType::kPrimary,
-                                         std::nullopt)}},
+        net::FirstPartySetEntry(primary, net::SiteType::kAssociated)},
+       {service_site,
+        net::FirstPartySetEntry(primary, net::SiteType::kService)},
+       {primary, net::FirstPartySetEntry(primary, net::SiteType::kPrimary)}},
       /*aliases=*/{});
   base::flat_map<net::SchemefulSite, net::FirstPartySetEntry> manual_sets = {
       {manual_associated_site,
-       net::FirstPartySetEntry(manual_primary, net::SiteType::kAssociated,
-                               std::nullopt)},
+       net::FirstPartySetEntry(manual_primary, net::SiteType::kAssociated)},
       {manual_service_site,
-       net::FirstPartySetEntry(manual_primary, net::SiteType::kService,
-                               std::nullopt)},
+       net::FirstPartySetEntry(manual_primary, net::SiteType::kService)},
       {manual_primary,
-       net::FirstPartySetEntry(manual_primary, net::SiteType::kPrimary,
-                               std::nullopt)}};
+       net::FirstPartySetEntry(manual_primary, net::SiteType::kPrimary)}};
   global_sets.ApplyManuallySpecifiedSet(
       net::LocalSetDeclaration::Create(/*set_entries=*/manual_sets,
                                        /*aliases=*/{})
@@ -1199,8 +1183,7 @@
       net::FirstPartySetsContextConfig::Create(
           {{config_site_member1,
             net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                config_primary_site, net::SiteType::kAssociated,
-                std::nullopt))},
+                config_primary_site, net::SiteType::kAssociated))},
            {config_site_member2, net::FirstPartySetEntryOverride()}})
           .value();
 
diff --git a/content/browser/first_party_sets/first_party_set_parser.cc b/content/browser/first_party_sets/first_party_set_parser.cc
index f455f30..49e2df3 100644
--- a/content/browser/first_party_sets/first_party_set_parser.cc
+++ b/content/browser/first_party_sets/first_party_set_parser.cc
@@ -256,9 +256,8 @@
     const net::SchemefulSite& primary = primary_result.site();
 
     std::vector<std::pair<net::SchemefulSite, net::FirstPartySetEntry>>
-        set_entries(
-            {{primary, net::FirstPartySetEntry(primary, net::SiteType::kPrimary,
-                                               std::nullopt)}});
+        set_entries({{primary, net::FirstPartySetEntry(
+                                   primary, net::SiteType::kPrimary)}});
 
     for (const SubsetDescriptor& descriptor : {
              SubsetDescriptor{
@@ -472,12 +471,7 @@
           static_cast<int>(index) < descriptor.size_limit.value()) {
         set_entries.emplace_back(
             site_result.site(),
-            net::FirstPartySetEntry(
-                primary, descriptor.site_type,
-                descriptor.size_limit.has_value()
-                    ? std::make_optional(
-                          net::FirstPartySetEntry::SiteIndex(index))
-                    : std::nullopt));
+            net::FirstPartySetEntry(primary, descriptor.site_type));
       }
       // Continue parsing even after we've reached the size limit (if there is
       // one), in order to surface malformed input domains as errors.
diff --git a/content/browser/first_party_sets/first_party_set_parser_unittest.cc b/content/browser/first_party_sets/first_party_set_parser_unittest.cc
index cdabda3..22cc4a9 100644
--- a/content/browser/first_party_sets/first_party_set_parser_unittest.cc
+++ b/content/browser/first_party_sets/first_party_set_parser_unittest.cc
@@ -105,18 +105,17 @@
   net::SchemefulSite example(GURL("https://example.test"));
   net::SchemefulSite aaaa(GURL("https://aaaa.test"));
 
-  EXPECT_EQ(
-      ParseSets(R"({"primary": "https://example.test",)"
-                R"("associatedSites": ["https://aaaa.test"]})"),
-      net::GlobalFirstPartySets(
-          kVersion,
-          {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
-              {aaaa,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-          },
-          {}));
+  EXPECT_EQ(ParseSets(R"({"primary": "https://example.test",)"
+                      R"("associatedSites": ["https://aaaa.test"]})"),
+            net::GlobalFirstPartySets(
+                kVersion,
+                {
+                    {example,
+                     net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+                    {aaaa, net::FirstPartySetEntry(example,
+                                                   net::SiteType::kAssociated)},
+                },
+                {}));
 }
 
 TEST(FirstPartySetParser, AcceptsMinimal_Service) {
@@ -129,10 +128,9 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
-              {aaaa, net::FirstPartySetEntry(example, net::SiteType::kService,
-                                             std::nullopt)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+              {aaaa, net::FirstPartySetEntry(example, net::SiteType::kService)},
           },
           {}));
 }
@@ -159,12 +157,10 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
-              {a,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-              {b, net::FirstPartySetEntry(example, net::SiteType::kService,
-                                          std::nullopt)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+              {a, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {b, net::FirstPartySetEntry(example, net::SiteType::kService)},
           },
           {{example_cctld, example}, {a_cctld, a}, {b_cctld, b}}));
   histogram_tester.ExpectUniqueSample(
@@ -220,10 +216,10 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example2, net::FirstPartySetEntry(
-                             example2, net::SiteType::kPrimary, std::nullopt)},
-              {associated2, net::FirstPartySetEntry(
-                                example2, net::SiteType::kAssociated, 0)},
+              {example2,
+               net::FirstPartySetEntry(example2, net::SiteType::kPrimary)},
+              {associated2,
+               net::FirstPartySetEntry(example2, net::SiteType::kAssociated)},
           },
           {}));
 }
@@ -247,14 +243,14 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example2, net::FirstPartySetEntry(
-                             example2, net::SiteType::kPrimary, std::nullopt)},
-              {associated2, net::FirstPartySetEntry(
-                                example2, net::SiteType::kAssociated, 0)},
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
+              {example2,
+               net::FirstPartySetEntry(example2, net::SiteType::kPrimary)},
+              {associated2,
+               net::FirstPartySetEntry(example2, net::SiteType::kAssociated)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
               {aaaa,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
           },
           {}));
   histogram_tester.ExpectUniqueSample(
@@ -280,10 +276,10 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example2, net::FirstPartySetEntry(
-                             example2, net::SiteType::kPrimary, std::nullopt)},
-              {associated2, net::FirstPartySetEntry(
-                                example2, net::SiteType::kAssociated, 0)},
+              {example2,
+               net::FirstPartySetEntry(example2, net::SiteType::kPrimary)},
+              {associated2,
+               net::FirstPartySetEntry(example2, net::SiteType::kAssociated)},
           },
           {}));
   histogram_tester.ExpectUniqueSample(
@@ -333,14 +329,14 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
               {associated1,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
-              {example2, net::FirstPartySetEntry(
-                             example2, net::SiteType::kPrimary, std::nullopt)},
-              {associated2, net::FirstPartySetEntry(
-                                example2, net::SiteType::kAssociated, 0)},
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {example2,
+               net::FirstPartySetEntry(example2, net::SiteType::kPrimary)},
+              {associated2,
+               net::FirstPartySetEntry(example2, net::SiteType::kAssociated)},
           },
           {}));
 }
@@ -360,14 +356,14 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
               {associated1,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
-              {example2, net::FirstPartySetEntry(
-                             example2, net::SiteType::kPrimary, std::nullopt)},
-              {associated2, net::FirstPartySetEntry(
-                                example2, net::SiteType::kAssociated, 0)},
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {example2,
+               net::FirstPartySetEntry(example2, net::SiteType::kPrimary)},
+              {associated2,
+               net::FirstPartySetEntry(example2, net::SiteType::kAssociated)},
           },
           {}));
 }
@@ -392,18 +388,18 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
               {associated1,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
-              {example2, net::FirstPartySetEntry(
-                             example2, net::SiteType::kPrimary, std::nullopt)},
-              {associated2, net::FirstPartySetEntry(
-                                example2, net::SiteType::kAssociated, 0)},
-              {example3, net::FirstPartySetEntry(
-                             example3, net::SiteType::kPrimary, std::nullopt)},
-              {associated3, net::FirstPartySetEntry(
-                                example3, net::SiteType::kAssociated, 0)},
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {example2,
+               net::FirstPartySetEntry(example2, net::SiteType::kPrimary)},
+              {associated2,
+               net::FirstPartySetEntry(example2, net::SiteType::kAssociated)},
+              {example3,
+               net::FirstPartySetEntry(example3, net::SiteType::kPrimary)},
+              {associated3,
+               net::FirstPartySetEntry(example3, net::SiteType::kAssociated)},
           },
           {}));
 }
@@ -412,18 +408,17 @@
   net::SchemefulSite example(GURL("https://example.test"));
   net::SchemefulSite aaaa(GURL("https://aaaa.test"));
 
-  EXPECT_EQ(
-      ParseSets(R"({"primary": "https://subdomain.example.test", )"
-                R"("associatedSites": ["https://aaaa.test"]})"),
-      net::GlobalFirstPartySets(
-          kVersion,
-          {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
-              {aaaa,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-          },
-          {}));
+  EXPECT_EQ(ParseSets(R"({"primary": "https://subdomain.example.test", )"
+                      R"("associatedSites": ["https://aaaa.test"]})"),
+            net::GlobalFirstPartySets(
+                kVersion,
+                {
+                    {example,
+                     net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+                    {aaaa, net::FirstPartySetEntry(example,
+                                                   net::SiteType::kAssociated)},
+                },
+                {}));
 }
 
 TEST(FirstPartySetParser, TruncatesPrimaryInvalidWithAlias) {
@@ -444,18 +439,17 @@
   net::SchemefulSite example(GURL("https://example.test"));
   net::SchemefulSite aaaa(GURL("https://aaaa.test"));
 
-  EXPECT_EQ(
-      ParseSets(R"({"primary": "https://example.test", )"
-                R"("associatedSites": ["https://subdomain.aaaa.test"]})"),
-      net::GlobalFirstPartySets(
-          kVersion,
-          {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
-              {aaaa,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-          },
-          {}));
+  EXPECT_EQ(ParseSets(R"({"primary": "https://example.test", )"
+                      R"("associatedSites": ["https://subdomain.aaaa.test"]})"),
+            net::GlobalFirstPartySets(
+                kVersion,
+                {
+                    {example,
+                     net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+                    {aaaa, net::FirstPartySetEntry(example,
+                                                   net::SiteType::kAssociated)},
+                },
+                {}));
 }
 
 TEST(FirstPartySetParser, TruncatesSubdomain_RepeatedDomain) {
@@ -482,14 +476,14 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
               {bbbb,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 2)},
-              {example2, net::FirstPartySetEntry(
-                             example2, net::SiteType::kPrimary, std::nullopt)},
-              {cccc, net::FirstPartySetEntry(example2,
-                                             net::SiteType::kAssociated, 0)},
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {example2,
+               net::FirstPartySetEntry(example2, net::SiteType::kPrimary)},
+              {cccc,
+               net::FirstPartySetEntry(example2, net::SiteType::kAssociated)},
           },
           {}));
 }
@@ -530,14 +524,14 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
               {bbbb,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
-              {example2, net::FirstPartySetEntry(
-                             example2, net::SiteType::kPrimary, std::nullopt)},
-              {cccc, net::FirstPartySetEntry(example2,
-                                             net::SiteType::kAssociated, 1)},
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {example2,
+               net::FirstPartySetEntry(example2, net::SiteType::kPrimary)},
+              {cccc,
+               net::FirstPartySetEntry(example2, net::SiteType::kAssociated)},
           },
           {}));
 
@@ -560,12 +554,12 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
               {bbbb,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
-              {example3, net::FirstPartySetEntry(
-                             example3, net::SiteType::kPrimary, std::nullopt)},
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {example3,
+               net::FirstPartySetEntry(example3, net::SiteType::kPrimary)},
           },
           {{example3_cctld, example3}}));
 
@@ -586,10 +580,10 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
               {bbbb,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
           },
           {}));
 }
@@ -609,14 +603,13 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
               {associated1,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-              {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
-                                            std::nullopt)},
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)},
               {associated2,
-               net::FirstPartySetEntry(foo, net::SiteType::kAssociated, 0)},
+               net::FirstPartySetEntry(foo, net::SiteType::kAssociated)},
           },
           {}));
   histogram_tester.ExpectUniqueSample(
@@ -645,14 +638,13 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
               {associated1,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-              {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
-                                            std::nullopt)},
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)},
               {associated2,
-               net::FirstPartySetEntry(foo, net::SiteType::kAssociated, 0)},
+               net::FirstPartySetEntry(foo, net::SiteType::kAssociated)},
           },
           {}));
   histogram_tester.ExpectUniqueSample(
@@ -689,10 +681,10 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
               {associated1,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
           },
           {}));
 }
@@ -770,14 +762,13 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
               {associated1,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-              {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
-                                            std::nullopt)},
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)},
               {associated2,
-               net::FirstPartySetEntry(foo, net::SiteType::kAssociated, 0)},
+               net::FirstPartySetEntry(foo, net::SiteType::kAssociated)},
           },
           {
               {associated1_cctld1, associated1},
@@ -1042,21 +1033,17 @@
           {
               {
                   {primary2,
-                   net::FirstPartySetEntry(primary2, net::SiteType::kPrimary,
-                                           std::nullopt)},
-                  {associated2,
-                   net::FirstPartySetEntry(primary2, net::SiteType::kAssociated,
-                                           std::nullopt)},
+                   net::FirstPartySetEntry(primary2, net::SiteType::kPrimary)},
+                  {associated2, net::FirstPartySetEntry(
+                                    primary2, net::SiteType::kAssociated)},
               },
           },
           {
               {
                   {primary3,
-                   net::FirstPartySetEntry(primary3, net::SiteType::kPrimary,
-                                           std::nullopt)},
-                  {associated3,
-                   net::FirstPartySetEntry(primary3, net::SiteType::kAssociated,
-                                           std::nullopt)},
+                   net::FirstPartySetEntry(primary3, net::SiteType::kPrimary)},
+                  {associated3, net::FirstPartySetEntry(
+                                    primary3, net::SiteType::kAssociated)},
               },
           },
           {}))));
@@ -1099,11 +1086,9 @@
           {
               {
                   {primary1,
-                   net::FirstPartySetEntry(primary1, net::SiteType::kPrimary,
-                                           std::nullopt)},
-                  {associated2,
-                   net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
-                                           std::nullopt)},
+                   net::FirstPartySetEntry(primary1, net::SiteType::kPrimary)},
+                  {associated2, net::FirstPartySetEntry(
+                                    primary1, net::SiteType::kAssociated)},
               },
           },
           {}, {}))));
@@ -1135,11 +1120,9 @@
           {
               {
                   {primary2,
-                   net::FirstPartySetEntry(primary2, net::SiteType::kPrimary,
-                                           std::nullopt)},
-                  {associated2,
-                   net::FirstPartySetEntry(primary2, net::SiteType::kAssociated,
-                                           std::nullopt)},
+                   net::FirstPartySetEntry(primary2, net::SiteType::kPrimary)},
+                  {associated2, net::FirstPartySetEntry(
+                                    primary2, net::SiteType::kAssociated)},
               },
           },
           {}, {}))));
@@ -1300,18 +1283,16 @@
             )");
   const FirstPartySetsOverridesPolicy want_policy(net::SetsMutation(
       {{
-           {primary1, net::FirstPartySetEntry(primary1, net::SiteType::kPrimary,
-                                              std::nullopt)},
+           {primary1,
+            net::FirstPartySetEntry(primary1, net::SiteType::kPrimary)},
            {associated_site1,
-            net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
-                                    std::nullopt)},
+            net::FirstPartySetEntry(primary1, net::SiteType::kAssociated)},
        },
        {
-           {primary2, net::FirstPartySetEntry(primary2, net::SiteType::kPrimary,
-                                              std::nullopt)},
+           {primary2,
+            net::FirstPartySetEntry(primary2, net::SiteType::kPrimary)},
            {associated_site2,
-            net::FirstPartySetEntry(primary2, net::SiteType::kAssociated,
-                                    std::nullopt)},
+            net::FirstPartySetEntry(primary2, net::SiteType::kAssociated)},
        }},
       {}, {}));
   EXPECT_THAT(FirstPartySetParser::ParseSetsFromEnterprisePolicy(policy_value),
@@ -1349,25 +1330,22 @@
             )");
   const FirstPartySetsOverridesPolicy want_policy(net::SetsMutation(
       {{
-           {primary1, net::FirstPartySetEntry(primary1, net::SiteType::kPrimary,
-                                              std::nullopt)},
+           {primary1,
+            net::FirstPartySetEntry(primary1, net::SiteType::kPrimary)},
            {associated_site1,
-            net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
-                                    std::nullopt)},
+            net::FirstPartySetEntry(primary1, net::SiteType::kAssociated)},
        },
        {
-           {primary2, net::FirstPartySetEntry(primary2, net::SiteType::kPrimary,
-                                              std::nullopt)},
+           {primary2,
+            net::FirstPartySetEntry(primary2, net::SiteType::kPrimary)},
            {associated_site2,
-            net::FirstPartySetEntry(primary2, net::SiteType::kAssociated,
-                                    std::nullopt)},
+            net::FirstPartySetEntry(primary2, net::SiteType::kAssociated)},
        }},
       {{
-          {primary3, net::FirstPartySetEntry(primary3, net::SiteType::kPrimary,
-                                             std::nullopt)},
+          {primary3,
+           net::FirstPartySetEntry(primary3, net::SiteType::kPrimary)},
           {associatedSite3,
-           net::FirstPartySetEntry(primary3, net::SiteType::kAssociated,
-                                   std::nullopt)},
+           net::FirstPartySetEntry(primary3, net::SiteType::kAssociated)},
       }},
       {}));
   EXPECT_THAT(FirstPartySetParser::ParseSetsFromEnterprisePolicy(policy_value),
@@ -1415,11 +1393,10 @@
             )");
   const FirstPartySetsOverridesPolicy want_policy(net::SetsMutation(
       {{
-          {primary1, net::FirstPartySetEntry(primary1, net::SiteType::kPrimary,
-                                             std::nullopt)},
+          {primary1,
+           net::FirstPartySetEntry(primary1, net::SiteType::kPrimary)},
           {associated1,
-           net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
-                                   std::nullopt)},
+           net::FirstPartySetEntry(primary1, net::SiteType::kAssociated)},
       }},
       {}, {}));
   EXPECT_THAT(FirstPartySetParser::ParseSetsFromEnterprisePolicy(policy_value),
@@ -1472,34 +1449,29 @@
   const FirstPartySetsOverridesPolicy want_policy(net::SetsMutation(
       /*replacement_sets=*/
       {{
-           {primary1, net::FirstPartySetEntry(primary1, net::SiteType::kPrimary,
-                                              std::nullopt)},
+           {primary1,
+            net::FirstPartySetEntry(primary1, net::SiteType::kPrimary)},
            {associated_site1,
-            net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
-                                    std::nullopt)},
+            net::FirstPartySetEntry(primary1, net::SiteType::kAssociated)},
            {associated_site1_cctld,
-            net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
-                                    std::nullopt)},
+            net::FirstPartySetEntry(primary1, net::SiteType::kAssociated)},
        },
        {
-           {primary2, net::FirstPartySetEntry(primary2, net::SiteType::kPrimary,
-                                              std::nullopt)},
+           {primary2,
+            net::FirstPartySetEntry(primary2, net::SiteType::kPrimary)},
            {primary2_cctld,
-            net::FirstPartySetEntry(primary2, net::SiteType::kPrimary,
-                                    std::nullopt)},
+            net::FirstPartySetEntry(primary2, net::SiteType::kPrimary)},
            {associated_site2,
-            net::FirstPartySetEntry(primary2, net::SiteType::kAssociated,
-                                    std::nullopt)},
+            net::FirstPartySetEntry(primary2, net::SiteType::kAssociated)},
        }},
       /*addition_sets=*/
       {{
-          {primary3, net::FirstPartySetEntry(primary3, net::SiteType::kPrimary,
-                                             std::nullopt)},
-          {service3, net::FirstPartySetEntry(primary3, net::SiteType::kService,
-                                             std::nullopt)},
+          {primary3,
+           net::FirstPartySetEntry(primary3, net::SiteType::kPrimary)},
+          {service3,
+           net::FirstPartySetEntry(primary3, net::SiteType::kService)},
           {service3_cctld,
-           net::FirstPartySetEntry(primary3, net::SiteType::kService,
-                                   std::nullopt)},
+           net::FirstPartySetEntry(primary3, net::SiteType::kService)},
       }},
       /*aliases=*/
       {
@@ -1533,18 +1505,13 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
-              {a,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-              {b,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
-              {c,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 2)},
-              {d,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 3)},
-              {e,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 4)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+              {a, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {b, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {c, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {d, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {e, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
           },
           {}));
 }
@@ -1577,22 +1544,15 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
-              {a,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-              {d,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
-              {e,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 2)},
-              {f,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 3)},
-              {g,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 4)},
-              {b, net::FirstPartySetEntry(example, net::SiteType::kService,
-                                          std::nullopt)},
-              {c, net::FirstPartySetEntry(example, net::SiteType::kService,
-                                          std::nullopt)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+              {a, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {d, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {e, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {f, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {g, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {b, net::FirstPartySetEntry(example, net::SiteType::kService)},
+              {c, net::FirstPartySetEntry(example, net::SiteType::kService)},
           },
           {}));
 }
@@ -1620,18 +1580,13 @@
       net::GlobalFirstPartySets(
           kVersion,
           {
-              {example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)},
-              {a,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-              {b,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
-              {c,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 2)},
-              {d,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 3)},
-              {e,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 4)},
+              {example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+              {a, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {b, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {c, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {d, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+              {e, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
           },
           {{a_cctld1, a}, {a_cctld2, a}}));
 }
@@ -1666,26 +1621,20 @@
       FirstPartySetParser::ParseSetsFromEnterprisePolicy(policy_value).first,
       base::ok(FirstPartySetsOverridesPolicy(net::SetsMutation(
           {{
-              {primary1, net::FirstPartySetEntry(
-                             primary1, net::SiteType::kPrimary, std::nullopt)},
+              {primary1,
+               net::FirstPartySetEntry(primary1, net::SiteType::kPrimary)},
               {associated1,
-               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
-                                       std::nullopt)},
+               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated)},
               {associated2,
-               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
-                                       std::nullopt)},
+               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated)},
               {associated3,
-               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
-                                       std::nullopt)},
+               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated)},
               {associated4,
-               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
-                                       std::nullopt)},
+               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated)},
               {associated5,
-               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
-                                       std::nullopt)},
+               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated)},
               {associated6,
-               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated,
-                                       std::nullopt)},
+               net::FirstPartySetEntry(primary1, net::SiteType::kAssociated)},
           }},
           {}, {}))));
 }
@@ -1720,10 +1669,10 @@
       base::ok(FirstPartySetsOverridesPolicy(net::SetsMutation(
           {},
           {{
-              {primary, net::FirstPartySetEntry(
-                            primary, net::SiteType::kPrimary, std::nullopt)},
-              {alias, net::FirstPartySetEntry(primary, net::SiteType::kPrimary,
-                                              std::nullopt)},
+              {primary,
+               net::FirstPartySetEntry(primary, net::SiteType::kPrimary)},
+              {alias,
+               net::FirstPartySetEntry(primary, net::SiteType::kPrimary)},
           }},
           {{alias, primary}}))));
 }
@@ -1757,10 +1706,10 @@
       FirstPartySetParser::ParseSetsFromEnterprisePolicy(policy_dict).first,
       base::ok(FirstPartySetsOverridesPolicy(net::SetsMutation(
           {{
-              {primary, net::FirstPartySetEntry(
-                            primary, net::SiteType::kPrimary, std::nullopt)},
-              {alias, net::FirstPartySetEntry(primary, net::SiteType::kPrimary,
-                                              std::nullopt)},
+              {primary,
+               net::FirstPartySetEntry(primary, net::SiteType::kPrimary)},
+              {alias,
+               net::FirstPartySetEntry(primary, net::SiteType::kPrimary)},
           }},
           {}, {{alias, primary}}))));
 }
@@ -1807,18 +1756,15 @@
           {
               {
                   {primary,
-                   net::FirstPartySetEntry(primary, net::SiteType::kPrimary,
-                                           std::nullopt)},
+                   net::FirstPartySetEntry(primary, net::SiteType::kPrimary)},
                   {associated1, net::FirstPartySetEntry(
-                                    primary, net::SiteType::kAssociated, 0)},
+                                    primary, net::SiteType::kAssociated)},
                   {associated2, net::FirstPartySetEntry(
-                                    primary, net::SiteType::kAssociated, 1)},
-                  {associated2_cctld,
-                   net::FirstPartySetEntry(primary, net::SiteType::kAssociated,
-                                           1)},
+                                    primary, net::SiteType::kAssociated)},
+                  {associated2_cctld, net::FirstPartySetEntry(
+                                          primary, net::SiteType::kAssociated)},
                   {service,
-                   net::FirstPartySetEntry(primary, net::SiteType::kService,
-                                           std::nullopt)},
+                   net::FirstPartySetEntry(primary, net::SiteType::kService)},
               },
           },
           /*addition_sets=*/{},
diff --git a/content/browser/first_party_sets/first_party_sets_handler_database_helper_unittest.cc b/content/browser/first_party_sets/first_party_sets_handler_database_helper_unittest.cc
index cb79800..f8303de 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_database_helper_unittest.cc
+++ b/content/browser/first_party_sets/first_party_sets_handler_database_helper_unittest.cc
@@ -42,28 +42,22 @@
   net::GlobalFirstPartySets old_sets(
       kVersion,
       /*entries=*/
-      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                         std::nullopt)},
-       {member1,
-        net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-       {member3,
-        net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)}},
+      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+       {member1, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+       {member3, net::FirstPartySetEntry(example, net::SiteType::kAssociated)}},
       /*aliases=*/{});
 
   net::GlobalFirstPartySets current_sets(
       kVersion,
       /*entries=*/
       {
-          {example, net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                            std::nullopt)},
+          {example, net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
           {member1,
-           net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
+           net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
           {member3,
-           net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
-          {foo,
-           net::FirstPartySetEntry(foo, net::SiteType::kPrimary, std::nullopt)},
-          {member2,
-           net::FirstPartySetEntry(foo, net::SiteType::kAssociated, 0)},
+           net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+          {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)},
+          {member2, net::FirstPartySetEntry(foo, net::SiteType::kAssociated)},
       },
       /*aliases=*/{});
 
@@ -86,24 +80,18 @@
   net::GlobalFirstPartySets old_sets(
       kVersion,
       /*entries=*/
-      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                         std::nullopt)},
-       {member1,
-        net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-       {member3,
-        net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
-       {foo,
-        net::FirstPartySetEntry(foo, net::SiteType::kPrimary, std::nullopt)},
-       {member2, net::FirstPartySetEntry(foo, net::SiteType::kAssociated, 0)}},
+      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+       {member1, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+       {member3, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+       {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)},
+       {member2, net::FirstPartySetEntry(foo, net::SiteType::kAssociated)}},
       /*aliases=*/{});
 
   net::GlobalFirstPartySets current_sets(
       kVersion,
       /*entries=*/
-      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                         std::nullopt)},
-       {member1,
-        net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)}},
+      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+       {member1, net::FirstPartySetEntry(example, net::SiteType::kAssociated)}},
       /*aliases=*/{});
 
   // Expected diff: "https://foo.test", "https://member2.test" and
@@ -125,28 +113,21 @@
   net::GlobalFirstPartySets old_sets(
       kVersion,
       /*entries=*/
-      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                         std::nullopt)},
-       {member1,
-        net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-       {foo,
-        net::FirstPartySetEntry(foo, net::SiteType::kPrimary, std::nullopt)},
-       {member2, net::FirstPartySetEntry(foo, net::SiteType::kAssociated, 0)},
-       {member3, net::FirstPartySetEntry(foo, net::SiteType::kAssociated, 1)}},
+      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+       {member1, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+       {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)},
+       {member2, net::FirstPartySetEntry(foo, net::SiteType::kAssociated)},
+       {member3, net::FirstPartySetEntry(foo, net::SiteType::kAssociated)}},
       /*aliases=*/{});
 
   net::GlobalFirstPartySets current_sets(
       kVersion,
       /*entries=*/
-      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                         std::nullopt)},
-       {member1,
-        net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-       {member3,
-        net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
-       {foo,
-        net::FirstPartySetEntry(foo, net::SiteType::kPrimary, std::nullopt)},
-       {member2, net::FirstPartySetEntry(foo, net::SiteType::kAssociated, 0)}},
+      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+       {member1, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+       {member3, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+       {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)},
+       {member2, net::FirstPartySetEntry(foo, net::SiteType::kAssociated)}},
       /*aliases=*/{});
 
   // Expected diff: "https://member3.test" changed primary.
@@ -165,18 +146,16 @@
   net::GlobalFirstPartySets old_sets(
       kVersion,
       /*entries=*/
-      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                         std::nullopt)},
-       {foo, net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-       {bar, net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)}},
+      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+       {foo, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+       {bar, net::FirstPartySetEntry(example, net::SiteType::kAssociated)}},
       /*aliases=*/{});
 
   net::GlobalFirstPartySets current_sets(
       kVersion,
       /*entries=*/
-      {{foo,
-        net::FirstPartySetEntry(foo, net::SiteType::kPrimary, std::nullopt)},
-       {bar, net::FirstPartySetEntry(foo, net::SiteType::kAssociated, 0)}},
+      {{foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)},
+       {bar, net::FirstPartySetEntry(foo, net::SiteType::kAssociated)}},
       /*aliases=*/{});
 
   // Expected diff: "https://example.test" left FPSs, "https://foo.test" and
@@ -198,17 +177,15 @@
   net::GlobalFirstPartySets old_sets(
       kVersion,
       /*entries=*/
-      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                         std::nullopt)},
-       {foo, net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)}},
+      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+       {foo, net::FirstPartySetEntry(example, net::SiteType::kAssociated)}},
       /*aliases=*/{});
 
   net::GlobalFirstPartySets current_sets(
       kVersion,
       /*entries=*/
-      {{example, net::FirstPartySetEntry(foo, net::SiteType::kAssociated, 0)},
-       {foo,
-        net::FirstPartySetEntry(foo, net::SiteType::kPrimary, std::nullopt)}},
+      {{example, net::FirstPartySetEntry(foo, net::SiteType::kAssociated)},
+       {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)}},
       /*aliases=*/{});
 
   // Expected diff: "https://example.test" and "https://foo.test" changed
@@ -229,10 +206,8 @@
   net::GlobalFirstPartySets current_sets(
       kVersion,
       /*entries=*/
-      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                         std::nullopt)},
-       {member1,
-        net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)}},
+      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+       {member1, net::FirstPartySetEntry(example, net::SiteType::kAssociated)}},
       /*aliases=*/{});
 
   EXPECT_THAT(
@@ -251,10 +226,8 @@
   net::GlobalFirstPartySets old_sets(
       kVersion,
       /*entries=*/
-      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                         std::nullopt)},
-       {member1,
-        net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)}},
+      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+       {member1, net::FirstPartySetEntry(example, net::SiteType::kAssociated)}},
       /*aliases=*/{});
 
   EXPECT_THAT(FirstPartySetsHandlerDatabaseHelper::ComputeSetsDiff(
@@ -271,10 +244,10 @@
   net::FirstPartySetsContextConfig current_config =
       net::FirstPartySetsContextConfig::Create(
           {
-              {foo, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                        foo, net::SiteType::kPrimary, std::nullopt))},
+              {foo, net::FirstPartySetEntryOverride(
+                        net::FirstPartySetEntry(foo, net::SiteType::kPrimary))},
               {member2, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            foo, net::SiteType::kAssociated, 0))},
+                            foo, net::SiteType::kAssociated))},
           })
           .value();
 
@@ -297,20 +270,18 @@
   net::GlobalFirstPartySets sets(
       kVersion,
       /*entries=*/
-      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                         std::nullopt)},
-       {member1,
-        net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)}},
+      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+       {member1, net::FirstPartySetEntry(example, net::SiteType::kAssociated)}},
       /*aliases=*/{});
 
   // "https://example.test" was removed from FPSs by policy modifications.
   net::FirstPartySetsContextConfig old_config =
       net::FirstPartySetsContextConfig::Create(
           {
-              {foo, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                        foo, net::SiteType::kPrimary, std::nullopt))},
+              {foo, net::FirstPartySetEntryOverride(
+                        net::FirstPartySetEntry(foo, net::SiteType::kPrimary))},
               {member1, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            foo, net::SiteType::kAssociated, 0))},
+                            foo, net::SiteType::kAssociated))},
               {example, net::FirstPartySetEntryOverride()},
           })
           .value();
@@ -319,12 +290,12 @@
   net::FirstPartySetsContextConfig current_config =
       net::FirstPartySetsContextConfig::Create(
           {
-              {foo, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                        foo, net::SiteType::kPrimary, std::nullopt))},
+              {foo, net::FirstPartySetEntryOverride(
+                        net::FirstPartySetEntry(foo, net::SiteType::kPrimary))},
               {member1, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            foo, net::SiteType::kAssociated, 0))},
+                            foo, net::SiteType::kAssociated))},
               {example, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            foo, net::SiteType::kAssociated, 0))},
+                            foo, net::SiteType::kAssociated))},
           })
           .value();
 
@@ -344,12 +315,12 @@
   net::FirstPartySetsContextConfig old_config =
       net::FirstPartySetsContextConfig::Create(
           {
-              {foo, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                        foo, net::SiteType::kPrimary, std::nullopt))},
+              {foo, net::FirstPartySetEntryOverride(
+                        net::FirstPartySetEntry(foo, net::SiteType::kPrimary))},
               {member1, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            foo, net::SiteType::kAssociated, 0))},
+                            foo, net::SiteType::kAssociated))},
               {member2, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            foo, net::SiteType::kAssociated, 0))},
+                            foo, net::SiteType::kAssociated))},
           })
           .value();
 
@@ -357,10 +328,10 @@
   net::FirstPartySetsContextConfig current_config =
       net::FirstPartySetsContextConfig::Create(
           {
-              {foo, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                        foo, net::SiteType::kPrimary, std::nullopt))},
+              {foo, net::FirstPartySetEntryOverride(
+                        net::FirstPartySetEntry(foo, net::SiteType::kPrimary))},
               {member1, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            foo, net::SiteType::kAssociated, 0))},
+                            foo, net::SiteType::kAssociated))},
           })
           .value();
 
@@ -379,11 +350,11 @@
       net::FirstPartySetsContextConfig::Create(
           {
               {example, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt))},
+                            example, net::SiteType::kPrimary))},
               {member1, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            example, net::SiteType::kAssociated, 0))},
+                            example, net::SiteType::kAssociated))},
               {member2, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            example, net::SiteType::kAssociated, 0))},
+                            example, net::SiteType::kAssociated))},
           })
           .value();
 
@@ -391,9 +362,9 @@
       net::FirstPartySetsContextConfig::Create(
           {
               {member1, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            member1, net::SiteType::kPrimary, std::nullopt))},
+                            member1, net::SiteType::kPrimary))},
               {member2, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            member1, net::SiteType::kAssociated, 0))},
+                            member1, net::SiteType::kAssociated))},
           })
           .value();
 
@@ -419,28 +390,28 @@
   net::FirstPartySetsContextConfig old_config =
       net::FirstPartySetsContextConfig::Create(
           {
-              {foo, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                        foo, net::SiteType::kPrimary, std::nullopt))},
+              {foo, net::FirstPartySetEntryOverride(
+                        net::FirstPartySetEntry(foo, net::SiteType::kPrimary))},
               {member1, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            foo, net::SiteType::kAssociated, 0))},
-              {bar, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                        bar, net::SiteType::kPrimary, std::nullopt))},
+                            foo, net::SiteType::kAssociated))},
+              {bar, net::FirstPartySetEntryOverride(
+                        net::FirstPartySetEntry(bar, net::SiteType::kPrimary))},
               {member2, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            bar, net::SiteType::kAssociated, 0))},
+                            bar, net::SiteType::kAssociated))},
           })
           .value();
 
   net::FirstPartySetsContextConfig current_config =
       net::FirstPartySetsContextConfig::Create(
           {
-              {foo, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                        foo, net::SiteType::kPrimary, std::nullopt))},
+              {foo, net::FirstPartySetEntryOverride(
+                        net::FirstPartySetEntry(foo, net::SiteType::kPrimary))},
               {member2, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            foo, net::SiteType::kAssociated, 0))},
-              {bar, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                        bar, net::SiteType::kPrimary, std::nullopt))},
+                            foo, net::SiteType::kAssociated))},
+              {bar, net::FirstPartySetEntryOverride(
+                        net::FirstPartySetEntry(bar, net::SiteType::kPrimary))},
               {member1, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            bar, net::SiteType::kAssociated, 0))},
+                            bar, net::SiteType::kAssociated))},
           })
           .value();
 
@@ -479,26 +450,21 @@
       net::GlobalFirstPartySets(
           base::Version("0.0.1"),
           /*entries=*/
-          {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                             std::nullopt)},
+          {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
            {member1,
-            net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
+            net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
            {member3,
-            net::FirstPartySetEntry(example, net::SiteType::kAssociated, 1)},
-           {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
-                                         std::nullopt)},
-           {member2,
-            net::FirstPartySetEntry(foo, net::SiteType::kAssociated, 0)}},
+            net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+           {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)},
+           {member2, net::FirstPartySetEntry(foo, net::SiteType::kAssociated)}},
           /*aliases=*/{}),
       /*config=*/net::FirstPartySetsContextConfig());
 
   net::GlobalFirstPartySets current_sets(
       kVersion,
       /*entries=*/
-      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                         std::nullopt)},
-       {member1,
-        net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)}},
+      {{example, net::FirstPartySetEntry(example, net::SiteType::kPrimary)},
+       {member1, net::FirstPartySetEntry(example, net::SiteType::kAssociated)}},
       /*aliases=*/{});
 
   std::optional<std::pair<std::vector<net::SchemefulSite>,
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl_browsertest.cc b/content/browser/first_party_sets/first_party_sets_handler_impl_browsertest.cc
index 4be93e4..1a13e6b 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_impl_browsertest.cc
+++ b/content/browser/first_party_sets/first_party_sets_handler_impl_browsertest.cc
@@ -69,16 +69,14 @@
 IN_PROC_BROWSER_TEST_F(FirstPartySetsHandlerImplBrowserTest, LocalSwitch) {
   // The First-Party Sets should be:
   // {primary: A, associatedSites: [B, C]}
-  EXPECT_EQ(
-      GetGlobalSets().FindEntry(net::SchemefulSite(GURL(kSiteB)),
-                                net::FirstPartySetsContextConfig()),
-      std::make_optional(net::FirstPartySetEntry(
-          net::SchemefulSite(GURL(kSiteA)), net::SiteType::kAssociated, 0)));
-  EXPECT_EQ(
-      GetGlobalSets().FindEntry(net::SchemefulSite(GURL(kSiteC)),
-                                net::FirstPartySetsContextConfig()),
-      std::make_optional(net::FirstPartySetEntry(
-          net::SchemefulSite(GURL(kSiteA)), net::SiteType::kAssociated, 1)));
+  EXPECT_EQ(GetGlobalSets().FindEntry(net::SchemefulSite(GURL(kSiteB)),
+                                      net::FirstPartySetsContextConfig()),
+            std::make_optional(net::FirstPartySetEntry(
+                net::SchemefulSite(GURL(kSiteA)), net::SiteType::kAssociated)));
+  EXPECT_EQ(GetGlobalSets().FindEntry(net::SchemefulSite(GURL(kSiteC)),
+                                      net::FirstPartySetsContextConfig()),
+            std::make_optional(net::FirstPartySetEntry(
+                net::SchemefulSite(GURL(kSiteA)), net::SiteType::kAssociated)));
 }
 
 // Verify that when both `kUseFirstPartySet` and `kUseRelatedWebsiteSet`
@@ -104,16 +102,14 @@
   //
   // After the new switch is applied, the expected First-Party Sets are:
   // {primary: D, associatedSites: [B, C]}
-  EXPECT_EQ(
-      GetGlobalSets().FindEntry(net::SchemefulSite(GURL(kSiteB)),
-                                net::FirstPartySetsContextConfig()),
-      std::make_optional(net::FirstPartySetEntry(
-          net::SchemefulSite(GURL(kSiteD)), net::SiteType::kAssociated, 0)));
-  EXPECT_EQ(
-      GetGlobalSets().FindEntry(net::SchemefulSite(GURL(kSiteC)),
-                                net::FirstPartySetsContextConfig()),
-      std::make_optional(net::FirstPartySetEntry(
-          net::SchemefulSite(GURL(kSiteD)), net::SiteType::kAssociated, 1)));
+  EXPECT_EQ(GetGlobalSets().FindEntry(net::SchemefulSite(GURL(kSiteB)),
+                                      net::FirstPartySetsContextConfig()),
+            std::make_optional(net::FirstPartySetEntry(
+                net::SchemefulSite(GURL(kSiteD)), net::SiteType::kAssociated)));
+  EXPECT_EQ(GetGlobalSets().FindEntry(net::SchemefulSite(GURL(kSiteC)),
+                                      net::FirstPartySetsContextConfig()),
+            std::make_optional(net::FirstPartySetEntry(
+                net::SchemefulSite(GURL(kSiteD)), net::SiteType::kAssociated)));
 
   EXPECT_EQ(GetGlobalSets().FindEntry(net::SchemefulSite(GURL(kSiteA)),
                                       net::FirstPartySetsContextConfig()),
diff --git a/content/browser/first_party_sets/first_party_sets_handler_impl_instance_unittest.cc b/content/browser/first_party_sets/first_party_sets_handler_impl_instance_unittest.cc
index dab5f83..f86fc52 100644
--- a/content/browser/first_party_sets/first_party_sets_handler_impl_instance_unittest.cc
+++ b/content/browser/first_party_sets/first_party_sets_handler_impl_instance_unittest.cc
@@ -330,14 +330,13 @@
           R"({"primary": "https://example.test",)"
           R"("associatedSites": ["https://associatedsite1.test"]})"));
 
-  EXPECT_THAT(
-      GetSetsAndWait().FindEntries({example, associated},
-                                   net::FirstPartySetsContextConfig()),
-      UnorderedElementsAre(
-          Pair(example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)),
-          Pair(associated, net::FirstPartySetEntry(
-                               example, net::SiteType::kAssociated, 0))));
+  EXPECT_THAT(GetSetsAndWait().FindEntries({example, associated},
+                                           net::FirstPartySetsContextConfig()),
+              UnorderedElementsAre(
+                  Pair(example, net::FirstPartySetEntry(
+                                    example, net::SiteType::kPrimary)),
+                  Pair(associated, net::FirstPartySetEntry(
+                                       example, net::SiteType::kAssociated))));
 }
 
 TEST_F(FirstPartySetsHandlerImplEnabledTest,
@@ -372,11 +371,9 @@
   EXPECT_THAT(
       persisted->first.FindEntries({foo, associated}, persisted->second),
       UnorderedElementsAre(
-          Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
-                                            std::nullopt)),
+          Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)),
           Pair(associated,
-               net::FirstPartySetEntry(foo, net::SiteType::kAssociated,
-                                       std::nullopt))));
+               net::FirstPartySetEntry(foo, net::SiteType::kAssociated))));
   histogram.ExpectUniqueSample(kFirstPartySetsClearSiteDataOutcomeHistogram,
                                /*sample=*/true, 1);
   histogram.ExpectTotalCount(kDelayedQueriesCountHistogram, 1);
@@ -420,11 +417,9 @@
     EXPECT_THAT(
         persisted->first.FindEntries({foo, associated}, persisted->second),
         UnorderedElementsAre(
-            Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
-                                              std::nullopt)),
+            Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)),
             Pair(associated,
-                 net::FirstPartySetEntry(foo, net::SiteType::kAssociated,
-                                         std::nullopt))));
+                 net::FirstPartySetEntry(foo, net::SiteType::kAssociated))));
     EXPECT_THAT(
         HasEntryInBrowserContextsClearedAndWait(handler, browser_context_id),
         Optional(true));
@@ -464,11 +459,9 @@
     EXPECT_THAT(
         persisted->first.FindEntries({foo, associated2}, persisted->second),
         UnorderedElementsAre(
-            Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
-                                              std::nullopt)),
+            Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)),
             Pair(associated2,
-                 net::FirstPartySetEntry(foo, net::SiteType::kAssociated,
-                                         std::nullopt))));
+                 net::FirstPartySetEntry(foo, net::SiteType::kAssociated))));
     EXPECT_THAT(
         HasEntryInBrowserContextsClearedAndWait(handler, browser_context_id),
         Optional(true));
@@ -494,13 +487,13 @@
 
   handler().Init(
       /*user_data_dir=*/{}, net::LocalSetDeclaration());
-  ASSERT_THAT(GetSetsAndWait().FindEntries({foo, associated},
-                                           net::FirstPartySetsContextConfig()),
-              UnorderedElementsAre(
-                  Pair(foo, net::FirstPartySetEntry(
-                                foo, net::SiteType::kPrimary, std::nullopt)),
-                  Pair(associated, net::FirstPartySetEntry(
-                                       foo, net::SiteType::kAssociated, 0))));
+  ASSERT_THAT(
+      GetSetsAndWait().FindEntries({foo, associated},
+                                   net::FirstPartySetsContextConfig()),
+      UnorderedElementsAre(
+          Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)),
+          Pair(associated,
+               net::FirstPartySetEntry(foo, net::SiteType::kAssociated))));
 
   ClearSiteDataOnChangedSetsForContextAndWait(
       context(), browser_context_id, net::FirstPartySetsContextConfig());
@@ -545,11 +538,9 @@
   EXPECT_THAT(
       persisted->first.FindEntries({foo, associated}, persisted->second),
       UnorderedElementsAre(
-          Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
-                                            std::nullopt)),
+          Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)),
           Pair(associated,
-               net::FirstPartySetEntry(foo, net::SiteType::kAssociated,
-                                       std::nullopt))));
+               net::FirstPartySetEntry(foo, net::SiteType::kAssociated))));
   histogram.ExpectUniqueSample(kFirstPartySetsClearSiteDataOutcomeHistogram,
                                /*sample=*/true, 1);
   histogram.ExpectTotalCount(kDelayedQueriesCountHistogram, 1);
@@ -573,17 +564,16 @@
   // Wait until initialization is complete.
   GetSetsAndWait();
 
-  EXPECT_THAT(
-      handler()
-          .GetSets(base::NullCallback())
-          .value()
-          .FindEntries({example, associated},
-                       net::FirstPartySetsContextConfig()),
-      UnorderedElementsAre(
-          Pair(example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)),
-          Pair(associated, net::FirstPartySetEntry(
-                               example, net::SiteType::kAssociated, 0))));
+  EXPECT_THAT(handler()
+                  .GetSets(base::NullCallback())
+                  .value()
+                  .FindEntries({example, associated},
+                               net::FirstPartySetsContextConfig()),
+              UnorderedElementsAre(
+                  Pair(example, net::FirstPartySetEntry(
+                                    example, net::SiteType::kPrimary)),
+                  Pair(associated, net::FirstPartySetEntry(
+                                       example, net::SiteType::kAssociated))));
 }
 
 TEST_F(FirstPartySetsHandlerImplEnabledTest,
@@ -604,26 +594,24 @@
   handler().SetPublicFirstPartySets(base::Version("1.2.3"),
                                     WritePublicSetsFile(input));
 
-  EXPECT_THAT(
-      future.Get().FindEntries({example, associated},
-                               net::FirstPartySetsContextConfig()),
-      UnorderedElementsAre(
-          Pair(example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)),
-          Pair(associated, net::FirstPartySetEntry(
-                               example, net::SiteType::kAssociated, 0))));
+  EXPECT_THAT(future.Get().FindEntries({example, associated},
+                                       net::FirstPartySetsContextConfig()),
+              UnorderedElementsAre(
+                  Pair(example, net::FirstPartySetEntry(
+                                    example, net::SiteType::kPrimary)),
+                  Pair(associated, net::FirstPartySetEntry(
+                                       example, net::SiteType::kAssociated))));
 
-  EXPECT_THAT(
-      handler()
-          .GetSets(base::NullCallback())
-          .value()
-          .FindEntries({example, associated},
-                       net::FirstPartySetsContextConfig()),
-      UnorderedElementsAre(
-          Pair(example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)),
-          Pair(associated, net::FirstPartySetEntry(
-                               example, net::SiteType::kAssociated, 0))));
+  EXPECT_THAT(handler()
+                  .GetSets(base::NullCallback())
+                  .value()
+                  .FindEntries({example, associated},
+                               net::FirstPartySetsContextConfig()),
+              UnorderedElementsAre(
+                  Pair(example, net::FirstPartySetEntry(
+                                    example, net::SiteType::kPrimary)),
+                  Pair(associated, net::FirstPartySetEntry(
+                                       example, net::SiteType::kAssociated))));
 }
 
 TEST_F(FirstPartySetsHandlerImplEnabledTest,
@@ -704,13 +692,12 @@
         set_entries.emplace_back(site, entry);
         return true;
       }));
-  EXPECT_THAT(
-      set_entries,
-      UnorderedElementsAre(
-          Pair(example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)),
-          Pair(associated, net::FirstPartySetEntry(
-                               example, net::SiteType::kAssociated, 0))));
+  EXPECT_THAT(set_entries,
+              UnorderedElementsAre(
+                  Pair(example, net::FirstPartySetEntry(
+                                    example, net::SiteType::kPrimary)),
+                  Pair(associated, net::FirstPartySetEntry(
+                                       example, net::SiteType::kAssociated))));
 }
 
 TEST_F(FirstPartySetsHandlerImplEnabledTest,
@@ -737,8 +724,8 @@
   EXPECT_TRUE(handler().ForEachEffectiveSetEntry(
       net::FirstPartySetsContextConfig::Create(
           {{associated2,
-            net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                example, net::SiteType::kAssociated, std::nullopt))}})
+            net::FirstPartySetEntryOverride(
+                net::FirstPartySetEntry(example, net::SiteType::kAssociated))}})
           .value(),
       [&](const net::SchemefulSite& site,
           const net::FirstPartySetEntry& entry) {
@@ -748,13 +735,12 @@
   EXPECT_THAT(
       set_entries,
       UnorderedElementsAre(
-          Pair(example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)),
+          Pair(example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)),
           Pair(associated1,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)),
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)),
           Pair(associated2,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated,
-                                       std::nullopt))));
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated))));
 }
 
 class FirstPartySetsHandlerGetContextConfigForPolicyTest
diff --git a/content/browser/first_party_sets/first_party_sets_loader_unittest.cc b/content/browser/first_party_sets/first_party_sets_loader_unittest.cc
index 07c778c..2a38a7fc 100644
--- a/content/browser/first_party_sets/first_party_sets_loader_unittest.cc
+++ b/content/browser/first_party_sets/first_party_sets_loader_unittest.cc
@@ -103,14 +103,13 @@
       WaitAndGetResult().FindEntries({example, associated1, foo, associated2},
                                      net::FirstPartySetsContextConfig()),
       UnorderedElementsAre(
-          Pair(example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)),
+          Pair(example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)),
           Pair(associated1,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)),
-          Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
-                                            std::nullopt)),
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)),
+          Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)),
           Pair(associated2,
-               net::FirstPartySetEntry(foo, net::SiteType::kAssociated, 0))));
+               net::FirstPartySetEntry(foo, net::SiteType::kAssociated))));
 }
 
 TEST_F(FirstPartySetsLoaderTest, SetComponentSets_Idempotent) {
@@ -139,10 +138,9 @@
       WaitAndGetResult().FindEntries({example, foo, example2, foo2},
                                      net::FirstPartySetsContextConfig()),
       UnorderedElementsAre(
-          Pair(example, net::FirstPartySetEntry(
-                            example, net::SiteType::kPrimary, std::nullopt)),
-          Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
-                                            std::nullopt))));
+          Pair(example,
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)),
+          Pair(foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary))));
 }
 
 TEST_F(FirstPartySetsLoaderTest, SetsManuallySpecified) {
@@ -157,10 +155,9 @@
       net::LocalSetDeclaration::Create(
           /*set_entries=*/base::flat_map<net::SchemefulSite,
                                          net::FirstPartySetEntry>({
-              {bar, net::FirstPartySetEntry(bar, net::SiteType::kPrimary,
-                                            std::nullopt)},
+              {bar, net::FirstPartySetEntry(bar, net::SiteType::kPrimary)},
               {associated2,
-               net::FirstPartySetEntry(bar, net::SiteType::kAssociated, 0)},
+               net::FirstPartySetEntry(bar, net::SiteType::kAssociated)},
           }),
           /*aliases=*/{})
           .value());
@@ -168,7 +165,7 @@
   EXPECT_THAT(
       WaitAndGetResult().FindEntry(associated2,
                                    net::FirstPartySetsContextConfig()),
-      Optional(net::FirstPartySetEntry(bar, net::SiteType::kAssociated, 0)));
+      Optional(net::FirstPartySetEntry(bar, net::SiteType::kAssociated)));
 }
 
 TEST_F(FirstPartySetsLoaderTest, SetsManuallySpecified_Idempotent) {
@@ -180,10 +177,9 @@
       net::LocalSetDeclaration::Create(
           /*set_entries=*/base::flat_map<net::SchemefulSite,
                                          net::FirstPartySetEntry>({
-              {bar, net::FirstPartySetEntry(bar, net::SiteType::kPrimary,
-                                            std::nullopt)},
+              {bar, net::FirstPartySetEntry(bar, net::SiteType::kPrimary)},
               {associated1,
-               net::FirstPartySetEntry(bar, net::SiteType::kAssociated, 0)},
+               net::FirstPartySetEntry(bar, net::SiteType::kAssociated)},
           }),
           /*aliases=*/{})
           .value());
@@ -193,10 +189,9 @@
       net::LocalSetDeclaration::Create(
           /*set_entries=*/base::flat_map<net::SchemefulSite,
                                          net::FirstPartySetEntry>({
-              {bar, net::FirstPartySetEntry(bar, net::SiteType::kPrimary,
-                                            std::nullopt)},
+              {bar, net::FirstPartySetEntry(bar, net::SiteType::kPrimary)},
               {associated2,
-               net::FirstPartySetEntry(bar, net::SiteType::kAssociated, 0)},
+               net::FirstPartySetEntry(bar, net::SiteType::kAssociated)},
           }),
           /*aliases=*/{})
           .value());
@@ -209,9 +204,9 @@
                       associated2,
                   },
                   net::FirstPartySetsContextConfig()),
-              UnorderedElementsAre(
-                  Pair(associated1, net::FirstPartySetEntry(
-                                        bar, net::SiteType::kAssociated, 0))));
+              UnorderedElementsAre(Pair(
+                  associated1,
+                  net::FirstPartySetEntry(bar, net::SiteType::kAssociated))));
 }
 
 }  // namespace content
diff --git a/content/browser/gpu/compositor_util.cc b/content/browser/gpu/compositor_util.cc
index 6b775ebf..3153afd 100644
--- a/content/browser/gpu/compositor_util.cc
+++ b/content/browser/gpu/compositor_util.cc
@@ -19,6 +19,7 @@
 #include "base/system/sys_info.h"
 #include "base/values.h"
 #include "build/build_config.h"
+#include "cc/base/features.h"
 #include "cc/base/switches.h"
 #include "components/viz/common/features.h"
 #include "content/browser/compositor/image_transport_factory.h"
@@ -217,6 +218,10 @@
   features.emplace_back(
       "webnn",
       SafeGetFeatureStatus(gpu_feature_info, gpu::GPU_FEATURE_TYPE_WEBNN));
+  features.emplace_back("trees_in_viz",
+                        base::FeatureList::IsEnabled(::features::kTreesInViz)
+                            ? gpu::kGpuFeatureStatusEnabled
+                            : gpu::kGpuFeatureStatusDisabled);
   return features;
 }
 
@@ -289,7 +294,8 @@
           gpu_feature_data.name == "vulkan" ||
           gpu_feature_data.name == "skia_graphite" ||
           gpu_feature_data.name == "surface_control" ||
-          gpu_feature_data.name == "webnn") {
+          gpu_feature_data.name == "webnn" ||
+          gpu_feature_data.name == "trees_in_viz") {
         status += "_on";
       }
     }
diff --git a/content/browser/indexed_db/instance/backing_store.h b/content/browser/indexed_db/instance/backing_store.h
index 77384e0..dd1f22c 100644
--- a/content/browser/indexed_db/instance/backing_store.h
+++ b/content/browser/indexed_db/instance/backing_store.h
@@ -189,28 +189,24 @@
         const blink::IndexedDBKey& key,
         std::unique_ptr<blink::IndexedDBKey>* found_primary_key,
         bool* exists) = 0;
-    virtual std::unique_ptr<Cursor> OpenObjectStoreKeyCursor(
-        int64_t object_store_id,
-        const blink::IndexedDBKeyRange& key_range,
-        blink::mojom::IDBCursorDirection,
-        Status*) = 0;
-    virtual std::unique_ptr<Cursor> OpenObjectStoreCursor(
-        int64_t object_store_id,
-        const blink::IndexedDBKeyRange& key_range,
-        blink::mojom::IDBCursorDirection,
-        Status*) = 0;
-    virtual std::unique_ptr<Cursor> OpenIndexKeyCursor(
+    virtual base::expected<std::unique_ptr<Cursor>, Status>
+    OpenObjectStoreKeyCursor(int64_t object_store_id,
+                             const blink::IndexedDBKeyRange& key_range,
+                             blink::mojom::IDBCursorDirection) = 0;
+    virtual base::expected<std::unique_ptr<Cursor>, Status>
+    OpenObjectStoreCursor(int64_t object_store_id,
+                          const blink::IndexedDBKeyRange& key_range,
+                          blink::mojom::IDBCursorDirection) = 0;
+    virtual base::expected<std::unique_ptr<Cursor>, Status> OpenIndexKeyCursor(
         int64_t object_store_id,
         int64_t index_id,
         const blink::IndexedDBKeyRange& key_range,
-        blink::mojom::IDBCursorDirection,
-        Status*) = 0;
-    virtual std::unique_ptr<Cursor> OpenIndexCursor(
+        blink::mojom::IDBCursorDirection) = 0;
+    virtual base::expected<std::unique_ptr<Cursor>, Status> OpenIndexCursor(
         int64_t object_store_id,
         int64_t index_id,
         const blink::IndexedDBKeyRange& key_range,
-        blink::mojom::IDBCursorDirection,
-        Status*) = 0;
+        blink::mojom::IDBCursorDirection) = 0;
   };
 
   // Another interface to be implemented by a backend implementation.
diff --git a/content/browser/indexed_db/instance/database.cc b/content/browser/indexed_db/instance/database.cc
index 4733fd1..3509cfd 100644
--- a/content/browser/indexed_db/instance/database.cc
+++ b/content/browser/indexed_db/instance/database.cc
@@ -359,8 +359,8 @@
 
   const IndexedDBKey* key;
 
-  Status s = Status::OK();
-  std::unique_ptr<BackingStore::Cursor> backing_store_cursor;
+  base::expected<std::unique_ptr<BackingStore::Cursor>, Status>
+      backing_store_cursor;
   if (key_range->IsOnlyKey()) {
     key = &key_range->lower();
   } else {
@@ -370,50 +370,50 @@
         backing_store_cursor =
             transaction->BackingStoreTransaction()->OpenObjectStoreKeyCursor(
                 object_store_id, *key_range,
-                blink::mojom::IDBCursorDirection::Next, &s);
+                blink::mojom::IDBCursorDirection::Next);
       } else {
         backing_store_cursor =
             transaction->BackingStoreTransaction()->OpenObjectStoreCursor(
                 object_store_id, *key_range,
-                blink::mojom::IDBCursorDirection::Next, &s);
+                blink::mojom::IDBCursorDirection::Next);
       }
     } else if (cursor_type == CursorType::kKeyOnly) {
       // Index Value Retrieval Operation
       backing_store_cursor =
           transaction->BackingStoreTransaction()->OpenIndexKeyCursor(
               object_store_id, index_id, *key_range,
-              blink::mojom::IDBCursorDirection::Next, &s);
+              blink::mojom::IDBCursorDirection::Next);
     } else {
       // Index Referenced Value Retrieval Operation
       backing_store_cursor =
           transaction->BackingStoreTransaction()->OpenIndexCursor(
               object_store_id, index_id, *key_range,
-              blink::mojom::IDBCursorDirection::Next, &s);
+              blink::mojom::IDBCursorDirection::Next);
     }
 
-    if (!s.ok()) {
+    if (!backing_store_cursor.has_value()) {
       std::move(callback).Run(
           blink::mojom::IDBDatabaseGetResult::NewErrorResult(CreateIDBErrorPtr(
               blink::mojom::IDBException::kUnknownError,
               "Corruption detected, unable to continue", transaction)));
-      return s;
+      return backing_store_cursor.error();
     }
 
-    if (!backing_store_cursor) {
+    if (!*backing_store_cursor) {
       // This means we've run out of data.
       std::move(callback).Run(
           blink::mojom::IDBDatabaseGetResult::NewEmpty(true));
-      return s;
+      return Status::OK();
     }
 
-    key = &backing_store_cursor->GetKey();
+    key = &(*backing_store_cursor)->GetKey();
   }
 
   if (index_id == IndexedDBIndexMetadata::kInvalidId) {
     // Object Store Retrieval Operation
     IndexedDBReturnValue value;
-    s = transaction->BackingStoreTransaction()->GetRecord(object_store_id, *key,
-                                                          &value);
+    Status s = transaction->BackingStoreTransaction()->GetRecord(
+        object_store_id, *key, &value);
     if (!s.ok()) {
       std::move(callback).Run(
           blink::mojom::IDBDatabaseGetResult::NewErrorResult(
@@ -451,7 +451,7 @@
 
   // From here we are dealing only with indexes.
   std::unique_ptr<IndexedDBKey> primary_key;
-  s = transaction->BackingStoreTransaction()->GetPrimaryKeyViaIndex(
+  Status s = transaction->BackingStoreTransaction()->GetPrimaryKeyViaIndex(
       object_store_id, index_id, *key, &primary_key);
   if (!s.ok()) {
     std::move(callback).Run(blink::mojom::IDBDatabaseGetResult::NewErrorResult(
@@ -591,39 +591,39 @@
   const IndexedDBObjectStoreMetadata& object_store_metadata =
       GetObjectStoreMetadata(object_store_id);
 
-  Status s = Status::OK();
-  std::unique_ptr<BackingStore::Cursor> cursor;
+  base::expected<std::unique_ptr<BackingStore::Cursor>, Status> cursor;
 
   if (result_type == blink::mojom::IDBGetAllResultType::Keys) {
     // Retrieving keys
     if (index_id == IndexedDBIndexMetadata::kInvalidId) {
       // Object Store: Key Retrieval Operation
       cursor = transaction->BackingStoreTransaction()->OpenObjectStoreKeyCursor(
-          object_store_id, *key_range, direction, &s);
+          object_store_id, *key_range, direction);
     } else {
       // Index Value: (Primary Key) Retrieval Operation
       cursor = transaction->BackingStoreTransaction()->OpenIndexKeyCursor(
-          object_store_id, index_id, *key_range, direction, &s);
+          object_store_id, index_id, *key_range, direction);
     }
   } else {
     // Retrieving values
     if (index_id == IndexedDBIndexMetadata::kInvalidId) {
       // Object Store: Value Retrieval Operation
       cursor = transaction->BackingStoreTransaction()->OpenObjectStoreCursor(
-          object_store_id, *key_range, direction, &s);
+          object_store_id, *key_range, direction);
     } else {
       // Object Store: Referenced Value Retrieval Operation
       cursor = transaction->BackingStoreTransaction()->OpenIndexCursor(
-          object_store_id, index_id, *key_range, direction, &s);
+          object_store_id, index_id, *key_range, direction);
     }
   }
 
-  if (!s.ok()) {
-    DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
+  if (!cursor.has_value()) {
+    DLOG(ERROR) << "Unable to open cursor operation: "
+                << cursor.error().ToString();
     result_sink->Get()->OnError(CreateIDBErrorPtr(
         blink::mojom::IDBException::kUnknownError,
         "Corruption detected, unable to continue", transaction));
-    return s;
+    return cursor.error();
   }
 
   std::vector<blink::mojom::IDBRecordPtr> found_records;
@@ -634,9 +634,9 @@
   };
 
   // No records found.
-  if (!cursor) {
+  if (!*cursor) {
     send_records(/*done=*/true);
-    return s;
+    return Status::OK();
   }
 
   bool did_first_seek = false;
@@ -654,8 +654,9 @@
   int64_t num_found_items = 0;
   while (num_found_items++ < max_count) {
     bool cursor_valid;
+    Status s;
     if (did_first_seek) {
-      cursor_valid = cursor->Continue(&s);
+      cursor_valid = (*cursor)->Continue(&s);
     } else {
       // Cursor creation performs the first seek, returning a nullptr cursor
       // when invalid.
@@ -676,13 +677,13 @@
     blink::mojom::IDBRecordPtr return_record;
 
     if (result_type == blink::mojom::IDBGetAllResultType::Keys) {
-      return_record = blink::mojom::IDBRecord::New(cursor->GetPrimaryKey(),
+      return_record = blink::mojom::IDBRecord::New((*cursor)->GetPrimaryKey(),
                                                    /*value=*/nullptr,
                                                    /*index_key=*/std::nullopt);
     } else if (result_type == blink::mojom::IDBGetAllResultType::Values) {
       blink::mojom::IDBReturnValuePtr return_value =
           ExtractReturnValueFromCursorValue(bucket_context_.get(),
-                                            object_store_metadata, *cursor);
+                                            object_store_metadata, **cursor);
       return_record = blink::mojom::IDBRecord::New(
           /*primary_key=*/std::nullopt, std::move(return_value),
           /*index_key=*/std::nullopt);
@@ -691,13 +692,13 @@
       // key.
       blink::mojom::IDBReturnValuePtr return_value =
           ExtractReturnValueFromCursorValue(bucket_context_.get(),
-                                            object_store_metadata, *cursor);
+                                            object_store_metadata, **cursor);
       std::optional<IndexedDBKey> index_key;
       if (index_id != IndexedDBIndexMetadata::kInvalidId) {
         // The index key only exists for `IDBIndex::getAllRecords()`.
-        index_key = cursor->GetKey();
+        index_key = (*cursor)->GetKey();
       }
-      return_record = blink::mojom::IDBRecord::New(cursor->GetPrimaryKey(),
+      return_record = blink::mojom::IDBRecord::New((*cursor)->GetPrimaryKey(),
                                                    std::move(return_value),
                                                    std::move(index_key));
     } else {
@@ -712,7 +713,7 @@
     }
   }
   send_records(/*done=*/true);
-  return s;
+  return Status::OK();
 }
 
 Status Database::SetIndexKeysOperation(
@@ -799,20 +800,18 @@
     transaction->AddPreemptiveEvent();
   }
 
-  Status s;
-  std::unique_ptr<BackingStore::Cursor> backing_store_cursor;
+  base::expected<std::unique_ptr<BackingStore::Cursor>, Status>
+      backing_store_cursor;
   if (params->index_id == IndexedDBIndexMetadata::kInvalidId) {
     if (params->cursor_type == CursorType::kKeyOnly) {
       DCHECK_EQ(params->task_type, blink::mojom::IDBTaskType::Normal);
       backing_store_cursor =
           transaction->BackingStoreTransaction()->OpenObjectStoreKeyCursor(
-              params->object_store_id, *params->key_range, params->direction,
-              &s);
+              params->object_store_id, *params->key_range, params->direction);
     } else {
       backing_store_cursor =
           transaction->BackingStoreTransaction()->OpenObjectStoreCursor(
-              params->object_store_id, *params->key_range, params->direction,
-              &s);
+              params->object_store_id, *params->key_range, params->direction);
     }
   } else {
     DCHECK_EQ(params->task_type, blink::mojom::IDBTaskType::Normal);
@@ -820,30 +819,31 @@
       backing_store_cursor =
           transaction->BackingStoreTransaction()->OpenIndexKeyCursor(
               params->object_store_id, params->index_id, *params->key_range,
-              params->direction, &s);
+              params->direction);
     } else {
       backing_store_cursor =
           transaction->BackingStoreTransaction()->OpenIndexCursor(
               params->object_store_id, params->index_id, *params->key_range,
-              params->direction, &s);
+              params->direction);
     }
   }
 
-  if (!s.ok()) {
-    DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
-    return s;
+  if (!backing_store_cursor.has_value()) {
+    DLOG(ERROR) << "Unable to open cursor operation: "
+                << backing_store_cursor.error().ToString();
+    return backing_store_cursor.error();
   }
 
-  if (!backing_store_cursor) {
+  if (!*backing_store_cursor) {
     // Occurs when we've reached the end of cursor's data.
     std::move(params->callback)
         .Run(blink::mojom::IDBDatabaseOpenCursorResult::NewEmpty(true));
-    return s;
+    return Status::OK();
   }
 
   mojo::PendingAssociatedRemote<blink::mojom::IDBCursor> pending_remote;
   Cursor* cursor = Cursor::CreateAndBind(
-      std::move(backing_store_cursor), params->cursor_type, params->task_type,
+      std::move(*backing_store_cursor), params->cursor_type, params->task_type,
       transaction->AsWeakPtr(), pending_remote);
   transaction->RegisterOpenCursor(cursor);
 
@@ -864,7 +864,7 @@
           blink::mojom::IDBDatabaseOpenCursorValue::New(
               std::move(pending_remote), cursor->key(), cursor->primary_key(),
               std::move(mojo_value))));
-  return s;
+  return Status::OK();
 }
 
 Status Database::CountOperation(
@@ -880,36 +880,40 @@
     return Status::InvalidArgument("Invalid object_store_id and/or index_id.");
   }
 
-  uint32_t count = 0;
-  std::unique_ptr<BackingStore::Cursor> backing_store_cursor;
-  Status s = Status::OK();
+  base::expected<std::unique_ptr<BackingStore::Cursor>, Status>
+      backing_store_cursor;
   if (index_id == IndexedDBIndexMetadata::kInvalidId) {
     backing_store_cursor =
         transaction->BackingStoreTransaction()->OpenObjectStoreKeyCursor(
-            object_store_id, *key_range, blink::mojom::IDBCursorDirection::Next,
-            &s);
+            object_store_id, *key_range,
+            blink::mojom::IDBCursorDirection::Next);
   } else {
     backing_store_cursor =
         transaction->BackingStoreTransaction()->OpenIndexKeyCursor(
             object_store_id, index_id, *key_range,
-            blink::mojom::IDBCursorDirection::Next, &s);
+            blink::mojom::IDBCursorDirection::Next);
   }
-  if (!s.ok()) {
-    DLOG(ERROR) << "Unable perform count operation: " << s.ToString();
-    return s;
+  if (!backing_store_cursor.has_value()) {
+    DLOG(ERROR) << "Unable perform count operation: "
+                << backing_store_cursor.error().ToString();
+    return backing_store_cursor.error();
   }
 
-  if (backing_store_cursor) {
-    do {
-      if (!s.ok()) {
-        return s;
-      }
-      ++count;
-    } while (backing_store_cursor->Continue(&s));
+  if (!*backing_store_cursor) {
+    std::move(callback).Run(/*success=*/true, /*count=*/0);
+    return Status::OK();
   }
 
+  uint32_t count = 1;
+  Status s;
+  while ((*backing_store_cursor)->Continue(&s)) {
+    if (!s.ok()) {
+      return s;
+    }
+    ++count;
+  }
   std::move(callback).Run(/*success=*/true, count);
-  return s;
+  return Status::OK();
 }
 
 Status Database::DeleteRangeOperation(
diff --git a/content/browser/indexed_db/instance/fake_transaction.cc b/content/browser/indexed_db/instance/fake_transaction.cc
index 468c84a..ad9ae807 100644
--- a/content/browser/indexed_db/instance/fake_transaction.cc
+++ b/content/browser/indexed_db/instance/fake_transaction.cc
@@ -158,44 +158,41 @@
                                                 found_primary_key, exists);
 }
 
-std::unique_ptr<indexed_db::BackingStore::Cursor>
+base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
 FakeTransaction::OpenObjectStoreKeyCursor(
     int64_t object_store_id,
     const blink::IndexedDBKeyRange& key_range,
-    blink::mojom::IDBCursorDirection direction,
-    Status* status) {
-  return wrapped_transaction_->OpenObjectStoreKeyCursor(
-      object_store_id, key_range, direction, status);
+    blink::mojom::IDBCursorDirection direction) {
+  return wrapped_transaction_->OpenObjectStoreKeyCursor(object_store_id,
+                                                        key_range, direction);
 }
 
-std::unique_ptr<indexed_db::BackingStore::Cursor>
+base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
 FakeTransaction::OpenObjectStoreCursor(
     int64_t object_store_id,
     const blink::IndexedDBKeyRange& key_range,
-    blink::mojom::IDBCursorDirection direction,
-    Status* status) {
+    blink::mojom::IDBCursorDirection direction) {
   return wrapped_transaction_->OpenObjectStoreCursor(object_store_id, key_range,
-                                                     direction, status);
+                                                     direction);
 }
 
-std::unique_ptr<indexed_db::BackingStore::Cursor>
-FakeTransaction::OpenIndexKeyCursor(int64_t object_store_id,
-                                    int64_t index_id,
-                                    const blink::IndexedDBKeyRange& key_range,
-                                    blink::mojom::IDBCursorDirection direction,
-                                    Status* status) {
+base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
+FakeTransaction::OpenIndexKeyCursor(
+    int64_t object_store_id,
+    int64_t index_id,
+    const blink::IndexedDBKeyRange& key_range,
+    blink::mojom::IDBCursorDirection direction) {
   return wrapped_transaction_->OpenIndexKeyCursor(object_store_id, index_id,
-                                                  key_range, direction, status);
+                                                  key_range, direction);
 }
 
-std::unique_ptr<indexed_db::BackingStore::Cursor>
+base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
 FakeTransaction::OpenIndexCursor(int64_t object_store_id,
                                  int64_t index_id,
                                  const blink::IndexedDBKeyRange& key_range,
-                                 blink::mojom::IDBCursorDirection direction,
-                                 Status* status) {
+                                 blink::mojom::IDBCursorDirection direction) {
   return wrapped_transaction_->OpenIndexCursor(object_store_id, index_id,
-                                               key_range, direction, status);
+                                               key_range, direction);
 }
 
 }  // namespace content::indexed_db
diff --git a/content/browser/indexed_db/instance/fake_transaction.h b/content/browser/indexed_db/instance/fake_transaction.h
index 31d5590..ebf93edf 100644
--- a/content/browser/indexed_db/instance/fake_transaction.h
+++ b/content/browser/indexed_db/instance/fake_transaction.h
@@ -81,28 +81,24 @@
       const blink::IndexedDBKey& key,
       std::unique_ptr<blink::IndexedDBKey>* found_primary_key,
       bool* exists) override;
-  std::unique_ptr<indexed_db::BackingStore::Cursor> OpenObjectStoreKeyCursor(
-      int64_t object_store_id,
-      const blink::IndexedDBKeyRange& key_range,
-      blink::mojom::IDBCursorDirection,
-      Status*) override;
-  std::unique_ptr<indexed_db::BackingStore::Cursor> OpenObjectStoreCursor(
-      int64_t object_store_id,
-      const blink::IndexedDBKeyRange& key_range,
-      blink::mojom::IDBCursorDirection,
-      Status*) override;
-  std::unique_ptr<indexed_db::BackingStore::Cursor> OpenIndexKeyCursor(
-      int64_t object_store_id,
-      int64_t index_id,
-      const blink::IndexedDBKeyRange& key_range,
-      blink::mojom::IDBCursorDirection,
-      Status*) override;
-  std::unique_ptr<indexed_db::BackingStore::Cursor> OpenIndexCursor(
-      int64_t object_store_id,
-      int64_t index_id,
-      const blink::IndexedDBKeyRange& key_range,
-      blink::mojom::IDBCursorDirection,
-      Status*) override;
+  base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
+  OpenObjectStoreKeyCursor(int64_t object_store_id,
+                           const blink::IndexedDBKeyRange& key_range,
+                           blink::mojom::IDBCursorDirection) override;
+  base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
+  OpenObjectStoreCursor(int64_t object_store_id,
+                        const blink::IndexedDBKeyRange& key_range,
+                        blink::mojom::IDBCursorDirection) override;
+  base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
+  OpenIndexKeyCursor(int64_t object_store_id,
+                     int64_t index_id,
+                     const blink::IndexedDBKeyRange& key_range,
+                     blink::mojom::IDBCursorDirection) override;
+  base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
+  OpenIndexCursor(int64_t object_store_id,
+                  int64_t index_id,
+                  const blink::IndexedDBKeyRange& key_range,
+                  blink::mojom::IDBCursorDirection) override;
 
  private:
   Status result_;
diff --git a/content/browser/indexed_db/instance/leveldb/backing_store.cc b/content/browser/indexed_db/instance/leveldb/backing_store.cc
index 63fd758..4406305 100644
--- a/content/browser/indexed_db/instance/leveldb/backing_store.cc
+++ b/content/browser/indexed_db/instance/leveldb/backing_store.cc
@@ -2359,23 +2359,25 @@
     const IndexedDBKeyRange& key_range) {
   // TODO(dmurph): Remove the need to create these cursors.
   // https://crbug.com/980678
-  Status s;
-  std::unique_ptr<indexed_db::BackingStore::Cursor> start_cursor =
-      OpenObjectStoreCursor(object_store_id, key_range,
-                            blink::mojom::IDBCursorDirection::Next, &s);
-  if (!s.ok()) {
-    return s;
+  auto result = OpenObjectStoreCursor(object_store_id, key_range,
+                                      blink::mojom::IDBCursorDirection::Next);
+  if (!result.has_value()) {
+    return result.error();
   }
+  std::unique_ptr<indexed_db::BackingStore::Cursor> start_cursor =
+      std::move(*result);
+
   if (!start_cursor) {
     return Status::OK();  // Empty range == delete success.
   }
-  std::unique_ptr<indexed_db::BackingStore::Cursor> end_cursor =
-      OpenObjectStoreCursor(object_store_id, key_range,
-                            blink::mojom::IDBCursorDirection::Prev, &s);
+  result = OpenObjectStoreCursor(object_store_id, key_range,
+                                 blink::mojom::IDBCursorDirection::Prev);
 
-  if (!s.ok()) {
-    return s;
+  if (!result.has_value()) {
+    return result.error();
   }
+  std::unique_ptr<indexed_db::BackingStore::Cursor> end_cursor =
+      std::move(*result);
   if (!end_cursor) {
     return Status::OK();  // Empty range == delete success.
   }
@@ -2396,8 +2398,8 @@
     return InternalInconsistencyStatus();
   }
 
-  s = DeleteBlobsInRange(this, database_id(), start_blob_number.Encode(),
-                         end_blob_number.Encode(), false);
+  Status s = DeleteBlobsInRange(this, database_id(), start_blob_number.Encode(),
+                                end_blob_number.Encode(), false);
   if (!s.ok()) {
     return s;
   }
@@ -3920,109 +3922,97 @@
   return s->ok();
 }
 
-std::unique_ptr<indexed_db::BackingStore::Cursor>
+base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
 BackingStore::Transaction::OpenObjectStoreCursor(
     int64_t object_store_id,
     const IndexedDBKeyRange& range,
-    blink::mojom::IDBCursorDirection direction,
-    Status* s) {
+    blink::mojom::IDBCursorDirection direction) {
   TRACE_EVENT0("IndexedDB", "BackingStore::OpenObjectStoreCursor");
 
   TransactionalLevelDBTransaction* leveldb_transaction = transaction();
   BackingStore::Cursor::CursorOptions cursor_options;
   cursor_options.mode = mode();
   // TODO(cmumford): Handle this error (crbug.com/363397)
+  Status s;
   if (!ObjectStoreCursorOptions(leveldb_transaction, database_id(),
                                 object_store_id, range, direction,
-                                &cursor_options, s)) {
+                                &cursor_options, &s)) {
+    if (!s.ok()) {
+      return base::unexpected(s);
+    }
     return nullptr;
   }
-  std::unique_ptr<ObjectStoreCursorImpl> cursor(
-      std::make_unique<ObjectStoreCursorImpl>(AsWeakPtr(), database_id(),
-                                              cursor_options));
-  if (!cursor->FirstSeek(s)) {
-    return nullptr;
-  }
-
-  return std::move(cursor);
+  return PrepareCursor(std::make_unique<ObjectStoreCursorImpl>(
+      AsWeakPtr(), database_id(), cursor_options));
 }
 
-std::unique_ptr<indexed_db::BackingStore::Cursor>
+base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
 BackingStore::Transaction::OpenObjectStoreKeyCursor(
     int64_t object_store_id,
     const IndexedDBKeyRange& range,
-    blink::mojom::IDBCursorDirection direction,
-    Status* s) {
+    blink::mojom::IDBCursorDirection direction) {
   TRACE_EVENT0("IndexedDB", "BackingStore::OpenObjectStoreKeyCursor");
 
   TransactionalLevelDBTransaction* leveldb_transaction = transaction();
   BackingStore::Cursor::CursorOptions cursor_options;
   cursor_options.mode = mode();
   // TODO(cmumford): Handle this error (crbug.com/363397)
+  Status s;
   if (!ObjectStoreCursorOptions(leveldb_transaction, database_id(),
                                 object_store_id, range, direction,
-                                &cursor_options, s)) {
+                                &cursor_options, &s)) {
+    if (!s.ok()) {
+      return base::unexpected(s);
+    }
     return nullptr;
   }
-  std::unique_ptr<ObjectStoreKeyCursorImpl> cursor(
-      std::make_unique<ObjectStoreKeyCursorImpl>(AsWeakPtr(), database_id(),
-                                                 cursor_options));
-  if (!cursor->FirstSeek(s)) {
-    return nullptr;
-  }
-
-  return std::move(cursor);
+  return PrepareCursor(std::make_unique<ObjectStoreKeyCursorImpl>(
+      AsWeakPtr(), database_id(), cursor_options));
 }
 
-std::unique_ptr<indexed_db::BackingStore::Cursor>
+base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
 BackingStore::Transaction::OpenIndexKeyCursor(
     int64_t object_store_id,
     int64_t index_id,
     const IndexedDBKeyRange& range,
-    blink::mojom::IDBCursorDirection direction,
-    Status* s) {
+    blink::mojom::IDBCursorDirection direction) {
   TRACE_EVENT0("IndexedDB", "BackingStore::OpenIndexKeyCursor");
-  *s = Status::OK();
   TransactionalLevelDBTransaction* leveldb_transaction = transaction();
   BackingStore::Cursor::CursorOptions cursor_options;
   cursor_options.mode = mode();
+  Status s;
   if (!IndexCursorOptions(leveldb_transaction, database_id(), object_store_id,
-                          index_id, range, direction, &cursor_options, s)) {
+                          index_id, range, direction, &cursor_options, &s)) {
+    if (!s.ok()) {
+      return base::unexpected(s);
+    }
     return nullptr;
   }
-  std::unique_ptr<IndexKeyCursorImpl> cursor(
-      std::make_unique<IndexKeyCursorImpl>(AsWeakPtr(), database_id(),
-                                           cursor_options));
-  if (!cursor->FirstSeek(s)) {
-    return nullptr;
-  }
-
-  return std::move(cursor);
+  return PrepareCursor(std::make_unique<IndexKeyCursorImpl>(
+      AsWeakPtr(), database_id(), cursor_options));
 }
 
-std::unique_ptr<indexed_db::BackingStore::Cursor>
+base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
 BackingStore::Transaction::OpenIndexCursor(
     int64_t object_store_id,
     int64_t index_id,
     const IndexedDBKeyRange& range,
-    blink::mojom::IDBCursorDirection direction,
-    Status* s) {
+    blink::mojom::IDBCursorDirection direction) {
   TRACE_EVENT0("IndexedDB", "BackingStore::OpenIndexCursor");
 
   TransactionalLevelDBTransaction* leveldb_transaction = transaction();
   BackingStore::Cursor::CursorOptions cursor_options;
   cursor_options.mode = mode();
+  Status s;
   if (!IndexCursorOptions(leveldb_transaction, database_id(), object_store_id,
-                          index_id, range, direction, &cursor_options, s)) {
+                          index_id, range, direction, &cursor_options, &s)) {
+    if (!s.ok()) {
+      return base::unexpected(s);
+    }
     return nullptr;
   }
-  auto cursor = std::make_unique<IndexCursorImpl>(AsWeakPtr(), database_id(),
-                                                  cursor_options);
-  if (!cursor->FirstSeek(s)) {
-    return nullptr;
-  }
-
-  return cursor;
+  return PrepareCursor(std::make_unique<IndexCursorImpl>(
+      AsWeakPtr(), database_id(), cursor_options));
 }
 
 bool BackingStore::IsBlobCleanupPending() {
@@ -4276,6 +4266,20 @@
   }
 }
 
+base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
+BackingStore::Transaction::PrepareCursor(std::unique_ptr<Cursor> cursor) {
+  Status s;
+  if (cursor->FirstSeek(&s)) {
+    DCHECK(s.ok());
+    return cursor;
+  }
+
+  if (!s.ok()) {
+    return base::unexpected(s);
+  }
+  return nullptr;
+}
+
 Status BackingStore::Transaction::CommitPhaseOne(BlobWriteCallback callback) {
   DCHECK(transaction_.get());
   DCHECK(backing_store_);
diff --git a/content/browser/indexed_db/instance/leveldb/backing_store.h b/content/browser/indexed_db/instance/leveldb/backing_store.h
index 5659f52..4c99960 100644
--- a/content/browser/indexed_db/instance/leveldb/backing_store.h
+++ b/content/browser/indexed_db/instance/leveldb/backing_store.h
@@ -113,6 +113,8 @@
     base::WeakPtrFactory<Database> weak_factory_{this};
   };
 
+  class Cursor;
+
   // This class could be moved to the implementation file, but it's left here to
   // avoid needless git churn.
   class CONTENT_EXPORT Transaction
@@ -208,28 +210,24 @@
         const blink::IndexedDBKey& key,
         std::unique_ptr<blink::IndexedDBKey>* found_primary_key,
         bool* exists) override;
-    std::unique_ptr<indexed_db::BackingStore::Cursor> OpenObjectStoreKeyCursor(
-        int64_t object_store_id,
-        const blink::IndexedDBKeyRange& key_range,
-        blink::mojom::IDBCursorDirection,
-        Status*) override;
-    std::unique_ptr<indexed_db::BackingStore::Cursor> OpenObjectStoreCursor(
-        int64_t object_store_id,
-        const blink::IndexedDBKeyRange& key_range,
-        blink::mojom::IDBCursorDirection,
-        Status*) override;
-    std::unique_ptr<indexed_db::BackingStore::Cursor> OpenIndexKeyCursor(
-        int64_t object_store_id,
-        int64_t index_id,
-        const blink::IndexedDBKeyRange& key_range,
-        blink::mojom::IDBCursorDirection,
-        Status*) override;
-    std::unique_ptr<indexed_db::BackingStore::Cursor> OpenIndexCursor(
-        int64_t object_store_id,
-        int64_t index_id,
-        const blink::IndexedDBKeyRange& key_range,
-        blink::mojom::IDBCursorDirection,
-        Status*) override;
+    base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
+    OpenObjectStoreKeyCursor(int64_t object_store_id,
+                             const blink::IndexedDBKeyRange& key_range,
+                             blink::mojom::IDBCursorDirection) override;
+    base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
+    OpenObjectStoreCursor(int64_t object_store_id,
+                          const blink::IndexedDBKeyRange& key_range,
+                          blink::mojom::IDBCursorDirection) override;
+    base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
+    OpenIndexKeyCursor(int64_t object_store_id,
+                       int64_t index_id,
+                       const blink::IndexedDBKeyRange& key_range,
+                       blink::mojom::IDBCursorDirection) override;
+    base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
+    OpenIndexCursor(int64_t object_store_id,
+                    int64_t index_id,
+                    const blink::IndexedDBKeyRange& key_range,
+                    blink::mojom::IDBCursorDirection) override;
 
     Status PutExternalObjectsIfNeeded(const std::string& object_store_data_key,
                                       std::vector<IndexedDBExternalObject>*);
@@ -284,6 +282,11 @@
     void PartitionBlobsToRemove(BlobJournalType* dead_blobs,
                                 BlobJournalType* live_blobs) const;
 
+    // Prepares a cursor and returns it if successful, an error Status if
+    // there's an error, or null if the cursor is empty.
+    base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
+    PrepareCursor(std::unique_ptr<Cursor> cursor);
+
     // This does NOT mean that this class can outlive the BackingStore.
     // This is only to protect against security issues before this class is
     // refactored away and this isn't necessary.
diff --git a/content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.cc b/content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.cc
index 3cdd7a4..7a2a491 100644
--- a/content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.cc
+++ b/content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.cc
@@ -161,44 +161,40 @@
   return Status::OK();
 }
 
-std::unique_ptr<BackingStore::Cursor>
+base::expected<std::unique_ptr<BackingStore::Cursor>, Status>
 BackingStoreTransactionImpl::OpenObjectStoreKeyCursor(
     int64_t object_store_id,
     const blink::IndexedDBKeyRange& key_range,
-    blink::mojom::IDBCursorDirection,
-    Status*) {
+    blink::mojom::IDBCursorDirection) {
   NOTIMPLEMENTED();
   return nullptr;
 }
 
-std::unique_ptr<BackingStore::Cursor>
+base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
 BackingStoreTransactionImpl::OpenObjectStoreCursor(
     int64_t object_store_id,
     const blink::IndexedDBKeyRange& key_range,
-    blink::mojom::IDBCursorDirection,
-    Status*) {
+    blink::mojom::IDBCursorDirection) {
   NOTIMPLEMENTED();
   return nullptr;
 }
 
-std::unique_ptr<BackingStore::Cursor>
+base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
 BackingStoreTransactionImpl::OpenIndexKeyCursor(
     int64_t object_store_id,
     int64_t index_id,
     const blink::IndexedDBKeyRange& key_range,
-    blink::mojom::IDBCursorDirection,
-    Status*) {
+    blink::mojom::IDBCursorDirection) {
   NOTIMPLEMENTED();
   return nullptr;
 }
 
-std::unique_ptr<BackingStore::Cursor>
+base::expected<std::unique_ptr<indexed_db::BackingStore::Cursor>, Status>
 BackingStoreTransactionImpl::OpenIndexCursor(
     int64_t object_store_id,
     int64_t index_id,
     const blink::IndexedDBKeyRange& key_range,
-    blink::mojom::IDBCursorDirection,
-    Status*) {
+    blink::mojom::IDBCursorDirection) {
   NOTIMPLEMENTED();
   return nullptr;
 }
diff --git a/content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.h b/content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.h
index 5d97093e..581a2ce 100644
--- a/content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.h
+++ b/content/browser/indexed_db/instance/sqlite/backing_store_transaction_impl.h
@@ -81,28 +81,24 @@
       const blink::IndexedDBKey& key,
       std::unique_ptr<blink::IndexedDBKey>* found_primary_key,
       bool* exists) override;
-  std::unique_ptr<BackingStore::Cursor> OpenObjectStoreKeyCursor(
-      int64_t object_store_id,
-      const blink::IndexedDBKeyRange& key_range,
-      blink::mojom::IDBCursorDirection,
-      Status*) override;
-  std::unique_ptr<BackingStore::Cursor> OpenObjectStoreCursor(
-      int64_t object_store_id,
-      const blink::IndexedDBKeyRange& key_range,
-      blink::mojom::IDBCursorDirection,
-      Status*) override;
-  std::unique_ptr<BackingStore::Cursor> OpenIndexKeyCursor(
+  base::expected<std::unique_ptr<BackingStore::Cursor>, Status>
+  OpenObjectStoreKeyCursor(int64_t object_store_id,
+                           const blink::IndexedDBKeyRange& key_range,
+                           blink::mojom::IDBCursorDirection) override;
+  base::expected<std::unique_ptr<BackingStore::Cursor>, Status>
+  OpenObjectStoreCursor(int64_t object_store_id,
+                        const blink::IndexedDBKeyRange& key_range,
+                        blink::mojom::IDBCursorDirection) override;
+  base::expected<std::unique_ptr<BackingStore::Cursor>, Status>
+  OpenIndexKeyCursor(int64_t object_store_id,
+                     int64_t index_id,
+                     const blink::IndexedDBKeyRange& key_range,
+                     blink::mojom::IDBCursorDirection) override;
+  base::expected<std::unique_ptr<BackingStore::Cursor>, Status> OpenIndexCursor(
       int64_t object_store_id,
       int64_t index_id,
       const blink::IndexedDBKeyRange& key_range,
-      blink::mojom::IDBCursorDirection,
-      Status*) override;
-  std::unique_ptr<BackingStore::Cursor> OpenIndexCursor(
-      int64_t object_store_id,
-      int64_t index_id,
-      const blink::IndexedDBKeyRange& key_range,
-      blink::mojom::IDBCursorDirection,
-      Status*) override;
+      blink::mojom::IDBCursorDirection) override;
 
  protected:
   base::WeakPtr<BackingStoreDatabaseImpl> db_;
diff --git a/content/browser/interest_group/auction_runner_unittest.cc b/content/browser/interest_group/auction_runner_unittest.cc
index d40f87a..0db1ac0 100644
--- a/content/browser/interest_group/auction_runner_unittest.cc
+++ b/content/browser/interest_group/auction_runner_unittest.cc
@@ -1834,12 +1834,14 @@
     std::set<std::string> interest_group_names;
     std::set<std::string> keys;
     base::Value::Dict additional_params;
+    std::optional<std::string> buyer_tkv_signals;
 
     bool operator<(const BiddingPartitionInfo& other) const {
       return std::tie(partition_id, interest_group_names, keys,
-                      additional_params) <
+                      additional_params, buyer_tkv_signals) <
              std::tie(other.partition_id, other.interest_group_names,
-                      other.keys, other.additional_params);
+                      other.keys, other.additional_params,
+                      other.buyer_tkv_signals);
     }
   };
 
@@ -2001,7 +2003,10 @@
         for (const auto& partition : compression_group.second) {
           partitions.emplace_back(BiddingPartitionInfo{
               partition.partition_id, *partition.interest_group_names,
-              *partition.keys, partition.additional_params->Clone()});
+              *partition.keys, partition.additional_params->Clone(),
+              partition.buyer_tkv_signals == nullptr
+                  ? std::nullopt
+                  : std::make_optional(*partition.buyer_tkv_signals)});
         }
         request_info.compression_groups.emplace(compression_group.first,
                                                 std::move(partitions));
@@ -2376,6 +2381,9 @@
       auction_config.per_buyer_experiment_group_ids[kv.first] = kv.second;
     }
 
+    auction_config.non_shared_params.per_buyer_tkv_signals =
+        per_buyer_tkv_signals_;
+
     auction_config.non_shared_params.all_buyers_group_limit =
         all_buyers_group_limit_;
     auction_config.non_shared_params.all_buyers_priority_signals =
@@ -3848,6 +3856,7 @@
   std::optional<uint16_t> seller_experiment_group_id_;
   std::optional<uint16_t> all_buyer_experiment_group_id_;
   std::map<url::Origin, uint16_t> per_buyer_experiment_group_id_;
+  base::flat_map<url::Origin, std::string> per_buyer_tkv_signals_;
   uint16_t all_buyers_group_limit_ = std::numeric_limits<std::uint16_t>::max();
   std::optional<base::flat_map<std::string, double>>
       all_buyers_priority_signals_;
@@ -27966,6 +27975,112 @@
   EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_descriptor->url);
 }
 
+// Test that `buyer_tkv_signals` is respected in the case of trusted KVv2
+// bidding signals. This test completely depends on the checks in
+// MockTrustedSignalsCache that exactly the expected signals requests were made
+// to the cache.
+TEST_P(AuctionRunnerTrustedSignalsTest, TrustedSignalsKVv2BuyerTKVSignals) {
+  // Only KVv2 request contains contextual data which is buyer_tkv_signals for
+  // bidding signals.
+  if (!UsingKVv2Signals()) {
+    return;
+  }
+
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      blink::features::kFledgeTrustedSignalsKVv2ContextualData);
+
+  per_buyer_tkv_signals_[kBidder1] = "signals";
+  auction_worklet::AddJavascriptResponse(
+      &url_loader_factory_, kBidder1Url,
+      MakeConstBidScript(1, "https://ad1.com/"));
+  auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
+                                         kMinimumDecisionScript);
+
+  std::vector<StorageInterestGroup> bidders;
+  bidders.emplace_back(MakeInterestGroup(
+      kBidder1, kBidder1Name, kBidder1Url, kBidder1TrustedSignalsUrl,
+      {"k1", "k2"}, GURL("https://ad1.com"), /*ad_component_urls=*/std::nullopt,
+      coordinator_origin_));
+
+  auto bidder1_request_info = DefaultBidder1SignalsRequestInfo();
+  bidder1_request_info.compression_groups[0][0].buyer_tkv_signals = "signals";
+  // The actual response doesn't matter - this test depends on the logic in
+  // MockTrustedSignalsCache to verify all the expected requests were sent, with
+  // the correct parameters.
+  AddBiddingSignalsCacheResult(std::move(bidder1_request_info),
+                               MakeBidder1CompressionGroupMap());
+
+  RunAuctionAndWait(kSellerUrl, std::move(bidders));
+  EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_descriptor->url);
+}
+
+// Test that `buyer_tkv_signals` set with bidder2 will not show up in the
+// request of bidder1's trusted KVv2 bidding signals. This test completely
+// depends on the checks in MockTrustedSignalsCache that exactly the expected
+// signals requests were made to the cache.
+TEST_P(AuctionRunnerTrustedSignalsTest,
+       TrustedSignalsKVv2BuyerTKVSignalsWithWrongBuyer) {
+  // Only KVv2 request contains contextual data which is buyer_tkv_signals for
+  // bidding signals.
+  if (!UsingKVv2Signals()) {
+    return;
+  }
+
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      blink::features::kFledgeTrustedSignalsKVv2ContextualData);
+
+  // Set up per_buyer_tkv_signals with only bidder2's origin and "signals" will
+  // not show up in bidder1's bidding signals request.
+  per_buyer_tkv_signals_[kBidder2] = "signals";
+  auction_worklet::AddJavascriptResponse(
+      &url_loader_factory_, kBidder1Url,
+      MakeConstBidScript(1, "https://ad1.com/"));
+  auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
+                                         kMinimumDecisionScript);
+
+  std::vector<StorageInterestGroup> bidders;
+  bidders.emplace_back(MakeInterestGroup(
+      kBidder1, kBidder1Name, kBidder1Url, kBidder1TrustedSignalsUrl,
+      {"k1", "k2"}, GURL("https://ad1.com"), /*ad_component_urls=*/std::nullopt,
+      coordinator_origin_));
+
+  AddDefaultBidder1SignalsResult();
+  RunAuctionAndWait(kSellerUrl, std::move(bidders));
+  EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_descriptor->url);
+}
+
+// When the feature is disabled, ensure `buyer_tkv_signals` is not included in
+// bidder1's bidding signals request even have the correct bidder origin. This
+// test completely depends on the checks in MockTrustedSignalsCache that exactly
+// the expected signals requests were made to the cache.
+TEST_P(AuctionRunnerTrustedSignalsTest,
+       TrustedSignalsKVv2BuyerTKVSignalsFeatureDisabled) {
+  // Only KVv2 request contains contextual data which is buyer_tkv_signals for
+  // bidding signals.
+  if (!UsingKVv2Signals()) {
+    return;
+  }
+
+  per_buyer_tkv_signals_[kBidder1] = "signals";
+  auction_worklet::AddJavascriptResponse(
+      &url_loader_factory_, kBidder1Url,
+      MakeConstBidScript(1, "https://ad1.com/"));
+  auction_worklet::AddJavascriptResponse(&url_loader_factory_, kSellerUrl,
+                                         kMinimumDecisionScript);
+
+  std::vector<StorageInterestGroup> bidders;
+  bidders.emplace_back(MakeInterestGroup(
+      kBidder1, kBidder1Name, kBidder1Url, kBidder1TrustedSignalsUrl,
+      {"k1", "k2"}, GURL("https://ad1.com"), /*ad_component_urls=*/std::nullopt,
+      coordinator_origin_));
+
+  AddDefaultBidder1SignalsResult();
+  RunAuctionAndWait(kSellerUrl, std::move(bidders));
+  EXPECT_EQ(GURL("https://ad1.com/"), result_.ad_descriptor->url);
+}
+
 TEST_F(AuctionRunnerTest, TrustedBiddingSignalsJointBatchedRequests) {
   url_loader_factory_.ClearResponses();
   auction_worklet::AddJavascriptResponse(
diff --git a/content/browser/interest_group/interest_group_auction.cc b/content/browser/interest_group/interest_group_auction.cc
index b96ca48c..230d99d 100644
--- a/content/browser/interest_group/interest_group_auction.cc
+++ b/content/browser/interest_group/interest_group_auction.cc
@@ -2392,7 +2392,7 @@
                 *interest_group.trusted_bidding_signals_coordinator,
                 interest_group.trusted_bidding_signals_keys,
                 std::move(additional_params),
-                /*buyer_tkv_signals=*/std::nullopt, partition_id);
+                auction_->GetBuyerTKVSignals(owner_), partition_id);
     return auction_worklet::mojom::TrustedSignalsCacheKey::New(
         bid_state.bidding_signals_handle->compression_group_token(),
         partition_id);
@@ -4790,6 +4790,21 @@
   return std::max(val, uint16_t{1});
 }
 
+std::optional<std::string> InterestGroupAuction::GetBuyerTKVSignals(
+    const url::Origin& owner) const {
+  if (!base::FeatureList::IsEnabled(
+          blink::features::kFledgeTrustedSignalsKVv2ContextualData)) {
+    return std::nullopt;
+  }
+
+  auto it = config_->non_shared_params.per_buyer_tkv_signals.find(owner);
+  if (it != config_->non_shared_params.per_buyer_tkv_signals.end()) {
+    return it->second;
+  }
+
+  return std::nullopt;
+}
+
 std::optional<uint16_t> InterestGroupAuction::GetBuyerExperimentId(
     const blink::AuctionConfig& config,
     const url::Origin& buyer) {
diff --git a/content/browser/interest_group/interest_group_auction.h b/content/browser/interest_group/interest_group_auction.h
index c758e33..89751b9 100644
--- a/content/browser/interest_group/interest_group_auction.h
+++ b/content/browser/interest_group/interest_group_auction.h
@@ -1210,6 +1210,10 @@
   // ensuring that it's at least 1.
   uint16_t GetBuyerMultiBidLimit(const url::Origin& buyer);
 
+  // Gets the buyer `per-buyer-tkv-signals` in `config` for interest group
+  // buyer.
+  std::optional<std::string> GetBuyerTKVSignals(const url::Origin& buyer) const;
+
   // -----------------------------------
   // Methods not associated with a phase
   // -----------------------------------
diff --git a/content/browser/interest_group/interest_group_browsertest.cc b/content/browser/interest_group/interest_group_browsertest.cc
index 7f58365c..2383ed931 100644
--- a/content/browser/interest_group/interest_group_browsertest.cc
+++ b/content/browser/interest_group/interest_group_browsertest.cc
@@ -25,11 +25,13 @@
 #include "base/containers/contains.h"
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
+#include "base/containers/span_reader.h"
 #include "base/feature_list.h"
 #include "base/functional/callback.h"
 #include "base/functional/callback_forward.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
+#include "base/json/string_escape.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
@@ -225,6 +227,8 @@
      0xd1, 0x8d, 0x16, 0x57, 0x5c, 0xe7, 0x3a, 0x2c, 0x60, 0x22, 0xfb,
      0x44, 0xe4, 0xc8, 0x5a, 0xb5, 0x41, 0xee, 0xf9, 0x34, 0xee};
 
+constexpr char kNoContextualDataValue[] = "\"no contextual data\"";
+
 std::string base64Decode(std::string_view input) {
   std::string bytes;
   CHECK(base::Base64UrlDecode(
@@ -790,6 +794,7 @@
          // TODO(crrev.com/c/6096602): Remove once implementation is removed.
          {blink::features::kFledgeDirectFromSellerSignalsWebBundles, {}},
          {blink::features::kFledgeTrustedSignalsKVv1CreativeScanning, {}},
+         {blink::features::kFledgeTrustedSignalsKVv2ContextualData, {}},
          {features::kFledgeTextConversionHelpers, {}},
          {network::features::kAdAuctionEventRegistration, {}},
          // Needed for reliable handling of click ARA (and hence clickiness)
@@ -6413,20 +6418,7 @@
   EXPECT_TRUE(console_observer.Wait());
 }
 
-class InterestGroupContextualDataBrowserTest : public InterestGroupBrowserTest {
- public:
-  InterestGroupContextualDataBrowserTest() {
-    feature_list_.InitWithFeatures(
-        {/*enabled_features=*/blink::features::
-             kFledgeTrustedSignalsKVv2ContextualData},
-        /*disabled_features=*/{});
-  }
-
- protected:
-  base::test::ScopedFeatureList feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(InterestGroupContextualDataBrowserTest,
+IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
                        RunAdAuctionInvalidPerBuyerTKVSignalsOrigin) {
   GURL test_url = embedded_https_test_server().GetURL("a.test", "/echo");
   url::Origin test_origin = url::Origin::Create(test_url);
@@ -6452,7 +6444,7 @@
   WaitForAccessObserved({});
 }
 
-IN_PROC_BROWSER_TEST_F(InterestGroupContextualDataBrowserTest,
+IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
                        RunAdAuctionInvalidPerBuyerTKVSignals) {
   GURL test_url = embedded_https_test_server().GetURL("a.test", "/echo");
   url::Origin test_origin = url::Origin::Create(test_url);
@@ -6477,7 +6469,32 @@
   WaitForAccessObserved({});
 }
 
-IN_PROC_BROWSER_TEST_F(InterestGroupContextualDataBrowserTest,
+IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
+                       RunAdAuctionUndefinedPerBuyerTKVSignals) {
+  GURL test_url = embedded_https_test_server().GetURL("a.test", "/echo");
+  url::Origin test_origin = url::Origin::Create(test_url);
+  GURL decision_url = embedded_https_test_server().GetURL(
+      "a.test", "/interest_group/decision_logic.js");
+  ASSERT_TRUE(NavigateToURL(shell(), test_url));
+
+  AttachInterestGroupObserver();
+
+  EXPECT_EQ(base::StringPrintf(
+                "TypeError: Failed to execute 'runAdAuction' on 'Navigator': "
+                "perBuyerTKVSignals for AuctionAdConfig with seller '%s' must "
+                "be a JSON-serializable object.",
+                test_origin.Serialize().c_str()),
+            RunAuctionAndWait(JsReplace(R"({
+      seller: $1,
+      decisionLogicURL: $2,
+      perBuyerTKVSignals: {'https://test.com': undefined},
+      interestGroupBuyers: []
+  })",
+                                        test_origin, decision_url)));
+  WaitForAccessObserved({});
+}
+
+IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
                        RunAdAuctionInvalidSellerTKVSignals) {
   GURL test_url = embedded_https_test_server().GetURL("a.test", "/echo");
   url::Origin test_origin = url::Origin::Create(test_url);
@@ -6506,7 +6523,7 @@
 
 // Exercise rejection path in the renderer for promise-delivered
 // sellerTKVSignals.
-IN_PROC_BROWSER_TEST_F(InterestGroupContextualDataBrowserTest,
+IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
                        RunAdAuctionRejectPromiseSellerTKVSignals) {
   GURL test_url = embedded_https_test_server().GetURL("a.test", "/echo");
   url::Origin test_origin = url::Origin::Create(test_url);
@@ -6531,7 +6548,7 @@
 
 // Exercise error-handling path in the renderer for promise-delivered
 // sellerTKVSignals.
-IN_PROC_BROWSER_TEST_F(InterestGroupContextualDataBrowserTest,
+IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
                        RunAdAuctionResolvePromiseInvalidSellerTKVSignals) {
   GURL test_url = embedded_https_test_server().GetURL("a.test", "/echo");
   url::Origin test_origin = url::Origin::Create(test_url);
@@ -29470,6 +29487,291 @@
   EXPECT_EQ(ad_url, RunAuctionAndWaitForUrl(auction_config));
 }
 
+class InterestGroupTrustedSignalsKVv2ContextualDataBrowserTest
+    : public InterestGroupTrustedSignalsKVv2BrowserTest {
+ public:
+  void SetUpOnMainThread() override {
+    embedded_https_test_server().RegisterRequestHandler(
+        base::BindRepeating(&HandleTrustedKVv2Signals));
+
+    InterestGroupPrivateNetworkBrowserTest::SetUpOnMainThread();
+  }
+
+  void TestPerBuyerTKVSignals(const std::string& expected_bidding_key,
+                              const std::string& buyer_tkv_signals) {
+    const char kPublisher[] = "a.test";
+    const char kBidder[] = "b.test";
+    const char kSeller[] = "c.test";
+
+    GURL test_url = embedded_https_test_server().GetURL(
+        kPublisher, "/page_with_iframe.html");
+    GURL ad_url =
+        embedded_https_test_server().GetURL(kBidder, "/echo?render_cars");
+    GURL bidder_url = embedded_https_test_server().GetURL(kBidder, "/echo");
+    GURL bidder_script_url = embedded_https_test_server().GetURL(
+        kBidder,
+        "/interest_group/bidding_logic_trusted_kvv2_bidding_signals.js");
+    GURL bidder_signals_url = embedded_https_test_server().GetURL(
+        kBidder, "/trusted_kvv2_bidding_signals");
+    GURL seller_script_url = embedded_https_test_server().GetURL(
+        kSeller, "/interest_group/decision_logic.js");
+
+    ConfigureSignalsServerKeys({url::Origin::Create(bidder_signals_url)});
+
+    url::Origin bidder_origin = url::Origin::Create(bidder_script_url);
+    url::Origin seller_origin = url::Origin::Create(seller_script_url);
+
+    // Navigate to bidder site, and add an interest group.
+    ASSERT_TRUE(NavigateToURL(shell(), bidder_url));
+    EXPECT_EQ(
+        kSuccess,
+        JoinInterestGroupAndVerify(
+            blink::TestInterestGroupBuilder(
+                /*owner=*/bidder_origin,
+                /*name=*/"group")
+                .SetBiddingUrl(bidder_script_url)
+                .SetTrustedBiddingSignalsUrl(bidder_signals_url)
+                .SetTrustedBiddingSignalsKeys({{"bidderTKVSignals"}})
+                .SetAds(
+                    /*ads=*/{{{ad_url, /*metadata=*/std::nullopt}}})
+                .SetTrustedBiddingSignalsCoordinator(
+                    url::Origin::Create(GURL("https://coordinator.test")))
+                .SetExecutionMode(
+                    blink::InterestGroup::ExecutionMode::kGroupedByOriginMode)
+                .Build()));
+
+    // Register a bidder script that only bids if `trustedBiddingSignals` is
+    // successfully fetched.
+    const char kBidderScript[] = R"(
+    function generateBid(
+        interestGroup, auctionSignals, perBuyerSignals, trustedBiddingSignals,
+        browserSignals, directFromSellerSignals,
+        crossOriginTrustedBiddingSignals) {
+      if ('crossOriginDataVersion' in browserSignals) {
+        throw 'Unexpected crossOriginDataVersion in browserSignals.';
+      }
+
+      if (crossOriginTrustedBiddingSignals !== null) {
+        throw 'Unexpected crossOriginTrustedBiddingSignals found.';
+      }
+
+      if (trustedBiddingSignals.bidderTKVSignals !== %s) {
+        throw 'Unexpected trustedBiddingSignals: ' +
+          JSON.stringify(trustedBiddingSignals);
+      }
+
+      return {
+        bid: 1,
+        render: interestGroup.ads[0].renderURL,
+      };
+    })";
+
+    network_responder_->RegisterNetworkResponse(
+        bidder_script_url.path(),
+        base::StringPrintf(kBidderScript, expected_bidding_key.c_str()),
+        "application/javascript");
+
+    // Navigate to publisher.
+    ASSERT_TRUE(
+        NavigateToURL(shell(), embedded_https_test_server().GetURL(
+                                   kPublisher, "/page_with_iframe.html")));
+
+    const char kAuctionConfig[] =
+        R"({
+              seller: "%s",
+              decisionLogicURL: "%s",
+              interestGroupBuyers: ["%s"],
+              perBuyerTKVSignals: {"%s": %s},
+            })";
+
+    std::string auction_config = base::StringPrintf(
+        kAuctionConfig, seller_origin.Serialize().c_str(),
+        seller_script_url.spec().c_str(), bidder_origin.Serialize().c_str(),
+        bidder_origin.Serialize().c_str(), buyer_tkv_signals.c_str());
+
+    EXPECT_EQ(ad_url, RunAuctionAndWaitForUrl(auction_config));
+  }
+
+ protected:
+  static std::unique_ptr<net::test_server::HttpResponse>
+  HandleTrustedKVv2Signals(const net::test_server::HttpRequest& request) {
+    if (!base::StartsWith(request.relative_url,
+                          "/trusted_kvv2_bidding_signals")) {
+      return nullptr;
+    }
+
+    // Only posts should be sent to the KVv2 serer - no GETs or OPTIONs.
+    EXPECT_EQ(request.method, net::test_server::METHOD_POST);
+
+    constexpr char kBiddingBase[] =
+        R"([
+          {
+            "id": 0,
+            "keyGroupOutputs": [
+              {
+                "tags": [
+                  "keys"
+                ],
+                "keyValues": {
+                  "bidderTKVSignals": {
+                    "value": "%s"
+                  }
+                }
+              }
+            ]
+          }
+        ])";
+
+    // Decrypt the request.
+    auto response_key_config = quiche::ObliviousHttpHeaderKeyConfig::Create(
+        kTestPrivacySandboxCoordinatorId, EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
+        EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_256_GCM);
+    CHECK(response_key_config.ok()) << response_key_config.status();
+
+    auto ohttp_gateway = quiche::ObliviousHttpGateway::Create(
+                             GetTestPrivacySandboxCoordinatorPrivateKey(),
+                             response_key_config.value())
+                             .value();
+
+    auto received_request = ohttp_gateway.DecryptObliviousHttpRequest(
+        request.content, "message/ad-auction-trusted-signals-request");
+    CHECK(received_request.ok()) << received_request.status();
+
+    // Get the request body as a CBOR value.
+    base::span<const uint8_t> body_span =
+        base::as_byte_span(received_request->GetPlaintextData());
+    base::SpanReader reader(body_span);
+    // Skip the first 1 byte since the request is always uncompressed.
+    reader.Skip(1u);
+    uint32_t length;
+    reader.ReadU32BigEndian(length);
+    std::optional<base::span<const uint8_t>> cbor_bytes = reader.Read(length);
+    CHECK(cbor_bytes);
+    std::optional<cbor::Value> request_body =
+        cbor::Reader::Read(base::span(cbor_bytes.value()));
+    CHECK(request_body);
+
+    // Extract the `contextualData` from the request body.
+    std::string contextual_data = kNoContextualDataValue;
+    const cbor::Value::MapValue& request_map = request_body->GetMap();
+
+    auto per_partition_metadata_it =
+        request_map.find(cbor::Value("perPartitionMetadata"));
+    if (per_partition_metadata_it != request_map.end()) {
+      const cbor::Value::ArrayValue& contextual_data_array =
+          per_partition_metadata_it->second.GetMap()
+              .at(cbor::Value("contextualData"))
+              .GetArray();
+
+      // With current browser test framework setup, there should only be one
+      // contextual data entry.
+      CHECK(contextual_data_array.size() == 1u);
+      contextual_data = contextual_data_array[0]
+                            .GetMap()
+                            .at(cbor::Value("value"))
+                            .GetString();
+    }
+
+    // Construct the response body.
+    std::string escaped_contextual_data;
+    CHECK(base::EscapeJSONString(contextual_data,
+                                 /*put_in_quotes=*/false,
+                                 &escaped_contextual_data));
+    std::string content =
+        base::StringPrintf(kBiddingBase, escaped_contextual_data.c_str());
+
+    cbor::Value::MapValue compression_group;
+    compression_group.try_emplace(cbor::Value("compressionGroupId"),
+                                  cbor::Value(0));
+    compression_group.try_emplace(
+        cbor::Value("content"),
+        cbor::Value(auction_worklet::test::ToCborVector(content)));
+
+    cbor::Value::ArrayValue compression_groups;
+    compression_groups.emplace_back(std::move(compression_group));
+
+    cbor::Value::MapValue body_map;
+    body_map.try_emplace(cbor::Value("compressionGroups"),
+                         cbor::Value(std::move(compression_groups)));
+
+    cbor::Value body_value(std::move(body_map));
+    std::optional<std::vector<uint8_t>> maybe_body_bytes =
+        cbor::Writer::Write(body_value);
+
+    std::string response_body = auction_worklet::test::CreateKVv2ResponseBody(
+        base::as_string_view(maybe_body_bytes.value()));
+    auto response_context =
+        std::move(received_request).value().ReleaseContext();
+
+    // Encrypt the response body.
+    auto maybe_response = ohttp_gateway.CreateObliviousHttpResponse(
+        std::move(response_body), response_context,
+        "message/ad-auction-trusted-signals-response");
+    EXPECT_TRUE(maybe_response.ok()) << maybe_response.status();
+
+    auto response = std::make_unique<net::test_server::BasicHttpResponse>();
+    response->set_content_type("message/ad-auction-trusted-signals-response");
+    response->set_content(maybe_response->EncapsulateAndSerialize());
+    response->AddCustomHeader("Ad-Auction-Allowed", "true");
+
+    return response;
+  }
+
+  const url::Origin kCoordinatorOrigin =
+      url::Origin::Create(GURL("https://coordinator.test"));
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    All,
+    InterestGroupTrustedSignalsKVv2ContextualDataBrowserTest,
+    testing::Values(true));
+
+IN_PROC_BROWSER_TEST_P(InterestGroupTrustedSignalsKVv2ContextualDataBrowserTest,
+                       PerBuyerTKVSignalsIsInteger) {
+  TestPerBuyerTKVSignals(/*expected_bidding_key=*/"100",
+                         /*buyer_tkv_signals=*/"100");
+}
+
+IN_PROC_BROWSER_TEST_P(InterestGroupTrustedSignalsKVv2ContextualDataBrowserTest,
+                       PerBuyerTKVSignalsIsString) {
+  TestPerBuyerTKVSignals(/*expected_bidding_key=*/"\"contextual data\"",
+                         /*buyer_tkv_signals=*/"\"contextual data\"");
+}
+
+IN_PROC_BROWSER_TEST_P(InterestGroupTrustedSignalsKVv2ContextualDataBrowserTest,
+                       PerBuyerTKVSignalsIsArray) {
+  TestPerBuyerTKVSignals(/*expected_bidding_key=*/"\"[1, 2, 3]\"",
+                         /*buyer_tkv_signals=*/"\"[1, 2, 3]\"");
+}
+
+IN_PROC_BROWSER_TEST_P(InterestGroupTrustedSignalsKVv2ContextualDataBrowserTest,
+                       PerBuyerTKVSignalsIsNull) {
+  TestPerBuyerTKVSignals(/*expected_bidding_key=*/"null",
+                         /*buyer_tkv_signals=*/"null");
+}
+
+class DisableKVv2ContextualDataBrowserTest
+    : public InterestGroupTrustedSignalsKVv2ContextualDataBrowserTest {
+ public:
+  DisableKVv2ContextualDataBrowserTest() {
+    feature_list_.InitAndDisableFeature(
+        blink::features::kFledgeTrustedSignalsKVv2ContextualData);
+  }
+
+ protected:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         DisableKVv2ContextualDataBrowserTest,
+                         testing::Values(true));
+
+IN_PROC_BROWSER_TEST_P(DisableKVv2ContextualDataBrowserTest,
+                       PerBuyerTKVSignalsWithDisabledFeature) {
+  TestPerBuyerTKVSignals(/*expected_bidding_key=*/kNoContextualDataValue,
+                         /*buyer_tkv_signals=*/"\"contextual data\"");
+}
+
 class DisableLocalAuctionInterestGroupBrowserTest
     : public InterestGroupBrowserTest {
  public:
@@ -29524,9 +29826,9 @@
                        DisableLocalAuctions) {
   SetUpTestWithOneInterestGroup();
   // If local auctions are disable via kFledgeDisableLocalAdsAuctions and
-  // there's no "serverResponse" key in the auctionConfig, runAdAuction will not
-  // throw an error. It will return nullptr, which matches what happens when
-  // there is no auction winner.
+  // there's no "serverResponse" key in the auctionConfig, runAdAuction will
+  // not throw an error. It will return nullptr, which matches what happens
+  // when there is no auction winner.
   EXPECT_EQ(nullptr, RunAuctionAndWait(JsReplace(
                          R"({
     seller: $1,
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 4e4dd67..f5534e1b 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -1630,11 +1630,14 @@
       perfetto::Track::Global(kGlobalInstantTrackId));
 
   // Define a helper to log both a trace event slice and a corresponding metric
-  // for one stage of a navigation.
+  // for one stage of a navigation. If `histogram_name` is specified, it will be
+  // used for the histogram name instead of `name`. If `url` is specified, it
+  // will be emitted as page_load.url argument along the trace event.
   auto log_trace_event_and_uma =
-      [&](const std::string& name, const perfetto::NamedTrack track,
+      [&](perfetto::StaticString name, const perfetto::NamedTrack track,
           const base::TimeTicks& begin_time, const base::TimeTicks& end_time,
-          const std::string& histogram_name = std::string()) {
+          const std::string& histogram_name = std::string(),
+          const std::string& url = std::string()) {
         if (begin_time.is_null() || end_time.is_null()) {
           return;
         }
@@ -1657,8 +1660,15 @@
           return;
         }
 
-        TRACE_EVENT_BEGIN("navigation", perfetto::DynamicString{name}, track,
-                          begin_time);
+        TRACE_EVENT_BEGIN("navigation", name, track, begin_time,
+                          [&](perfetto::EventContext& ctx) {
+                            if (url.empty()) {
+                              return;
+                            }
+                            perfetto::protos::pbzero::PageLoad* page_load =
+                                ctx.event<ChromeTrackEvent>()->set_page_load();
+                            page_load->set_url(url);
+                          });
         TRACE_EVENT_END("navigation", track, end_time);
 
         // When provided, `histogram_name` is used to avoid including variable
@@ -1668,23 +1678,32 @@
         // URL in metric names for UMA.
         base::UmaHistogramTimes(
             "Navigation.Timeline." +
-                (histogram_name.empty() ? name : histogram_name) + ".Duration",
+                (histogram_name.empty() ? std::string(name.value)
+                                        : histogram_name) +
+                ".Duration",
             end_time - begin_time);
       };
 
   // Actual navigation events are logged below in contiguous (or nested)
   // intervals.
 
-  // Record a top-level "Navigation: url" trace event with the duration of the
+  // Record a top-level "Navigation" trace event with the duration of the
   // full navigation, and then break it down into nested intervals which will
-  // show up under it. Do not include `url` in the histogram name. Note that
-  // `url` is the committing URL, which might differ from the starting URL, e.g.
-  // due to redirects.
+  // show up under it. Note that `url` is the committing URL, which might differ
+  // from the starting URL, e.g. due to redirects.
   // TODO(crbug.com/405437928): Overlapping navigations may incorrectly appear
   // to be nested, using the wrong end times.
+  log_trace_event_and_uma("NavigationTotal", track1, timeline.start,
+                          timeline.finish,
+                          /*histogram_name=*/"Total", /*url=*/url.spec());
+  // Emit a trace event with url in the name for convenience.
+  // TODO(crbug.com/415720503): Remove once Perfetto navigation plugins
+  // surfaces urls.
   std::string top_level_trace_event_name = "Navigation: " + url.spec();
-  log_trace_event_and_uma(top_level_trace_event_name, track1, timeline.start,
-                          timeline.finish, /*histogram_name=*/"Total");
+  TRACE_EVENT_BEGIN("navigation",
+                    perfetto::DynamicString(top_level_trace_event_name), track1,
+                    timeline.start);
+  TRACE_EVENT_END("navigation", track1, timeline.finish);
 
   if (!timeline.begin_navigation.is_null()) {
     // Most navigations (other than synchronous renderer commits) go through
@@ -9956,7 +9975,7 @@
 
     if (!shown_contents) {
       // These point to freed memory, so null them out to prevent inadvertent
-      // UAF in the feature (see NOTE above).
+      // UAF in the future (see NOTE above).
       new_frame_tree = nullptr;
       new_main_rfh = nullptr;
       new_rwh = nullptr;
@@ -9966,7 +9985,7 @@
           new_main_rfh->GetView()->GetViewBounds());
       reply->window_screen_rect.emplace(
           new_main_rfh->GetView()->GetBoundsInRootWindow());
-      reply->visual_properties = new_rwh->GetInitialVisualProperties();
+      reply->visual_properties = new_rwh->GetVisualProperties();
     }
   }
 
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 86a8079..70acc7b 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -740,6 +740,10 @@
                      const gfx::Size& min_size,
                      const gfx::Size& max_size);
 
+  // Generates a filled in VisualProperties struct representing the current
+  // properties of this widget.
+  blink::VisualProperties GetVisualProperties();
+
   // Returns the result of GetVisualProperties(), resetting and storing that
   // value as what has been sent to the renderer. This should be called when
   // getting VisualProperties that will be sent in order to create a
@@ -1134,10 +1138,6 @@
   void ResetStateForCreatedRenderWidget(
       const blink::VisualProperties& initial_props);
 
-  // Generates a filled in VisualProperties struct representing the current
-  // properties of this widget.
-  blink::VisualProperties GetVisualProperties();
-
   // Returns true if the |new_visual_properties| differs from
   // |old_page_visual_properties| in a way that indicates a size changed.
   static bool DidVisualPropertiesSizeChange(
diff --git a/content/browser/resources/gpu/info_view.ts b/content/browser/resources/gpu/info_view.ts
index 130a9ba..efb0bba 100644
--- a/content/browser/resources/gpu/info_view.ts
+++ b/content/browser/resources/gpu/info_view.ts
@@ -672,6 +672,7 @@
       'webgpu': 'WebGPU',
       'skia_graphite': 'Skia Graphite',
       'webnn': 'WebNN',
+      'trees_in_viz': 'TreesInViz',
     };
 
     const statusMap: Record<string, {label: string, class: string}> = {
diff --git a/content/browser/speech/network_speech_recognition_engine_impl.cc b/content/browser/speech/network_speech_recognition_engine_impl.cc
index 4328465..a138840 100644
--- a/content/browser/speech/network_speech_recognition_engine_impl.cc
+++ b/content/browser/speech/network_speech_recognition_engine_impl.cc
@@ -129,8 +129,7 @@
 
 void NetworkSpeechRecognitionEngineImpl::UpdateRecognitionContext(
     const media::SpeechRecognitionRecognitionContext& recognition_context) {
-  Abort(media::mojom::SpeechRecognitionErrorCode::
-            kRecognitionContextNotSupported);
+  Abort(media::mojom::SpeechRecognitionErrorCode::kPhrasesNotSupported);
 }
 
 void NetworkSpeechRecognitionEngineImpl::EndRecognition() {
diff --git a/content/browser/speech/speech_recognition_manager_impl.cc b/content/browser/speech/speech_recognition_manager_impl.cc
index 4b762dc..b1136a2 100644
--- a/content/browser/speech/speech_recognition_manager_impl.cc
+++ b/content/browser/speech/speech_recognition_manager_impl.cc
@@ -233,8 +233,7 @@
     // Set the error if on-device speech recognition is not used but recognition
     // context is set.
     if (config.recognition_context.has_value()) {
-      error = media::mojom::SpeechRecognitionErrorCode::
-          kRecognitionContextNotSupported;
+      error = media::mojom::SpeechRecognitionErrorCode::kPhrasesNotSupported;
     }
   }
 
diff --git a/content/browser/speech/speech_recognition_manager_impl_unittest.cc b/content/browser/speech/speech_recognition_manager_impl_unittest.cc
index 6181cf87..e42e6b5 100644
--- a/content/browser/speech/speech_recognition_manager_impl_unittest.cc
+++ b/content/browser/speech/speech_recognition_manager_impl_unittest.cc
@@ -189,7 +189,7 @@
   }));
 }
 
-TEST_F(SpeechRecognitionManagerImplTest, RecognitionContextNotSupportedError) {
+TEST_F(SpeechRecognitionManagerImplTest, PhrasesNotSupportedError) {
   SpeechRecognitionSessionConfig config;
   config.on_device = false;
   config.language = "en-US";
@@ -199,8 +199,8 @@
                           receiver_.BindNewPipeAndPassRemote(), std::nullopt);
 
   EXPECT_TRUE(base::test::RunUntil([&]() {
-    return error_ == media::mojom::SpeechRecognitionErrorCode::
-                         kRecognitionContextNotSupported &&
+    return error_ ==
+               media::mojom::SpeechRecognitionErrorCode::kPhrasesNotSupported &&
            ended_;
   }));
 }
@@ -214,10 +214,10 @@
 
   EXPECT_CALL(
       listener_,
-      OnRecognitionError(_, media::mojom::SpeechRecognitionError(
-                                media::mojom::SpeechRecognitionErrorCode::
-                                    kRecognitionContextNotSupported,
-                                media::mojom::SpeechAudioErrorDetails::kNone)));
+      OnRecognitionError(
+          _, media::mojom::SpeechRecognitionError(
+                 media::mojom::SpeechRecognitionErrorCode::kPhrasesNotSupported,
+                 media::mojom::SpeechAudioErrorDetails::kNone)));
   EXPECT_CALL(listener_, OnRecognitionEnd(_));
   manager_->CreateSession(config, mojo::NullReceiver(), mojo::NullRemote(),
                           std::nullopt);
diff --git a/content/browser/speech/speech_recognizer_impl_android.cc b/content/browser/speech/speech_recognizer_impl_android.cc
index cf4a6d2..38491cc 100644
--- a/content/browser/speech/speech_recognizer_impl_android.cc
+++ b/content/browser/speech/speech_recognizer_impl_android.cc
@@ -67,10 +67,10 @@
   }
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   listener()->OnRecognitionError(
-      session_id(), media::mojom::SpeechRecognitionError(
-                        media::mojom::SpeechRecognitionErrorCode::
-                            kRecognitionContextNotSupported,
-                        media::mojom::SpeechAudioErrorDetails::kNone));
+      session_id(),
+      media::mojom::SpeechRecognitionError(
+          media::mojom::SpeechRecognitionErrorCode::kPhrasesNotSupported,
+          media::mojom::SpeechAudioErrorDetails::kNone));
 }
 
 void SpeechRecognizerImplAndroid::StartRecognitionOnUIThread(
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index 1bd76e3..f8c1355 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -1423,27 +1423,6 @@
 
   // TODO(yigu): Clean up the client metadata related errors for metrics and
   // console logs.
-  if (!idp_info->metadata.brand_background_color &&
-      idp_info->metadata.brand_text_color) {
-    idp_info->metadata.brand_text_color = std::nullopt;
-    render_frame_host().AddMessageToConsole(
-        blink::mojom::ConsoleMessageLevel::kWarning,
-        "The FedCM text color is ignored because background color was not "
-        "provided");
-  }
-  if (idp_info->metadata.brand_background_color &&
-      idp_info->metadata.brand_text_color) {
-    float text_contrast_ratio = color_utils::GetContrastRatio(
-        *idp_info->metadata.brand_background_color,
-        *idp_info->metadata.brand_text_color);
-    if (text_contrast_ratio < color_utils::kMinimumReadableContrastRatio) {
-      idp_info->metadata.brand_text_color = std::nullopt;
-      render_frame_host().AddMessageToConsole(
-          blink::mojom::ConsoleMessageLevel::kWarning,
-          "The FedCM text color is ignored because it does not contrast enough "
-          "with the provided background color");
-    }
-  }
   FetchAccountPicturesAndBrandIcons(std::move(idp_info), std::move(accounts),
                                     std::move(client_metadata));
 }
@@ -2975,8 +2954,9 @@
 
   for (const auto& json : sd_jwt->disclosures) {
     data_decoder::DataDecoder::ParseJsonIsolated(
-        json, base::BindOnce(&FederatedAuthRequestImpl::OnDisclosureParsed,
-                             weak_ptr_factory_.GetWeakPtr(), callback, json));
+        json.value(),
+        base::BindOnce(&FederatedAuthRequestImpl::OnDisclosureParsed,
+                       weak_ptr_factory_.GetWeakPtr(), callback, json.value()));
   }
 }
 
@@ -2996,7 +2976,7 @@
     return;
   }
 
-  disclosures_.push_back({disclosure->name, json});
+  disclosures_.push_back({disclosure->name, sdjwt::JSONString(json)});
   cb.Run();
 }
 
diff --git a/content/browser/webid/federated_auth_request_impl.h b/content/browser/webid/federated_auth_request_impl.h
index 688067a..59b1139 100644
--- a/content/browser/webid/federated_auth_request_impl.h
+++ b/content/browser/webid/federated_auth_request_impl.h
@@ -692,7 +692,7 @@
 
   // A list of discloures that were parsed in the token response, when
   // the token's format is "vc+sd-jwt".
-  std::vector<std::pair<std::string, std::string>> disclosures_;
+  std::vector<std::pair<std::string, content::sdjwt::JSONString>> disclosures_;
 
   base::WeakPtrFactory<FederatedAuthRequestImpl> weak_ptr_factory_{this};
 };
diff --git a/content/browser/webid/federated_provider_fetcher.cc b/content/browser/webid/federated_provider_fetcher.cc
index 495cf08..5d260361 100644
--- a/content/browser/webid/federated_provider_fetcher.cc
+++ b/content/browser/webid/federated_provider_fetcher.cc
@@ -8,6 +8,8 @@
 #include "content/browser/webid/flags.h"
 #include "content/browser/webid/webid_utils.h"
 #include "net/base/schemeful_site.h"
+#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
+#include "ui/gfx/color_utils.h"
 
 namespace content {
 
@@ -200,6 +202,25 @@
     }
   }
 
+  if (!idp_metadata.brand_background_color && idp_metadata.brand_text_color) {
+    idp_metadata.brand_text_color = std::nullopt;
+    render_frame_host_->AddMessageToConsole(
+        blink::mojom::ConsoleMessageLevel::kWarning,
+        "The FedCM text color is ignored because background color was not "
+        "provided");
+  }
+  if (idp_metadata.brand_background_color && idp_metadata.brand_text_color) {
+    float text_contrast_ratio = color_utils::GetContrastRatio(
+        *idp_metadata.brand_background_color, *idp_metadata.brand_text_color);
+    if (text_contrast_ratio < color_utils::kMinimumReadableContrastRatio) {
+      idp_metadata.brand_text_color = std::nullopt;
+      render_frame_host_->AddMessageToConsole(
+          blink::mojom::ConsoleMessageLevel::kWarning,
+          "The FedCM text color is ignored because it does not contrast enough "
+          "with the provided background color");
+    }
+  }
+
   fetch_result.endpoints = endpoints;
   fetch_result.metadata = idp_metadata;
 
diff --git a/content/browser/webid/jwt_signer_unittest.cc b/content/browser/webid/jwt_signer_unittest.cc
index 84a66a80..e09bc19 100644
--- a/content/browser/webid/jwt_signer_unittest.cc
+++ b/content/browser/webid/jwt_signer_unittest.cc
@@ -111,24 +111,25 @@
   payload.sub = "goto@google.com";
 
   Jwt issued;
-  issued.header = *header.Serialize();
-  issued.payload = *payload.Serialize();
+  issued.header = JSONString(header.Serialize()->value());
+  issued.payload = JSONString(payload.Serialize()->value());
 
   auto success = issued.Sign(CreateJwtSigner(std::move(private_key)));
 
   EXPECT_TRUE(success);
 
   auto signature = base::Base64UrlDecode(
-      issued.signature, base::Base64UrlDecodePolicy::IGNORE_PADDING);
+      issued.signature.value(), base::Base64UrlDecodePolicy::IGNORE_PADDING);
 
   EXPECT_TRUE(signature);
 
   std::string header_base64;
-  base::Base64UrlEncode(
-      issued.header, base::Base64UrlEncodePolicy::OMIT_PADDING, &header_base64);
+  base::Base64UrlEncode(issued.header.value(),
+                        base::Base64UrlEncodePolicy::OMIT_PADDING,
+                        &header_base64);
 
   std::string payload_base64;
-  base::Base64UrlEncode(issued.payload,
+  base::Base64UrlEncode(issued.payload.value(),
                         base::Base64UrlEncodePolicy::OMIT_PADDING,
                         &payload_base64);
 
@@ -166,15 +167,15 @@
   EXPECT_TRUE(issuer_json->Serialize());
 
   Jwt issued;
-  issued.header = *header.Serialize();
-  issued.payload = *payload.Serialize();
+  issued.header = JSONString(header.Serialize()->value());
+  issued.payload = JSONString(payload.Serialize()->value());
   auto signer = CreateJwtSigner(std::move(issuer_private_key));
   auto success = issued.Sign(std::move(signer));
 
   EXPECT_TRUE(success);
 
-  auto presentation =
-      SdJwt::Disclose({{name.name, name.Serialize()}}, {"name"});
+  auto presentation = SdJwt::Disclose(
+      {{name.name, JSONString(name.Serialize().value())}}, {"name"});
   EXPECT_TRUE(presentation);
 
   SdJwt sd_jwt;
diff --git a/content/browser/webid/sd_jwt.cc b/content/browser/webid/sd_jwt.cc
index c0d02d1..a23ccd7b 100644
--- a/content/browser/webid/sd_jwt.cc
+++ b/content/browser/webid/sd_jwt.cc
@@ -32,11 +32,11 @@
   return str;
 }
 
-base64_t Base64UrlEncode(const std::string_view& str) {
-  base64_t base64;
+Base64String Base64UrlEncode(const std::string_view& str) {
+  std::string base64;
   base::Base64UrlEncode(str, base::Base64UrlEncodePolicy::OMIT_PADDING,
                         &base64);
-  return base64;
+  return Base64String(base64);
 }
 
 }  // namespace
@@ -82,7 +82,7 @@
   return result;
 }
 
-std::optional<json_t> Jwk::Serialize() const {
+std::optional<std::string> Jwk::Serialize() const {
   base::Value::Dict result;
 
   result.Set("kty", kty);
@@ -122,43 +122,49 @@
   }
 
   Disclosure result;
-  result.salt = list[0].GetString();
+  result.salt = Base64String(list[0].GetString());
   result.name = list[1].GetString();
   result.value = list[2].GetString();
   return result;
 }
 
-std::optional<json_t> Disclosure::ToJson() const {
+std::optional<JSONString> Disclosure::ToJson() const {
   base::Value::List list;
 
-  list.Append(salt);
+  list.Append(salt.value());
   list.Append(name);
   list.Append(value);
 
-  return base::WriteJson(list);
+  auto result = base::WriteJson(list);
+
+  if (!result) {
+    return std::nullopt;
+  }
+
+  return JSONString(*result);
 }
 
-base64_t Disclosure::Serialize() const {
-  return Base64UrlEncode(*ToJson());
+Base64String Disclosure::Serialize() const {
+  return Base64UrlEncode(ToJson()->value());
 }
 
-std::optional<base64_t> Disclosure::Digest(Hasher hasher) const {
-  auto disclosure_base64 = Serialize();
+std::optional<Base64String> Disclosure::Digest(Hasher hasher) const {
+  Base64String disclosure = Serialize();
   std::string result;
-  base::Base64UrlEncode(hasher.Run(disclosure_base64),
+  base::Base64UrlEncode(hasher.Run(disclosure.value()),
                         base::Base64UrlEncodePolicy::OMIT_PADDING, &result);
-  return result;
+  return Base64String(result);
 }
 
 // static
-base64_t Disclosure::CreateSalt() {
+Base64String Disclosure::CreateSalt() {
   const size_t salt_size = 32;
   std::array<uint8_t, salt_size> salt_bytes;
   crypto::RandBytes(salt_bytes);
-  base64_t salt_base64;
+  std::string salt;
   base::Base64UrlEncode(salt_bytes, base::Base64UrlEncodePolicy::OMIT_PADDING,
-                        &salt_base64);
-  return salt_base64;
+                        &salt);
+  return Base64String(salt);
 }
 
 std::optional<SdJwt> SdJwt::From(const base::Value::List& list) {
@@ -175,13 +181,13 @@
     return std::nullopt;
   }
 
-  std::vector<json_t> disclosures;
+  std::vector<JSONString> disclosures;
 
   for (auto& disclosure : list[1].GetList()) {
     if (!disclosure.is_string()) {
       return std::nullopt;
     }
-    disclosures.push_back(disclosure.GetString());
+    disclosures.push_back(JSONString(disclosure.GetString()));
   }
 
   SdJwt result;
@@ -263,21 +269,27 @@
   return result;
 }
 
-std::optional<json_t> Header::ToJson() const {
+std::optional<JSONString> Header::ToJson() const {
   base::Value::Dict header_dict;
 
   header_dict.Set("typ", typ);
   header_dict.Set("alg", alg);
 
-  return base::WriteJson(header_dict);
+  auto result = base::WriteJson(header_dict);
+
+  if (!result) {
+    return std::nullopt;
+  }
+
+  return JSONString(*result);
 }
 
-std::optional<base64_t> Header::Serialize() const {
+std::optional<Base64String> Header::Serialize() const {
   auto header_json = ToJson();
   if (!header_json) {
     return std::nullopt;
   }
-  return Base64UrlEncode(*header_json);
+  return Base64UrlEncode(header_json->value());
 }
 
 ConfirmationKey::ConfirmationKey() = default;
@@ -341,7 +353,7 @@
 
   auto* sd_hash = json.FindString("sd_hash");
   if (sd_hash) {
-    result.sd_hash = *sd_hash;
+    result.sd_hash = Base64String(*sd_hash);
   }
 
   auto* _sd_alg = json.FindString("_sd_alg");
@@ -354,14 +366,14 @@
       if (!el.is_string()) {
         return std::nullopt;
       }
-      result._sd.push_back(el.GetString());
+      result._sd.push_back(Base64String(el.GetString()));
     }
   }
 
   return result;
 }
 
-std::optional<json_t> Payload::ToJson() const {
+std::optional<JSONString> Payload::ToJson() const {
   base::Value::Dict payload_dict;
 
   if (!iss.empty()) {
@@ -406,14 +418,14 @@
     payload_dict.Set("exp", (int)exp->ToTimeT());
   }
 
-  if (!sd_hash.empty()) {
-    payload_dict.Set("sd_hash", sd_hash);
+  if (!sd_hash.value().empty()) {
+    payload_dict.Set("sd_hash", sd_hash.value());
   }
 
   if (_sd.size() > 0) {
     base::Value::List list;
     for (auto disclosure : _sd) {
-      list.Append(disclosure);
+      list.Append(disclosure.value());
     }
     payload_dict.Set("_sd", std::move(list));
   }
@@ -422,29 +434,35 @@
     payload_dict.Set("_sd_alg", _sd_alg);
   }
 
-  return base::WriteJson(payload_dict);
+  auto result = base::WriteJson(payload_dict);
+
+  if (!result) {
+    return std::nullopt;
+  }
+
+  return JSONString(*result);
 }
 
-std::optional<base64_t> Payload::Serialize() const {
+std::optional<Base64String> Payload::Serialize() const {
   auto payload_json = ToJson();
   if (!payload_json) {
     return std::nullopt;
   }
-  return Base64UrlEncode(*payload_json);
+  return Base64UrlEncode(payload_json->value());
 }
 
 Jwt::Jwt() = default;
 Jwt::~Jwt() = default;
 Jwt::Jwt(const Jwt& other) = default;
 
-std::string Jwt::Serialize() const {
+JSONString Jwt::Serialize() const {
   std::string result;
-  result += Base64UrlEncode(header);
+  result += Base64UrlEncode(header.value()).value();
   result += ".";
-  result += Base64UrlEncode(payload);
+  result += Base64UrlEncode(payload.value()).value();
   result += ".";
-  result += signature;
-  return result;
+  result += signature.value();
+  return JSONString(result);
 }
 
 // static
@@ -458,9 +476,9 @@
   }
 
   Jwt result;
-  result.header = list[0].GetString();
-  result.payload = list[1].GetString();
-  result.signature = list[2].GetString();
+  result.header = JSONString(list[0].GetString());
+  result.payload = JSONString(list[1].GetString());
+  result.signature = Base64String(list[2].GetString());
 
   return result;
 }
@@ -498,8 +516,8 @@
 }
 
 bool Jwt::Sign(Signer signer) {
-  std::string message =
-      Base64UrlEncode(header) + "." + Base64UrlEncode(payload);
+  std::string message = Base64UrlEncode(header.value()).value() + "." +
+                        Base64UrlEncode(payload.value()).value();
 
   auto sig = std::move(signer).Run(message);
   if (!sig) {
@@ -507,7 +525,7 @@
   }
 
   base::Base64UrlEncode(*sig, base::Base64UrlEncodePolicy::OMIT_PADDING,
-                        &signature);
+                        &signature.value());
 
   return true;
 }
@@ -518,12 +536,12 @@
 
 std::string SdJwt::Serialize() const {
   std::string result;
-  result += jwt.Serialize();
+  result += jwt.Serialize().value();
 
   result += "~";
 
-  for (const json_t& disclosure : disclosures) {
-    result += Base64UrlEncode(disclosure);
+  for (const JSONString& disclosure : disclosures) {
+    result += Base64UrlEncode(disclosure.value()).value();
     result += "~";
   }
 
@@ -533,7 +551,7 @@
 std::string SdJwtKb::Serialize() const {
   std::string result;
   result += sd_jwt.Serialize();
-  result += kb_jwt.Serialize();
+  result += kb_jwt.Serialize().value();
 
   return result;
 }
@@ -543,18 +561,18 @@
 SdJwtKb::SdJwtKb(const SdJwtKb& other) = default;
 
 // static
-std::optional<std::vector<json_t>> SdJwt::Disclose(
-    const std::vector<std::pair<std::string, json_t>>& disclosures,
+std::optional<std::vector<JSONString>> SdJwt::Disclose(
+    const std::vector<std::pair<std::string, JSONString>>& disclosures,
     const std::vector<std::string>& selector) {
   // Implements the selective disclosure:
   // https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-13.html#name-disclosing-to-a-verifier
 
-  std::map<std::string, json_t> disclosures_by_name;
-  for (const std::pair<std::string, json_t>& disclosure : disclosures) {
+  std::map<std::string, JSONString> disclosures_by_name;
+  for (const std::pair<std::string, JSONString>& disclosure : disclosures) {
     disclosures_by_name[disclosure.first] = disclosure.second;
   }
 
-  std::vector<json_t> result;
+  std::vector<JSONString> result;
   for (const std::string& name : selector) {
     if (disclosures_by_name.count(name)) {
       result.push_back(disclosures_by_name[name]);
@@ -587,7 +605,7 @@
   payload.aud = aud;
   payload.nonce = nonce;
   payload.iat = iat;
-  payload.sd_hash = hash;
+  payload.sd_hash = Base64String(hash);
 
   Jwt kb_jwt;
   auto header_json = header.ToJson();
diff --git a/content/browser/webid/sd_jwt.h b/content/browser/webid/sd_jwt.h
index 47ee34c..74f978a 100644
--- a/content/browser/webid/sd_jwt.h
+++ b/content/browser/webid/sd_jwt.h
@@ -12,6 +12,7 @@
 #include "base/functional/callback.h"
 #include "base/memory/raw_ref.h"
 #include "base/time/time.h"
+#include "base/types/strong_alias.h"
 #include "base/values.h"
 #include "content/common/content_export.h"
 
@@ -51,8 +52,8 @@
   original value. Reverting back to the original value is
   important because they are signed and need to be verified.
 
-  So, a Jwt gets parsed into a json_t header and json_t payload,
-  which represent a string containing JSON, and a base64_t
+  So, a Jwt gets parsed into a JSON header and JSON payload,
+  which represent a string containing JSON, and a Base64
   signature, which represents a string containing a base64url
   encoded blob.
 
@@ -72,7 +73,7 @@
 
   An SdJwt gets parsed into the issued Jwt as well as into the list
   of selective disclosures. Like a Jwt, the selective disclosures
-  are represented as json_t so that they can be serialized back to
+  are represented as JSON so that they can be serialized back to
   their original values.
 
   SdJwt sd_jwt = SdJwt::From(SdJwt::Parse(encoding))
@@ -112,9 +113,9 @@
 };
 
 // A string that can be parsed as application/json.
-typedef std::string json_t;
+using JSONString = base::StrongAlias<class JSONStringTag, std::string>;
 // A string that is base64url encoded.
-typedef std::string base64_t;
+using Base64String = base::StrongAlias<class Base64StringTag, std::string>;
 
 // https://datatracker.ietf.org/doc/html/rfc7519#section-5
 struct CONTENT_EXPORT Header {
@@ -128,8 +129,8 @@
 
   static std::optional<Header> From(const base::Value::Dict& json);
 
-  std::optional<json_t> ToJson() const;
-  std::optional<base64_t> Serialize() const;
+  std::optional<JSONString> ToJson() const;
+  std::optional<Base64String> Serialize() const;
 };
 
 // This struct holds the JWK in the "cnf" [1] parameter in the
@@ -170,11 +171,11 @@
   std::string vct;
 
   // Used in the Issued JWT
-  std::vector<base64_t> _sd;
+  std::vector<Base64String> _sd;
   std::string _sd_alg;
 
   // Used in the Key Binding JWT
-  base64_t sd_hash;
+  Base64String sd_hash;
 
   Payload();
   ~Payload();
@@ -182,8 +183,8 @@
 
   static std::optional<Payload> From(const base::Value::Dict& json);
 
-  std::optional<json_t> ToJson() const;
-  std::optional<base64_t> Serialize() const;
+  std::optional<JSONString> ToJson() const;
+  std::optional<Base64String> Serialize() const;
 };
 
 /**
@@ -197,9 +198,9 @@
 
 // https://datatracker.ietf.org/doc/html/rfc7519
 struct CONTENT_EXPORT Jwt {
-  json_t header;
-  json_t payload;
-  base64_t signature;
+  JSONString header;
+  JSONString payload;
+  Base64String signature;
 
   Jwt();
   ~Jwt();
@@ -209,7 +210,7 @@
 
   static std::optional<Jwt> From(const base::Value::List& json);
   static std::optional<base::Value::List> Parse(const std::string_view& jwt);
-  json_t Serialize() const;
+  JSONString Serialize() const;
 };
 
 /**
@@ -233,7 +234,7 @@
  * can create disclosures as issuers.
  */
 struct CONTENT_EXPORT Disclosure {
-  base64_t salt;
+  Base64String salt;
   std::string name;
   std::string value;
 
@@ -245,17 +246,17 @@
 
   // Creates a random value with the following requirements:
   // https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-13.html#name-entropy-of-the-salt
-  static base64_t CreateSalt();
+  static Base64String CreateSalt();
 
-  base64_t Serialize() const;
-  std::optional<json_t> ToJson() const;
-  std::optional<base64_t> Digest(Hasher hasher) const;
+  Base64String Serialize() const;
+  std::optional<JSONString> ToJson() const;
+  std::optional<Base64String> Digest(Hasher hasher) const;
 };
 
 // https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-13.html
 struct CONTENT_EXPORT SdJwt {
   Jwt jwt;
-  std::vector<json_t> disclosures;
+  std::vector<JSONString> disclosures;
 
   SdJwt();
   ~SdJwt();
@@ -264,8 +265,8 @@
   static std::optional<SdJwt> From(const base::Value::List& json);
   static std::optional<base::Value::List> Parse(const std::string_view& sdjwt);
 
-  static std::optional<std::vector<json_t>> Disclose(
-      const std::vector<std::pair<std::string, json_t>>& disclosures,
+  static std::optional<std::vector<JSONString>> Disclose(
+      const std::vector<std::pair<std::string, JSONString>>& disclosures,
       const std::vector<std::string>& selector);
 
   std::string Serialize() const;
diff --git a/content/browser/webid/sd_jwt_unittest.cc b/content/browser/webid/sd_jwt_unittest.cc
index d34f4c51..439318d 100644
--- a/content/browser/webid/sd_jwt_unittest.cc
+++ b/content/browser/webid/sd_jwt_unittest.cc
@@ -81,7 +81,7 @@
   auto disclosure = Disclosure::From(base::JSONReader::Read(json)->GetList());
   EXPECT_TRUE(disclosure);
 
-  EXPECT_EQ(disclosure->salt, "_26bc4LT-ac6q2KI6cBW5es");
+  EXPECT_EQ(disclosure->salt, Base64String("_26bc4LT-ac6q2KI6cBW5es"));
   EXPECT_EQ(disclosure->name, "family_name");
   EXPECT_EQ(disclosure->value, "Möbius");
 }
@@ -91,11 +91,11 @@
   // https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-13.html#section-4.2.1
 
   Disclosure disclosure;
-  disclosure.salt = "_26bc4LT-ac6q2KI6cBW5es";
+  disclosure.salt = Base64String("_26bc4LT-ac6q2KI6cBW5es");
   disclosure.name = "family_name";
   disclosure.value = "Möbius";
 
-  auto base64 = disclosure.Serialize();
+  Base64String base64 = disclosure.Serialize();
 
   // This value is different from what's in the spec, but that's because
   // our JSON serialization strips whitespaces between array elements
@@ -108,7 +108,7 @@
   std::string expected =
       "WyJfMjZiYzRMVC1hYzZxMktJNmNCVzVlcyIsImZhbWlseV9uYW1lIiwiTcO2Yml1cyJd";
 
-  EXPECT_STREQ(base64.c_str(), expected.c_str());
+  EXPECT_STREQ(base64->c_str(), expected.c_str());
 }
 
 TEST_F(SdJwtTest, JwtParsing) {
@@ -119,9 +119,9 @@
   auto token = Jwt::From(*Jwt::Parse(jwt));
   EXPECT_TRUE(token);
 
-  EXPECT_EQ(token->header, "header");
-  EXPECT_EQ(token->payload, "payload");
-  EXPECT_EQ(token->signature, "signature");
+  EXPECT_EQ(token->header, JSONString("header"));
+  EXPECT_EQ(token->payload, JSONString("payload"));
+  EXPECT_EQ(token->signature, Base64String("signature"));
 }
 
 TEST_F(SdJwtTest, JwtParsingInvalid) {
@@ -141,13 +141,13 @@
 
 TEST_F(SdJwtTest, JwtSerializing) {
   Jwt token;
-  token.header = "header";
-  token.payload = "payload";
-  token.signature = "signature";
+  token.header = JSONString("header");
+  token.payload = JSONString("payload");
+  token.signature = Base64String("signature");
 
   // Jwt's top level structure:
   // Base64UrlEncode(header) . Base64UrlEncode(payload)  . signature
-  EXPECT_EQ(token.Serialize(), "aGVhZGVy.cGF5bG9hZA.signature");
+  EXPECT_EQ(token.Serialize(), JSONString("aGVhZGVy.cGF5bG9hZA.signature"));
 }
 
 TEST_F(SdJwtTest, HeaderParsingAndSerializing) {
@@ -155,9 +155,10 @@
   header.alg = "foo";
   header.typ = "bar";
 
-  EXPECT_EQ(header.ToJson(), R"({"alg":"foo","typ":"bar"})");
+  EXPECT_EQ(header.ToJson(), JSONString(R"({"alg":"foo","typ":"bar"})"));
   // The serializion of the header is a base64 encoding of the JSON.
-  EXPECT_EQ(header.Serialize(), "eyJhbGciOiJmb28iLCJ0eXAiOiJiYXIifQ");
+  EXPECT_EQ(header.Serialize(),
+            Base64String("eyJhbGciOiJmb28iLCJ0eXAiOiJiYXIifQ"));
 
   // Test that we can go back from base64 to value.
   auto parsed =
@@ -171,9 +172,9 @@
   Payload payload;
   payload.sub = "foo";
 
-  EXPECT_EQ(payload.ToJson(), R"({"sub":"foo"})");
+  EXPECT_EQ(payload.ToJson(), JSONString(R"({"sub":"foo"})"));
   // The serializion of the header is a base64 encoding of the JSON.
-  EXPECT_EQ(payload.Serialize(), "eyJzdWIiOiJmb28ifQ");
+  EXPECT_EQ(payload.Serialize(), Base64String("eyJzdWIiOiJmb28ifQ"));
 
   // Test that we can go back from base64 to value.
   auto parsed = Payload::From(*base::JSONReader::ReadDict(R"({"sub":"foo"})"));
@@ -207,10 +208,11 @@
   auto token = Jwt::From(*Jwt::Parse(jwt));
   EXPECT_TRUE(token);
 
-  EXPECT_EQ(token->header, R"({"alg": "ES256", "typ": "example+sd-jwt"})");
+  EXPECT_EQ(token->header,
+            JSONString(R"({"alg": "ES256", "typ": "example+sd-jwt"})"));
   EXPECT_EQ(token->signature,
-            "oQ0UNJB1E1agYouB1yfGXfYLyWueHhfMFuicSV-n_"
-            "GLXHtX0XK99sfDDERiWKukCUzadGTT4QbCwXe6JvVmZWw");
+            Base64String("oQ0UNJB1E1agYouB1yfGXfYLyWueHhfMFuicSV-n_"
+                         "GLXHtX0XK99sfDDERiWKukCUzadGTT4QbCwXe6JvVmZWw"));
 
   std::string expected =
       R"({)"
@@ -237,15 +239,17 @@
       R"("y": "ZxjiWWbZMQGHVWKVQ4hbSIirsVfuecCE6t4jT9F2HZQ"}})"
       R"(})";
 
-  EXPECT_STREQ(token->payload.c_str(), expected.c_str());
+  EXPECT_STREQ(token->payload.value().c_str(), expected.c_str());
 
-  auto header = Header::From(*base::JSONReader::ReadDict(token->header));
+  auto header =
+      Header::From(*base::JSONReader::ReadDict(token->header.value()));
   EXPECT_TRUE(header);
 
   EXPECT_EQ(header->typ, "example+sd-jwt");
   EXPECT_EQ(header->alg, "ES256");
 
-  auto payload = Payload::From(*base::JSONReader::ReadDict(token->payload));
+  auto payload =
+      Payload::From(*base::JSONReader::ReadDict(token->payload.value()));
   EXPECT_TRUE(payload);
 
   EXPECT_EQ(payload->iss, "https://issuer.example.com");
@@ -269,15 +273,15 @@
   auto token = SdJwt::From(*SdJwt::Parse(jwt));
   EXPECT_TRUE(token);
 
-  EXPECT_EQ(token->jwt.header, "header");
-  EXPECT_EQ(token->jwt.payload, "payload");
-  EXPECT_EQ(token->jwt.signature, "signature");
+  EXPECT_EQ(token->jwt.header, JSONString("header"));
+  EXPECT_EQ(token->jwt.payload, JSONString("payload"));
+  EXPECT_EQ(token->jwt.signature, Base64String("signature"));
   EXPECT_EQ(token->disclosures.size(), 2ul);
-  EXPECT_EQ(token->disclosures[0], "disclosure1");
-  EXPECT_EQ(token->disclosures[1], "disclosure2");
+  EXPECT_EQ(token->disclosures[0], JSONString("disclosure1"));
+  EXPECT_EQ(token->disclosures[1], JSONString("disclosure2"));
 
   // Asserts that we can serialize the token again.
-  token->jwt.header = "new-header";
+  token->jwt.header = JSONString("new-header");
   EXPECT_EQ(
       token->Serialize(),
       "bmV3LWhlYWRlcg.cGF5bG9hZA.signature~ZGlzY2xvc3VyZTE~ZGlzY2xvc3VyZTI~");
@@ -316,12 +320,12 @@
 
 TEST_F(SdJwtTest, SelectiveDisclosure) {
   Disclosure name;
-  name.salt = "fake-salt1";
+  name.salt = Base64String("fake-salt1");
   name.name = "name";
   name.value = "Sam";
 
   Disclosure email;
-  email.salt = "fake-salt2";
+  email.salt = Base64String("fake-salt2");
   email.name = "email";
   email.value = "goto@email.com";
 
@@ -339,7 +343,8 @@
   EXPECT_EQ(presentation->size(), 1ul);
 
   // ... and that it was selected correctly:
-  EXPECT_EQ((*presentation)[0], "[\"fake-salt1\",\"name\",\"Sam\"]");
+  EXPECT_EQ((*presentation)[0],
+            JSONString("[\"fake-salt1\",\"name\",\"Sam\"]"));
 }
 
 TEST_F(SdJwtTest, SdJwtKbParsingAndSerializing) {
@@ -356,18 +361,18 @@
   auto token = SdJwtKb::Parse(bin);
   EXPECT_TRUE(token);
 
-  EXPECT_EQ(token->sd_jwt.jwt.header, "header");
-  EXPECT_EQ(token->sd_jwt.jwt.payload, "payload");
-  EXPECT_EQ(token->sd_jwt.jwt.signature, "iss_signature");
+  EXPECT_EQ(token->sd_jwt.jwt.header, JSONString("header"));
+  EXPECT_EQ(token->sd_jwt.jwt.payload, JSONString("payload"));
+  EXPECT_EQ(token->sd_jwt.jwt.signature, Base64String("iss_signature"));
   EXPECT_EQ(token->sd_jwt.disclosures.size(), 2ul);
-  EXPECT_EQ(token->sd_jwt.disclosures[0], "disclosure1");
-  EXPECT_EQ(token->sd_jwt.disclosures[1], "disclosure2");
-  EXPECT_EQ(token->kb_jwt.header, "header");
-  EXPECT_EQ(token->kb_jwt.payload, "payload");
-  EXPECT_EQ(token->kb_jwt.signature, "kb_signature");
+  EXPECT_EQ(token->sd_jwt.disclosures[0], JSONString("disclosure1"));
+  EXPECT_EQ(token->sd_jwt.disclosures[1], JSONString("disclosure2"));
+  EXPECT_EQ(token->kb_jwt.header, JSONString("header"));
+  EXPECT_EQ(token->kb_jwt.payload, JSONString("payload"));
+  EXPECT_EQ(token->kb_jwt.signature, Base64String("kb_signature"));
 
   // Asserts that we can serialize the token again.
-  token->sd_jwt.jwt.header = "new-header";
+  token->sd_jwt.jwt.header = JSONString("new-header");
   EXPECT_EQ(
       token->Serialize(),
       "bmV3LWhlYWRlcg.cGF5bG9hZA.iss_signature~ZGlzY2xvc3VyZTE~ZGlzY2xvc3VyZTI~"
@@ -419,12 +424,12 @@
 
 TEST_F(SdJwtTest, SdJwtKb_Bind) {
   Disclosure name;
-  name.salt = "fake-salt1";
+  name.salt = Base64String("fake-salt1");
   name.name = "name";
   name.value = "Sam";
 
   Disclosure email;
-  email.salt = "fake-salt2";
+  email.salt = Base64String("fake-salt2");
   email.name = "email";
   email.value = "goto@email.com";
 
@@ -440,9 +445,9 @@
   EXPECT_TRUE(disclosures);
 
   Jwt issued;
-  issued.header = "header";
-  issued.payload = "payload";
-  issued.signature = "signature";
+  issued.header = JSONString("header");
+  issued.payload = JSONString("payload");
+  issued.signature = Base64String("signature");
 
   SdJwt presentation;
   presentation.jwt = issued;
@@ -459,13 +464,13 @@
 
   // Checks KB headers:
   // https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-13.html#section-4.3
-  auto header = Header::From(*base::JSONReader::ReadDict(kb.header));
+  auto header = Header::From(*base::JSONReader::ReadDict(kb.header.value()));
   EXPECT_TRUE(header);
   // typ MUST be "kb+jwt".
   EXPECT_EQ(header->typ, "kb+jwt");
   EXPECT_EQ(header->alg, "ES256");
 
-  auto payload = Payload::From(*base::JSONReader::ReadDict(kb.payload));
+  auto payload = Payload::From(*base::JSONReader::ReadDict(kb.payload.value()));
   EXPECT_TRUE(payload);
   // aud is required.
   EXPECT_EQ(payload->aud, "https://verifier.example");
@@ -478,9 +483,11 @@
 
   // Checks for how the hash was constructed:
   // https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-13.html#section-4.3.1
-  EXPECT_EQ(payload->sd_hash,
-            "U2hhMjU2KGFHVmhaR1Z5LmNHRjViRzloWkEuc2lnbmF0dXJlfld5Sm1ZV3RsTFh"
-            "OaGJIUXhJaXdpYm1GdFpTSXNJbE5oYlNKZH4p");
+  EXPECT_EQ(
+      payload->sd_hash,
+      Base64String(
+          "U2hhMjU2KGFHVmhaR1Z5LmNHRjViRzloWkEuc2lnbmF0dXJlfld5Sm1ZV3RsTFh"
+          "OaGJIUXhJaXdpYm1GdFpTSXNJbE5oYlNKZH4p"));
 
   // Checks that the signature was constructed correctly too:
   // https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-13.html#section-4.3.1
@@ -503,7 +510,7 @@
   //           Base64(["fake-salt1","name","Sam"])~)"
   //  }
   //
-  base64_t base64;
+  std::string base64;
   base::Base64UrlEncode(
       "Signed(eyJhbGciOiJFUzI1NiIsInR5cCI6ImtiK2p3dCJ9."
       "eyJhdWQiOiJodHRwczovL3ZlcmlmaWVyLmV4YW1wbGUiLCJpYXQiOjEyMzQsIm5"
@@ -512,7 +519,7 @@
       "EZoT2FHSklVWGhKYVhkcFltMUdkRnBUU1hOSmJFNW9ZbE5LWkg0cCJ9)",
       base::Base64UrlEncodePolicy::OMIT_PADDING, &base64);
 
-  EXPECT_STREQ(kb.signature.c_str(), base64.c_str());
+  EXPECT_STREQ(kb.signature->c_str(), base64.c_str());
 }
 
 }  // namespace content::sdjwt
diff --git a/content/browser/xr/service/xr_runtime_manager_impl.cc b/content/browser/xr/service/xr_runtime_manager_impl.cc
index 8a185c9..e8679f1 100644
--- a/content/browser/xr/service/xr_runtime_manager_impl.cc
+++ b/content/browser/xr/service/xr_runtime_manager_impl.cc
@@ -59,19 +59,6 @@
   return *xr_runtime_manager_observers;
 }
 
-#if !BUILDFLAG(IS_ANDROID)
-bool IsEnabled(const base::CommandLine* command_line,
-               const base::Feature& feature,
-               const std::string& name) {
-  if (!command_line->HasSwitch(switches::kWebXrForceRuntime))
-    return base::FeatureList::IsEnabled(feature);
-
-  return (base::CompareCaseInsensitiveASCII(
-              command_line->GetSwitchValueASCII(switches::kWebXrForceRuntime),
-              name) == 0);
-}
-#endif
-
 bool IsForcedRuntime(const base::CommandLine* command_line,
                      const std::string& name) {
   return (base::CompareCaseInsensitiveASCII(
@@ -188,14 +175,20 @@
   providers.push_back(std::make_unique<IsolatedVRDeviceProvider>());
 #endif  // !BUILDFLAG(IS_ANDROID)
 
-  bool orientation_provider_enabled = true;
+  const bool is_orientation_provider_forced =
+      IsForcedRuntime(base::CommandLine::ForCurrentProcess(),
+                      switches::kWebXrRuntimeOrientationSensors);
 
-#if !BUILDFLAG(IS_ANDROID)
-  const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
-  orientation_provider_enabled =
-      IsEnabled(cmd_line, device::features::kWebXrOrientationSensorDevice,
-                ::switches::kWebXrRuntimeOrientationSensors);
-#endif
+  // We can use the orientation provider if it's forced, or if the feature is
+  // enabled and 2D chrome is not being rendered in a head-mounted display.
+  // On such displays inline sessions can cause "swimmy" behavior, because the
+  // content would move in response to the user's head motion, but in unexpected
+  // ways.
+  bool orientation_provider_enabled =
+      is_orientation_provider_forced ||
+      (base::FeatureList::IsEnabled(
+           device::features::kWebXrOrientationSensorDevice) &&
+       !device::features::IsXrDevice());
 
   if (orientation_provider_enabled) {
     mojo::PendingRemote<device::mojom::SensorProvider> sensor_provider;
diff --git a/content/child/child_performance_coordinator_unittest.cc b/content/child/child_performance_coordinator_unittest.cc
index 36e1bea..ba1e3da 100644
--- a/content/child/child_performance_coordinator_unittest.cc
+++ b/content/child/child_performance_coordinator_unittest.cc
@@ -4,17 +4,15 @@
 
 #include "content/child/child_performance_coordinator.h"
 
-#include <optional>
+#include <memory>
 #include <utility>
 
 #include "base/functional/callback.h"
 #include "base/memory/read_only_shared_memory_region.h"
-#include "base/memory/scoped_refptr.h"
-#include "base/memory/structured_shared_memory.h"
 #include "base/task/sequenced_task_runner.h"
 #include "base/test/task_environment.h"
 #include "components/performance_manager/public/mojom/coordination_unit.mojom.h"
-#include "components/performance_manager/scenario_api/performance_scenario_memory.h"
+#include "components/performance_manager/scenario_api/performance_scenario_test_support.h"
 #include "components/performance_manager/scenario_api/performance_scenarios.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
@@ -25,8 +23,8 @@
 namespace {
 
 using performance_manager::mojom::ChildProcessCoordinationUnit;
+using performance_scenarios::PerformanceScenarioTestHelper;
 using performance_scenarios::ScenarioScope;
-using performance_scenarios::ScenarioState;
 using ::testing::_;
 using ::testing::Invoke;
 
@@ -53,52 +51,47 @@
 
 class ChildPerformanceCoordinatorTest : public ::testing::Test {
  public:
-  // Initializes `coordinator` and waits for a mock ChildProcessCoordinationUnit
-  // to send it `global_region` and `process_region`.
+  void SetUp() override {
+    scenario_test_helper_ =
+        PerformanceScenarioTestHelper::CreateWithoutMapping();
+    ASSERT_TRUE(scenario_test_helper_);
+  }
+
+  // Initializes the ChildPerformanceCoordinator and waits for a mock
+  // ChildProcessCoordinationUnit to send it `global_region` and
+  // `process_region`.
   void InitializeAndWaitForScenarioRegions(
-      ChildPerformanceCoordinator& coordinator,
       base::ReadOnlySharedMemoryRegion global_region,
       base::ReadOnlySharedMemoryRegion process_region) {
-    global_region_ = std::move(global_region);
-    process_region_ = std::move(process_region);
-    quit_closure_ = task_env_.QuitClosure();
-
+    base::OnceClosure quit_closure = task_env_.QuitClosure();
     StrictMockChildProcessCoordinationUnit mock_coordination_unit;
     EXPECT_CALL(mock_coordination_unit,
                 InitializeChildProcessCoordination(_, _))
         .WillOnce(Invoke(
-            this,
-            &ChildPerformanceCoordinatorTest::SendScenarioRegionsAndQuit));
-    mock_coordination_unit.Bind(coordinator.InitializeAndPassReceiver());
+            [&](uint64_t, InitializeChildProcessCoordinationCallback callback) {
+              std::move(callback).Run(std::move(global_region),
+                                      std::move(process_region));
+              // `callback` will post to ChildPerformanceCoordinator. Quit the
+              // runloop after the posted task.
+              task_env_.GetMainThreadTaskRunner()->PostTask(
+                  FROM_HERE, std::move(quit_closure));
+            }));
+    mock_coordination_unit.Bind(coordinator_.InitializeAndPassReceiver());
     task_env_.RunUntilQuit();
   }
 
-  // Invokes `callback` with the `global_region_` and `process_region_` and
-  // quits the run loop.
-  void SendScenarioRegionsAndQuit(
-      uint64_t,
-      InitializeChildProcessCoordinationCallback callback) {
-    std::move(callback).Run(std::move(global_region_),
-                            std::move(process_region_));
-    // `callback` will post to ChildPerformanceCoordinator. Quit the runloop
-    // after the posted task.
-    task_env_.GetMainThreadTaskRunner()->PostTask(FROM_HERE,
-                                                  std::move(quit_closure_));
+  PerformanceScenarioTestHelper& scenario_test_helper() {
+    return *scenario_test_helper_;
   }
 
  private:
   base::test::TaskEnvironment task_env_;
-
-  // State used by SendScenarioRegionsAndQuit.
-  base::ReadOnlySharedMemoryRegion global_region_;
-  base::ReadOnlySharedMemoryRegion process_region_;
-  base::OnceClosure quit_closure_;
+  std::unique_ptr<PerformanceScenarioTestHelper> scenario_test_helper_;
+  ChildPerformanceCoordinator coordinator_;
 };
 
 TEST_F(ChildPerformanceCoordinatorTest, NoScenarioRegion) {
-  ChildPerformanceCoordinator coordinator;
-  InitializeAndWaitForScenarioRegions(coordinator,
-                                      base::ReadOnlySharedMemoryRegion(),
+  InitializeAndWaitForScenarioRegions(base::ReadOnlySharedMemoryRegion(),
                                       base::ReadOnlySharedMemoryRegion());
 
   EXPECT_FALSE(performance_scenarios::GetScenarioMappingForScope(
@@ -108,13 +101,9 @@
 }
 
 TEST_F(ChildPerformanceCoordinatorTest, GlobalScenarioRegion) {
-  auto shared_memory = base::StructuredSharedMemory<ScenarioState>::Create();
-  ASSERT_TRUE(shared_memory.has_value());
-
-  ChildPerformanceCoordinator coordinator;
-  InitializeAndWaitForScenarioRegions(coordinator,
-                                      shared_memory->TakeReadOnlyRegion(),
-                                      base::ReadOnlySharedMemoryRegion());
+  InitializeAndWaitForScenarioRegions(
+      scenario_test_helper().GetReadOnlyScenarioRegion(ScenarioScope::kGlobal),
+      base::ReadOnlySharedMemoryRegion());
 
   EXPECT_TRUE(performance_scenarios::GetScenarioMappingForScope(
       ScenarioScope::kGlobal));
@@ -123,13 +112,10 @@
 }
 
 TEST_F(ChildPerformanceCoordinatorTest, ProcessScenarioRegion) {
-  auto shared_memory = base::StructuredSharedMemory<ScenarioState>::Create();
-  ASSERT_TRUE(shared_memory.has_value());
-
-  ChildPerformanceCoordinator coordinator;
-  InitializeAndWaitForScenarioRegions(coordinator,
-                                      base::ReadOnlySharedMemoryRegion(),
-                                      shared_memory->TakeReadOnlyRegion());
+  InitializeAndWaitForScenarioRegions(
+      base::ReadOnlySharedMemoryRegion(),
+      scenario_test_helper().GetReadOnlyScenarioRegion(
+          ScenarioScope::kCurrentProcess));
 
   EXPECT_FALSE(performance_scenarios::GetScenarioMappingForScope(
       ScenarioScope::kGlobal));
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 55e7a18f..6874fe39 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -367,8 +367,6 @@
            kSetOnlyIfOverridden},
           {"FledgeBiddingAndAuctionServerAPI",
            raw_ref(blink::features::kFledgeBiddingAndAuctionServer), kDefault},
-          {"FontationsFontBackend",
-           raw_ref(blink::features::kFontationsFontBackend)},
           {"FontSrcLocalMatching", raw_ref(features::kFontSrcLocalMatching)},
           {"MachineLearningNeuralNetwork",
            raw_ref(webnn::mojom::features::kWebMachineLearningNeuralNetwork),
diff --git a/content/public/android/BUILD.gn b/content/public/android/BUILD.gn
index 11c916e4..f7a261f 100644
--- a/content/public/android/BUILD.gn
+++ b/content/public/android/BUILD.gn
@@ -177,6 +177,7 @@
     "//content/public/common:common_java",
     "//device/bluetooth:java",
     "//device/gamepad:java",
+    "//device/vr/public:java",
     "//media/base/android:media_java",
     "//media/capture/video/android:capture_java",
     "//media/midi:midi_java",
diff --git a/content/public/browser/navigation_throttle.cc b/content/public/browser/navigation_throttle.cc
index 75cd13f..f93753a 100644
--- a/content/public/browser/navigation_throttle.cc
+++ b/content/public/browser/navigation_throttle.cc
@@ -6,6 +6,7 @@
 
 #include <utility>
 
+#include "base/check_deref.h"
 #include "content/browser/renderer_host/navigation_request.h"
 
 namespace content {
@@ -59,7 +60,12 @@
 NavigationThrottle::ThrottleCheckResult::~ThrottleCheckResult() {}
 
 NavigationThrottle::NavigationThrottle(NavigationHandle* navigation_handle)
-    : navigation_handle_(navigation_handle) {}
+    : navigation_handle_(navigation_handle) {
+  CHECK(navigation_handle_);
+}
+
+NavigationThrottle::NavigationThrottle(NavigationThrottleRegistry& registry)
+    : navigation_handle_(&registry.GetNavigationHandle()) {}
 
 NavigationThrottle::~NavigationThrottle() {}
 
diff --git a/content/public/browser/navigation_throttle.h b/content/public/browser/navigation_throttle.h
index 8d32662..79becb63 100644
--- a/content/public/browser/navigation_throttle.h
+++ b/content/public/browser/navigation_throttle.h
@@ -9,8 +9,10 @@
 
 #include "base/functional/callback.h"
 #include "base/memory/raw_ptr.h"
+#include "base/memory/raw_ref.h"
 #include "base/memory/safety_checks.h"
 #include "content/common/content_export.h"
+#include "content/public/browser/navigation_throttle_registry.h"
 #include "net/base/net_errors.h"
 
 namespace content {
@@ -140,7 +142,12 @@
     std::optional<std::string> error_page_content_;
   };
 
-  NavigationThrottle(NavigationHandle* navigation_handle);
+  // Note: This legacy constructor will be removed soon. New code should use the
+  // other constructor that takes a NavigationThrottleRegistry&.
+  // TODO(https://crbug.com/412524375): Remove this constructor.
+  explicit NavigationThrottle(NavigationHandle* navigation_handle);
+
+  explicit NavigationThrottle(NavigationThrottleRegistry& registry);
   virtual ~NavigationThrottle();
 
   // Called when a network request is about to be made for this navigation.
@@ -237,6 +244,10 @@
   virtual void CancelDeferredNavigation(ThrottleCheckResult result);
 
  private:
+  // TODO(https://crbug.com/412524375): Once all subclasses are migrated to
+  // construct this instance with a NavigationThrottleRegistry*, remove
+  // `navigation_handle_` and replace it with
+  // `const raw_ref<NavigationThrottleRegistry> registry_`.
   const raw_ptr<NavigationHandle> navigation_handle_;
 
   // Used in tests.
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index a7383e4..3949fd5d 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -1344,11 +1344,6 @@
   mojo::Remote<blink::mojom::RendererAudioInputStreamFactory>
       audio_input_stream_factory_;
 
-  // This interface handles generated code cache requests both to fetch code
-  // cache when loading resources and to store code caches when code caches are
-  // generated during the JS / Wasm script execution.
-  mojo::Remote<blink::mojom::CodeCacheHost> code_cache_host_;
-
   // The media permission dispatcher attached to this frame.
   std::unique_ptr<MediaPermissionDispatcher> media_permission_dispatcher_;
 
diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn
index 205af82..9d41c83a 100644
--- a/content/shell/BUILD.gn
+++ b/content/shell/BUILD.gn
@@ -248,6 +248,18 @@
 
   if (is_ios) {
     sources += [
+      "browser/bluetooth/ios/shell_bluetooth_chooser_coordinator.h",
+      "browser/bluetooth/ios/shell_bluetooth_chooser_coordinator.mm",
+      "browser/bluetooth/ios/shell_bluetooth_chooser_ios.h",
+      "browser/bluetooth/ios/shell_bluetooth_chooser_ios.mm",
+      "browser/bluetooth/ios/shell_bluetooth_chooser_mediator.h",
+      "browser/bluetooth/ios/shell_bluetooth_chooser_mediator.mm",
+      "browser/bluetooth/ios/shell_bluetooth_device_list_consumer.h",
+      "browser/bluetooth/ios/shell_bluetooth_device_list_delegate.h",
+      "browser/bluetooth/ios/shell_bluetooth_device_list_view_controller.h",
+      "browser/bluetooth/ios/shell_bluetooth_device_list_view_controller.mm",
+      "browser/bluetooth/shell_bluetooth_delegate_impl_client.cc",
+      "browser/bluetooth/shell_bluetooth_delegate_impl_client.h",
       "browser/color_chooser/shell_color_chooser_ios.h",
       "browser/color_chooser/shell_color_chooser_ios.mm",
       "browser/shell_browser_main_parts_ios.mm",
@@ -256,23 +268,6 @@
       "browser/shell_platform_delegate_ios.mm",
       "browser/shell_web_contents_view_delegate_ios.mm",
     ]
-
-    if (target_platform == "iphoneos") {
-      sources += [
-        "browser/bluetooth/ios/shell_bluetooth_chooser_coordinator.h",
-        "browser/bluetooth/ios/shell_bluetooth_chooser_coordinator.mm",
-        "browser/bluetooth/ios/shell_bluetooth_chooser_ios.h",
-        "browser/bluetooth/ios/shell_bluetooth_chooser_ios.mm",
-        "browser/bluetooth/ios/shell_bluetooth_chooser_mediator.h",
-        "browser/bluetooth/ios/shell_bluetooth_chooser_mediator.mm",
-        "browser/bluetooth/ios/shell_bluetooth_device_list_consumer.h",
-        "browser/bluetooth/ios/shell_bluetooth_device_list_delegate.h",
-        "browser/bluetooth/ios/shell_bluetooth_device_list_view_controller.h",
-        "browser/bluetooth/ios/shell_bluetooth_device_list_view_controller.mm",
-        "browser/bluetooth/shell_bluetooth_delegate_impl_client.cc",
-        "browser/bluetooth/shell_bluetooth_delegate_impl_client.h",
-      ]
-    }
   }
 
   if (is_win) {
diff --git a/content/shell/browser/bluetooth/ios/shell_bluetooth_chooser_coordinator.mm b/content/shell/browser/bluetooth/ios/shell_bluetooth_chooser_coordinator.mm
index 2858c82..855c2fd 100644
--- a/content/shell/browser/bluetooth/ios/shell_bluetooth_chooser_coordinator.mm
+++ b/content/shell/browser/bluetooth/ios/shell_bluetooth_chooser_coordinator.mm
@@ -20,13 +20,13 @@
 
   _deviceListViewController =
       [[ShellDeviceListViewController alloc] initWithTitle:title];
-  _deviceListViewController.modalPresentationStyle = UIModalPresentationPopover;
-  _deviceListViewController.popoverPresentationController.delegate =
+
+  // Set `modalPresentationStyle` to UIModalPresentationOverFullScreen available
+  // on iOS and tvOS.
+  _deviceListViewController.modalPresentationStyle =
+      UIModalPresentationOverFullScreen;
+  _deviceListViewController.presentationController.delegate =
       _deviceListViewController;
-  _deviceListViewController.popoverPresentationController.sourceView =
-      baseViewController.view;
-  _deviceListViewController.popoverPresentationController.sourceRect =
-      baseViewController.view.bounds;
 
   _bluetoothChooserMediator = [[ShellBluetoothChooserMediator alloc]
       initWithBluetoothChooser:bluetoothChooser];
diff --git a/content/shell/browser/bluetooth/ios/shell_bluetooth_device_list_view_controller.h b/content/shell/browser/bluetooth/ios/shell_bluetooth_device_list_view_controller.h
index 0be9cb8d..faf279fe 100644
--- a/content/shell/browser/bluetooth/ios/shell_bluetooth_device_list_view_controller.h
+++ b/content/shell/browser/bluetooth/ios/shell_bluetooth_device_list_view_controller.h
@@ -17,7 +17,7 @@
 // The ViewController that has UITableView to show the bluetooth device list.
 @interface ShellDeviceListViewController
     : UITableViewController <ShellBluetoothDeviceListConsumer,
-                             UIPopoverPresentationControllerDelegate>
+                             UIAdaptivePresentationControllerDelegate>
 
 // The view controller this coordinator was initialized with.
 @property(weak, nonatomic, readonly) UIViewController* baseViewController;
diff --git a/content/shell/browser/bluetooth/ios/shell_bluetooth_device_list_view_controller.mm b/content/shell/browser/bluetooth/ios/shell_bluetooth_device_list_view_controller.mm
index 006da4e..f8d9032 100644
--- a/content/shell/browser/bluetooth/ios/shell_bluetooth_device_list_view_controller.mm
+++ b/content/shell/browser/bluetooth/ios/shell_bluetooth_device_list_view_controller.mm
@@ -4,6 +4,7 @@
 
 #import "content/shell/browser/bluetooth/ios/shell_bluetooth_device_list_view_controller.h"
 
+#include "build/build_config.h"
 #import "content/shell/browser/bluetooth/ios/shell_bluetooth_device_list_delegate.h"
 
 // Has device information to display it on a cell of UITableView.
@@ -62,10 +63,36 @@
   }
   _selectedRowIndex = -1;
   _listTitle = [title copy];
+#if BUILDFLAG(IS_IOS_TVOS)
+  // On tvOS, the modal view has transparent background by default.
+  // Set the background color so that the texts from the dialog don't overlap
+  // the web page.
+  self.view.backgroundColor = [UIColor systemGrayColor];
+#endif
+  self.tableView.bounces = NO;
+
+#if !BUILDFLAG(IS_IOS_TVOS)
+  // Add Up/Down swipe actions to close this modal dialog.
+  // On tvOS, do not add any specific actions to close the dialog since
+  // the dialog is closed with a back button from a remote controller and
+  // swipe actions are used for focus navigation.
+  UISwipeGestureRecognizer* swipeUpDown = [[UISwipeGestureRecognizer alloc]
+      initWithTarget:self
+              action:@selector(recognizeSwipe:)];
+  [swipeUpDown setDirection:(UISwipeGestureRecognizerDirectionUp |
+                             UISwipeGestureRecognizerDirectionDown)];
+  [self.view addGestureRecognizer:swipeUpDown];
+#endif
 
   return self;
 }
 
+#if !BUILDFLAG(IS_IOS_TVOS)
+- (void)recognizeSwipe:(UITapGestureRecognizer*)gesture {
+  [self dismissViewControllerAnimated:YES completion:nil];
+}
+#endif
+
 - (NSString*)tableView:(UITableView*)tableView
     titleForHeaderInSection:(NSInteger)section {
   return self.listTitle;
diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc
index cdf869046..ab42138 100644
--- a/content/shell/browser/shell_content_browser_client.cc
+++ b/content/shell/browser/shell_content_browser_client.cc
@@ -23,7 +23,6 @@
 #include "base/functional/callback_helpers.h"
 #include "base/logging.h"
 #include "base/no_destructor.h"
-#include "base/notimplemented.h"
 #include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
@@ -134,10 +133,8 @@
 
 #if BUILDFLAG(IS_IOS)
 #include "components/permissions/bluetooth_delegate_impl.h"
-#if !BUILDFLAG(IS_IOS_TVOS)
 #include "content/shell/browser/bluetooth/shell_bluetooth_delegate_impl_client.h"
 #endif
-#endif
 
 #if BUILDFLAG(IS_WIN)
 #include "media/mojo/mojom/media_foundation_preferences.mojom.h"
@@ -840,16 +837,11 @@
 
 #if BUILDFLAG(IS_IOS)
 BluetoothDelegate* ShellContentBrowserClient::GetBluetoothDelegate() {
-#if !BUILDFLAG(IS_IOS_TVOS)
   if (!bluetooth_delegate_) {
     bluetooth_delegate_ = std::make_unique<permissions::BluetoothDelegateImpl>(
         std::make_unique<ShellBluetoothDelegateImplClient>());
   }
   return bluetooth_delegate_.get();
-#else
-  TVOS_NOT_YET_IMPLEMENTED();
-  return nullptr;
-#endif
 }
 #endif
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 30ab9e2..6e99811f 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1428,6 +1428,7 @@
     "../browser/back_forward_cache_browsertest.h",
     "../browser/back_forward_cache_features_browsertest.cc",
     "../browser/back_forward_cache_internal_browsertest.cc",
+    "../browser/back_forward_cache_limit_browsertest.cc",
     "../browser/back_forward_cache_network_request_browsertest.cc",
     "../browser/back_forward_cache_no_store_browsertest.cc",
     "../browser/back_forward_cache_not_restored_reasons_browsertest.cc",
@@ -3144,6 +3145,7 @@
     "//components/payments/mojom",
     "//components/performance_manager/public/mojom",
     "//components/performance_manager/scenario_api",
+    "//components/performance_manager/scenario_api:test_support",
     "//components/permissions:permissions_common",
     "//components/permissions:test_support",
     "//components/services/quarantine/public/mojom",
diff --git a/content/test/content_test_bundle_data.filelist b/content/test/content_test_bundle_data.filelist
index f4530f5..5405bc8 100644
--- a/content/test/content_test_bundle_data.filelist
+++ b/content/test/content_test_bundle_data.filelist
@@ -5234,6 +5234,9 @@
 data/accessibility/html/select-slowly-build-expected-auralinux.txt
 data/accessibility/html/select-slowly-build-expected-blink.txt
 data/accessibility/html/select-slowly-build.html
+data/accessibility/html/select-with-input-2-expected-auralinux.txt
+data/accessibility/html/select-with-input-2-expected-blink.txt
+data/accessibility/html/select-with-input-2.html
 data/accessibility/html/select-with-input-expected-auralinux.txt
 data/accessibility/html/select-with-input-expected-blink.txt
 data/accessibility/html/select-with-input.html
diff --git a/content/test/data/accessibility/html/select-with-input-2-expected-auralinux.txt b/content/test/data/accessibility/html/select-with-input-2-expected-auralinux.txt
new file mode 100644
index 0000000..4eb6b45
--- /dev/null
+++ b/content/test/data/accessibility/html/select-with-input-2-expected-auralinux.txt
@@ -0,0 +1,32 @@
+[document web]
+++[section]
+++++[combo box]
+++++++[menu] controlled-by=[entry]
+++++++++[section]
+++++++++++[section]
+++++++++++++[entry] selectable-text controller-for=[menu]
+++++++++++[menu item] name='one' selectable selected
+++++++++++[menu item] name='two' selectable
+++++[combo box]
+++++++[menu] controlled-by=[entry]
+++++++++[section]
+++++++++++[menu item] name='one' selectable selected
+++++++++++[section]
+++++++++++++[entry] selectable-text controller-for=[menu]
+++++++++++[menu item] name='two' selectable
+++++[combo box]
+++++++[dialog]
+++++++++[section]
+++++++++++[section]
+++++++++++++[radio button] checkable checkable:true
+++++++++++[menu item] name='one' selectable selected
+++++++++++[menu item] name='two' selectable
+++++[combo box]
+++++++[dialog] controlled-by=[entry,entry]
+++++++++[section]
+++++++++++[section]
+++++++++++++[entry] selectable-text controller-for=[dialog]
+++++++++++[section]
+++++++++++++[entry] selectable-text controller-for=[dialog]
+++++++++++[menu item] name='one' selectable selected
+++++++++++[menu item] name='two' selectable
diff --git a/content/test/data/accessibility/html/select-with-input-2-expected-blink.txt b/content/test/data/accessibility/html/select-with-input-2-expected-blink.txt
new file mode 100644
index 0000000..a17bea82
--- /dev/null
+++ b/content/test/data/accessibility/html/select-with-input-2-expected-blink.txt
@@ -0,0 +1,37 @@
+rootWebArea
+++genericContainer ignored
+++++genericContainer
+++++++comboBoxSelect collapsed value='one'
+++++++++menuListPopup invisible ispopup=auto
+++++++++++genericContainer invisible
+++++++++++++genericContainer invisible
+++++++++++++++textField invisible controlsIds=menuListPopup
+++++++++++++++++genericContainer invisible
+++++++++++++menuListOption name='one' selected=true
+++++++++++++menuListOption invisible name='two' selected=false
+++++++comboBoxSelect collapsed value='one'
+++++++++menuListPopup invisible ispopup=auto
+++++++++++genericContainer invisible
+++++++++++++menuListOption name='one' selected=true
+++++++++++++genericContainer invisible
+++++++++++++++textField invisible controlsIds=menuListPopup
+++++++++++++++++genericContainer invisible
+++++++++++++menuListOption invisible name='two' selected=false
+++++++comboBoxSelect collapsed value='one'
+++++++++dialog invisible ispopup=auto
+++++++++++genericContainer invisible
+++++++++++++genericContainer invisible
+++++++++++++++radioButton invisible checkedState=false
+++++++++++++menuListOption name='one' selected=true
+++++++++++++menuListOption invisible name='two' selected=false
+++++++comboBoxSelect collapsed value='one'
+++++++++dialog invisible ispopup=auto
+++++++++++genericContainer invisible
+++++++++++++genericContainer invisible
+++++++++++++++textField invisible controlsIds=dialog
+++++++++++++++++genericContainer invisible
+++++++++++++genericContainer invisible
+++++++++++++++textField invisible controlsIds=dialog
+++++++++++++++++genericContainer invisible
+++++++++++++menuListOption name='one' selected=true
+++++++++++++menuListOption invisible name='two' selected=false
diff --git a/content/test/data/accessibility/html/select-with-input-2.html b/content/test/data/accessibility/html/select-with-input-2.html
new file mode 100644
index 0000000..4f6b8d1
--- /dev/null
+++ b/content/test/data/accessibility/html/select-with-input-2.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<style>
+select, ::picker(select) {
+  appearance: base-select;
+}
+</style>
+
+<select id=s1>
+  <span class=input></span>
+  <option>one</option>
+  <option>two</option>
+</select>
+
+<select id=s2>
+  <option>one</option>
+  <span class=input></span>
+  <option>two</option>
+</select>
+
+<select id=s3>
+  <span class=input data-type=radio></span>
+  <option>one</option>
+  <option>two</option>
+</select>
+
+<select id=s4>
+  <span class=input></span>
+  <span class=input></span>
+  <option>one</option>
+  <option>two</option>
+</select>
+
+<script>
+  document.querySelectorAll('.input').forEach(container => {
+    const input = document.createElement('input');
+    input.type = container.getAttribute('data-type');
+    container.appendChild(input);
+  });
+</script>
diff --git a/content/test/gpu/bad_machine_finder/tasks.py b/content/test/gpu/bad_machine_finder/tasks.py
index f1eda8d..fe4cebf7 100644
--- a/content/test/gpu/bad_machine_finder/tasks.py
+++ b/content/test/gpu/bad_machine_finder/tasks.py
@@ -5,7 +5,7 @@
 
 import collections
 import functools
-from typing import Generator, List, Tuple
+from typing import Generator
 
 
 class BotStats:
@@ -78,6 +78,7 @@
     self._total_tasks = 0
     self._failed_tasks = 0
     self._bots = collections.defaultdict(BotStats)
+    self._cached_overall_failure_rates: list[float] | None = None
 
   def Freeze(self) -> None:
     if self._frozen:
@@ -98,18 +99,18 @@
     assert self._frozen
     return self._failed_tasks
 
-  def IterBots(self) -> Generator[Tuple[str, 'BotStats'], None, None]:
+  def IterBots(self) -> Generator[tuple[str, 'BotStats'], None, None]:
     assert self._frozen
     for bot_id, stats in self._bots.items():
       yield bot_id, stats
 
-  @functools.lru_cache(maxsize=None)
-  def GetOverallFailureRates(self) -> List[float]:
+  def GetOverallFailureRates(self) -> list[float]:
     assert self._frozen
-    failure_rates = []
-    for _, stats in self._bots.items():
-      failure_rates.append(stats.overall_failure_rate)
-    return failure_rates
+    if self._cached_overall_failure_rates is None:
+      self._cached_overall_failure_rates = []
+      for _, stats in self._bots.items():
+        self._cached_overall_failure_rates.append(stats.overall_failure_rate)
+    return self._cached_overall_failure_rates
 
   # Mutators
 
diff --git a/content/test/gpu/gather_power_measurement_results.py b/content/test/gpu/gather_power_measurement_results.py
index 163a9a2..65ad692 100755
--- a/content/test/gpu/gather_power_measurement_results.py
+++ b/content/test/gpu/gather_power_measurement_results.py
@@ -40,9 +40,8 @@
   url = ulib_request.Request(
       'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds/' + method,
       request, headers)
-  conn = ulib_request.urlopen(url)
-  result = conn.read()
-  conn.close()
+  with ulib_request.urlopen(url) as conn:
+    result = conn.read()
   # Result is a multi-line string the first line of which is
   # deliberate garbage and the rest of which is a JSON payload.
   return json.loads(''.join(result.splitlines()[1:]))
@@ -127,9 +126,8 @@
                 ulib_parse.unquote(stdout_url))
 
   # The following fails with Python 2.7.6, but succeeds with Python 2.7.14.
-  conn = ulib_request.urlopen(stdout_url + '?format=raw')
-  lines = conn.read().splitlines()
-  conn.close()
+  with ulib_request.urlopen(stdout_url + '?format=raw') as conn:
+    lines = conn.read().splitlines()
 
   pattern = re.compile(r'^\[(\d+)/(\d+)\]$')
   results = None
diff --git a/content/test/gpu/gather_swarming_json_results.py b/content/test/gpu/gather_swarming_json_results.py
index 5fa0ed3..841211c 100755
--- a/content/test/gpu/gather_swarming_json_results.py
+++ b/content/test/gpu/gather_swarming_json_results.py
@@ -33,9 +33,8 @@
   url = ulib.Request(
       'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds/' + method,
       request, headers)
-  conn = ulib.urlopen(url)
-  result = conn.read().decode('utf-8')
-  conn.close()
+  with ulib.urlopen(url) as conn:
+    result = conn.read().decode('utf-8')
   # Result is a multi-line string the first line of which is
   # deliberate garbage and the rest of which is a JSON payload.
   return json.loads(''.join(result.splitlines()[1:]))
@@ -81,9 +80,8 @@
 
 
 def JsonLoadFromUrl(url):
-  conn = ulib.urlopen(url + '?format=raw')
-  result = conn.read()
-  conn.close()
+  with ulib.urlopen(url + '?format=raw') as conn:
+    result = conn.read()
   return json.loads(result)
 
 
diff --git a/content/test/gpu/gold_inexact_matching/base_parameter_optimizer.py b/content/test/gpu/gold_inexact_matching/base_parameter_optimizer.py
index 1551030..3d30b95 100644
--- a/content/test/gpu/gold_inexact_matching/base_parameter_optimizer.py
+++ b/content/test/gpu/gold_inexact_matching/base_parameter_optimizer.py
@@ -14,7 +14,6 @@
 import shutil
 import subprocess
 import tempfile
-from typing import Dict, List, Optional, Set, Tuple
 
 from PIL import Image  # pylint: disable=import-error
 
@@ -42,10 +41,10 @@
 #     }
 #   }
 # }
-ExpectationJson = Dict[str, Dict[str, Dict[str, str]]]
+ExpectationJson = dict[str, dict[str, dict[str, str]]]
 
 
-class BaseParameterOptimizer():
+class BaseParameterOptimizer:
   """Abstract base class for running a parameter optimization for a test."""
   MIN_EDGE_THRESHOLD = 0
   MAX_EDGE_THRESHOLD = 255
@@ -62,16 +61,16 @@
     """
     self._args = args
     self._test_name = test_name
-    self._goldctl_binary: Optional[str] = None
-    self._working_dir: Optional[str] = None
-    self._expectations: Optional[ExpectationJson] = None
+    self._goldctl_binary: str | None = None
+    self._working_dir: str | None = None
+    self._expectations: ExpectationJson | None = None
     # TODO(skbug.com/10610): Switch away from the public instance once
     # authentication is fixed for the non-public instance.
     self._gold_url = f'https://{args.gold_instance}-public-gold.skia.org'
     self._pool = multiprocessing.Pool()
     # A map of strings, denoting a resolution or trace, to a set of strings,
     # denoting images that are that dimension or belong to that trace.
-    self._images: Dict[str, Set[str]] = collections.defaultdict(set)
+    self._images: dict[str, set[str]] = collections.defaultdict(set)
     self._VerifyArgs()
     parameter_set.ParameterSet.ignored_border_thickness = \
         self._args.ignored_border_thickness
@@ -367,7 +366,7 @@
     return self._goldctl_binary
 
   def _RunComparisonForParameters(
-      self, parameters: parameter_set.ParameterSet) -> Tuple[bool, int, int]:
+      self, parameters: parameter_set.ParameterSet) -> tuple[bool, int, int]:
     """Runs a comparison for all image combinations using some parameters.
 
     Args:
@@ -419,7 +418,7 @@
 
   def _GenerateComparisonCmd(
       self, left_digest: str, right_digest: str,
-      parameters: parameter_set.ParameterSet) -> List[str]:
+      parameters: parameter_set.ParameterSet) -> list[str]:
     """Generates a comparison command for the given arguments.
 
     The returned command can be passed directly to a subprocess call.
@@ -445,7 +444,7 @@
     return cmd
 
 
-def RunCommandAndExtractData(cmd: List[str]) -> Tuple[bool, int, int]:
+def RunCommandAndExtractData(cmd: list[str]) -> tuple[bool, int, int]:
   """Runs a comparison command and extracts data from it.
 
   This is outside of the parameter optimizers because it is meant to be run via
diff --git a/content/test/gpu/gpu_tests/color_profile_manager.py b/content/test/gpu/gpu_tests/color_profile_manager.py
index c31dd4c0..1155c151 100644
--- a/content/test/gpu/gpu_tests/color_profile_manager.py
+++ b/content/test/gpu/gpu_tests/color_profile_manager.py
@@ -32,9 +32,8 @@
     if skip_restoring_color_profile:
       print('Skipping restoring the original color profile')
       return
-    for display_id in display_profile_url_map:
-      color_profile_manager_mac.SetDisplayCustomProfile(
-          display_id, display_profile_url_map[display_id])
+    for display_id, profile_url in display_profile_url_map.items():
+      color_profile_manager_mac.SetDisplayCustomProfile(display_id, profile_url)
 
   atexit.register(Restore)
 
diff --git a/content/test/gpu/gpu_tests/context_lost_integration_test.py b/content/test/gpu/gpu_tests/context_lost_integration_test.py
index 761268b..4a09e59 100644
--- a/content/test/gpu/gpu_tests/context_lost_integration_test.py
+++ b/content/test/gpu/gpu_tests/context_lost_integration_test.py
@@ -202,10 +202,11 @@
     cls.SetStaticServerDirs([gpu_path_util.GPU_DATA_DIR])
 
   # Can be changed to functools.cache on Python 3.9+.
+  @classmethod
   @functools.lru_cache(maxsize=None)
-  def _GetWaitTimeout(self):
+  def _GetWaitTimeout(cls):
     timeout = 60
-    if self._is_asan or self.browser.browser_type == 'debug':
+    if cls._is_asan or cls.browser.browser_type == 'debug':
       timeout *= 2
     return timeout
 
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py b/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
index 99de5f7..214f71a 100644
--- a/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
+++ b/content/test/gpu/gpu_tests/gpu_integration_test_unittest.py
@@ -158,12 +158,12 @@
       # tempfile_ext.NamedTemporaryFile(), put it in the list of generators
       # starting this with block. Also remove the try finally statement
       # below.
-      temp_file = tempfile.NamedTemporaryFile(delete=False)
-      temp_file.close()
+      with tempfile.NamedTemporaryFile(delete=False) as temp_file:
+        temp_file_name = temp_file.name
       try:
         test_argv = [
             test_name,
-            f'--write-full-results-to={temp_file.name}',
+            f'--write-full-results-to={temp_file_name}',
             # We don't want the underlying typ-based tests to report their
             # results to ResultDB.
             '--disable-resultsink',
@@ -177,10 +177,10 @@
         telemetry_args = browser_test_runner.ProcessConfig(
             unittest_config, processed_args)
         run_browser_tests.RunTests(telemetry_args)
-        with open(temp_file.name, encoding='utf-8') as f:
+        with open(temp_file_name, encoding='utf-8') as f:
           self._test_result = json.load(f)
       finally:
-        temp_file.close()
+        os.remove(temp_file_name)
 
   def testOverrideDefaultRetryArgumentsinRunGpuIntegrationTests(self) -> None:
     self._RunGpuIntegrationTests('run_tests_with_expectations_files',
diff --git a/content/test/gpu/gpu_tests/ipg_utils.py b/content/test/gpu/gpu_tests/ipg_utils.py
index 8fc08a1..14de5137 100644
--- a/content/test/gpu/gpu_tests/ipg_utils.py
+++ b/content/test/gpu/gpu_tests/ipg_utils.py
@@ -25,6 +25,8 @@
 import subprocess
 from typing import Any
 
+import dataclasses  # Built-in, but Pylint 2.7 gives an ordering false positive.
+
 from gpu_tests.util import host_information
 
 SummaryType = dict[str, dict[str, float]]
@@ -32,6 +34,17 @@
 MetricType = dict[str, list[str] | list[float]]
 
 
+@dataclasses.dataclass
+class _LogFileColumn:
+  """Represents the parsed data from a column in an IPG log file."""
+  # The index of this column within the file.
+  index: int
+  # The name of the column.
+  label: str
+  # The sum of all rows within the column.
+  total: float = 0.0
+
+
 def LocateIPG() -> str:
   if host_information.IsWindows():
     ipg_dir = os.getenv('IPG_Dir')
@@ -89,42 +102,41 @@
   if not logfile:
     logfile = GenerateIPGLogFilename()
   if not os.path.isfile(logfile):
-    raise Exception("Can't locate logfile at " + logfile)
+    raise Exception(f"Can't locate logfile at {logfile}")
   first_line = True
   samples = 0
-  cols = 0
-  indices = []
-  labels = []
-  sums = []
+  total_columns = 0
+  columns = []
   col_time = None
-  for line in open(logfile, encoding='utf-8'):
+  with open(logfile, encoding='utf-8') as infile:
+    contents = infile.read()
+  for line in contents.splitlines(keepends=True):
     tokens = [token.strip('" ') for token in line.split(',')]
     if first_line:
       first_line = False
-      cols = len(tokens)
-      for ii in range(0, cols):
+      total_columns = len(tokens)
+      for ii in range(total_columns):
         token = tokens[ii]
         if token.startswith('Elapsed Time'):
           col_time = ii
         elif token.endswith('(Watt)'):
-          indices.append(ii)
-          labels.append(token[:-len('(Watt)')])
-          sums.append(0.0)
+          columns.append(_LogFileColumn(index=ii, label=token[:-len('(Watt)')]))
       assert col_time
-      assert cols > 0
-      assert len(indices) > 0
+      assert total_columns > 0
+      assert len(columns) > 0
       continue
-    if len(tokens) != cols:
+    if len(tokens) != total_columns:
       continue
     if skip_in_sec > 0 and float(tokens[col_time]) < skip_in_sec:
       continue
     samples += 1
-    for ii, index in enumerate(indices):
-      sums[ii] += float(tokens[index])
+    for c in columns:
+      c.total += float(tokens[c.index])
+
   results = {'samples': samples}
   if samples > 0:
-    for ii in range(0, len(indices)):
-      results[labels[ii]] = sums[ii] / samples
+    for c in columns:
+      results[c.label] = c.total / samples
   return results
 
 
@@ -154,10 +166,10 @@
         core = core[len(prefix):]
       per_core_results[core] = results
 
-      for key in results:
+      for key, value in results.items():
         if key in ('samples', 'log'):
           continue
-        metrics.setdefault(key, []).append(results[key])
+        metrics.setdefault(key, []).append(value)
     return per_core_results, metrics
 
   def _CalculateSummaryStatistics(metrics: MetricType) -> SummaryType:
@@ -195,8 +207,7 @@
   output['summary'] = summary
 
   if output_json:
-    json_file = open(output_json, 'w', encoding='utf-8')
-    json_file.write(json.dumps(output, indent=4))
-    json_file.close()
+    with open(output_json, 'w', encoding='utf-8') as json_file:
+      json_file.write(json.dumps(output, indent=4))
 
   return summary
diff --git a/content/test/gpu/gpu_tests/overlay_support.py b/content/test/gpu/gpu_tests/overlay_support.py
index dbe318e..0115673 100644
--- a/content/test/gpu/gpu_tests/overlay_support.py
+++ b/content/test/gpu/gpu_tests/overlay_support.py
@@ -120,7 +120,7 @@
     if set(self_dict.keys()) != set(other_dict.keys()):
       return False
 
-    return all(self_dict[k] == other_dict[k] for k in self_dict)
+    return all(v == other_dict[k] for k, v in self_dict.items())
 
   def WithDirectComposition(self) -> 'GpuOverlayConfig':
     """Enables direct composition support via software."""
@@ -461,7 +461,9 @@
                 .WithHardwareNV12Support(driver_conditionals=[
                     DriverConditional('ge', '31.0.15.4601')])\
                 .WithHardwareYUY2Support(driver_conditionals=[
-                    DriverConditional('ge', '31.0.15.4601')])
+                    DriverConditional('ge', '31.0.15.4601')])\
+                .WithHardwareBGRA8Support(driver_conditionals=[
+                    DriverConditional('ge', '32.0.15.7602')])\
                 .WithForceComposedBGRA8(driver_conditionals=[
                     DriverConditional('lt', '31.0.15.4601')])\
                 .WithZeroCopyConfig(ZeroCopyConfig(
diff --git a/content/test/gpu/gpu_tests/skia_gold_integration_test_base.py b/content/test/gpu/gpu_tests/skia_gold_integration_test_base.py
index b7d7e03..a13f54c4 100644
--- a/content/test/gpu/gpu_tests/skia_gold_integration_test_base.py
+++ b/content/test/gpu/gpu_tests/skia_gold_integration_test_base.py
@@ -344,9 +344,16 @@
     # PNG to disk, following the pattern in bitmap_unittest.py. The key to
     # avoiding PermissionErrors seems to be to not actually try to write to
     # the temporary file object, but to re-open its name for all operations.
-    temp_file = tempfile.NamedTemporaryFile(suffix='.png').name
-    image_util.WritePngFile(bitmap, temp_file)
-    cloud_storage.Insert(bucket, name, temp_file, publicly_readable=public)
+    with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as temp_file:
+      temp_file_name = temp_file.name
+    try:
+      image_util.WritePngFile(bitmap, temp_file_name)
+      cloud_storage.Insert(bucket,
+                           name,
+                           temp_file_name,
+                           publicly_readable=public)
+    finally:
+      os.remove(temp_file_name)
 
   # Not used consistently, but potentially useful for debugging issues on the
   # bots, so kept around for future use.
@@ -454,25 +461,25 @@
       test_case: the GPU SkiaGoldTestCase object for the test.
     """
     # Write screenshot to PNG file on local disk.
-    png_temp_file = tempfile.NamedTemporaryFile(
-        suffix='.png', dir=self._skia_gold_temp_dir).name
-    image_util.WritePngFile(screenshot, png_temp_file)
+    with tempfile.NamedTemporaryFile(suffix='.png',
+                                     dir=self._skia_gold_temp_dir,
+                                     delete=False) as png_temp_file:
+      png_temp_file_name = png_temp_file.name
+    image_util.WritePngFile(screenshot, png_temp_file_name)
 
-    gpu_keys = self.GetGoldJsonKeys(test_case)
     gold_session = self.GetSkiaGoldSessionManager().GetSkiaGoldSession(
-        gpu_keys, corpus=SKIA_GOLD_CORPUS)
+        self.GetGoldJsonKeys(test_case), corpus=SKIA_GOLD_CORPUS)
     gold_properties = self.GetSkiaGoldProperties()
     use_luci = not (gold_properties.local_pixel_tests
                     or gold_properties.no_luci_auth)
-    optional_keys = self.GetGoldOptionalKeys()
 
     status, error = gold_session.RunComparison(
         name=image_name,
-        png_file=png_temp_file,
+        png_file=png_temp_file_name,
         inexact_matching_args=test_case.matching_algorithm.GetCmdline(),
         use_luci=use_luci,
         service_account=gold_properties.service_account,
-        optional_keys=optional_keys)
+        optional_keys=self.GetGoldOptionalKeys())
     if not status:
       return
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt
index b533c7c..81fd678 100644
--- a/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/cast_streaming_expectations.txt
@@ -1,7 +1,7 @@
 # BEGIN TAG HEADER (autogenerated, see validate_tag_consistency.py)
 # OS
 # tags: [ android android-oreo android-pie android-r android-s android-t
-#             android-14
+#             android-14 android-15 android-16
 #         chromeos
 #         fuchsia
 #         linux ubuntu
@@ -11,6 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
+#         android-brya
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
@@ -37,7 +38,8 @@
 #         google google-0xffff google-0xc0de
 #         imagination
 #         intel intel-gen-9 intel-gen-12 intel-0xa2e intel-0xd26 intel-0xa011
-#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x5912 intel-0x9bc5
+#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912
+#             intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0xfe9 nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783
 #         qualcomm qualcomm-0x41333430 qualcomm-0x36333630 qualcomm-0x36334330 ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
index 3b140a3..0eab87e 100644
--- a/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/context_lost_expectations.txt
@@ -1,7 +1,7 @@
 # BEGIN TAG HEADER (autogenerated, see validate_tag_consistency.py)
 # OS
 # tags: [ android android-oreo android-pie android-r android-s android-t
-#             android-14
+#             android-14 android-15 android-16
 #         chromeos
 #         fuchsia
 #         linux ubuntu
@@ -11,6 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
+#         android-brya
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
@@ -37,7 +38,8 @@
 #         google google-0xffff google-0xc0de
 #         imagination
 #         intel intel-gen-9 intel-gen-12 intel-0xa2e intel-0xd26 intel-0xa011
-#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x5912 intel-0x9bc5
+#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912
+#             intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0xfe9 nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783
 #         qualcomm qualcomm-0x41333430 qualcomm-0x36333630 qualcomm-0x36334330 ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt
index 10092b4..e63345d 100644
--- a/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/expected_color_expectations.txt
@@ -1,7 +1,7 @@
 # BEGIN TAG HEADER (autogenerated, see validate_tag_consistency.py)
 # OS
 # tags: [ android android-oreo android-pie android-r android-s android-t
-#             android-14
+#             android-14 android-15 android-16
 #         chromeos
 #         fuchsia
 #         linux ubuntu
@@ -11,6 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
+#         android-brya
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
@@ -37,7 +38,8 @@
 #         google google-0xffff google-0xc0de
 #         imagination
 #         intel intel-gen-9 intel-gen-12 intel-0xa2e intel-0xd26 intel-0xa011
-#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x5912 intel-0x9bc5
+#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912
+#             intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0xfe9 nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783
 #         qualcomm qualcomm-0x41333430 qualcomm-0x36333630 qualcomm-0x36334330 ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
index 0cdc7f6..9b9ada6 100644
--- a/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/gpu_process_expectations.txt
@@ -1,7 +1,7 @@
 # BEGIN TAG HEADER (autogenerated, see validate_tag_consistency.py)
 # OS
 # tags: [ android android-oreo android-pie android-r android-s android-t
-#             android-14
+#             android-14 android-15 android-16
 #         chromeos
 #         fuchsia
 #         linux ubuntu
@@ -11,6 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
+#         android-brya
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
@@ -37,7 +38,8 @@
 #         google google-0xffff google-0xc0de
 #         imagination
 #         intel intel-gen-9 intel-gen-12 intel-0xa2e intel-0xd26 intel-0xa011
-#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x5912 intel-0x9bc5
+#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912
+#             intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0xfe9 nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783
 #         qualcomm qualcomm-0x41333430 qualcomm-0x36333630 qualcomm-0x36334330 ]
@@ -160,6 +162,7 @@
 crbug.com/380269801 [ android android-sm-a137f ] GpuProcess_visibility [ Failure ]
 crbug.com/380269801 [ android android-sm-a236b ] GpuProcess_visibility [ Failure ]
 crbug.com/380269801 [ android android-pixel-4 ] GpuProcess_visibility [ RetryOnFailure ]
+crbug.com/380269801 [ android android-brya ] GpuProcess_visibility [ Failure ]
 
 #######################################################################
 # Automated Entries After This Point - Do Not Manually Add Below Here #
diff --git a/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
index 1e7e40a3..d83181619 100644
--- a/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/hardware_accelerated_feature_expectations.txt
@@ -1,7 +1,7 @@
 # BEGIN TAG HEADER (autogenerated, see validate_tag_consistency.py)
 # OS
 # tags: [ android android-oreo android-pie android-r android-s android-t
-#             android-14
+#             android-14 android-15 android-16
 #         chromeos
 #         fuchsia
 #         linux ubuntu
@@ -11,6 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
+#         android-brya
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
@@ -37,7 +38,8 @@
 #         google google-0xffff google-0xc0de
 #         imagination
 #         intel intel-gen-9 intel-gen-12 intel-0xa2e intel-0xd26 intel-0xa011
-#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x5912 intel-0x9bc5
+#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912
+#             intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0xfe9 nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783
 #         qualcomm qualcomm-0x41333430 qualcomm-0x36333630 qualcomm-0x36334330 ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
index dd9b677..45461066 100644
--- a/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/info_collection_expectations.txt
@@ -1,7 +1,7 @@
 # BEGIN TAG HEADER (autogenerated, see validate_tag_consistency.py)
 # OS
 # tags: [ android android-oreo android-pie android-r android-s android-t
-#             android-14
+#             android-14 android-15 android-16
 #         chromeos
 #         fuchsia
 #         linux ubuntu
@@ -11,6 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
+#         android-brya
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
@@ -37,7 +38,8 @@
 #         google google-0xffff google-0xc0de
 #         imagination
 #         intel intel-gen-9 intel-gen-12 intel-0xa2e intel-0xd26 intel-0xa011
-#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x5912 intel-0x9bc5
+#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912
+#             intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0xfe9 nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783
 #         qualcomm qualcomm-0x41333430 qualcomm-0x36333630 qualcomm-0x36334330 ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
index d3b85dc..e41b10b1 100644
--- a/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/pixel_expectations.txt
@@ -1,7 +1,7 @@
 # BEGIN TAG HEADER (autogenerated, see validate_tag_consistency.py)
 # OS
 # tags: [ android android-oreo android-pie android-r android-s android-t
-#             android-14
+#             android-14 android-15 android-16
 #         chromeos
 #         fuchsia
 #         linux ubuntu
@@ -11,6 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
+#         android-brya
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
@@ -37,7 +38,8 @@
 #         google google-0xffff google-0xc0de
 #         imagination
 #         intel intel-gen-9 intel-gen-12 intel-0xa2e intel-0xd26 intel-0xa011
-#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x5912 intel-0x9bc5
+#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912
+#             intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0xfe9 nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783
 #         qualcomm qualcomm-0x41333430 qualcomm-0x36333630 qualcomm-0x36334330 ]
@@ -479,9 +481,6 @@
 crbug.com/370694819 [ angle-opengl asan graphite-disabled mac ] Pixel_WebGPUDestroyed_OffscreenCanvas* [ Failure ]
 crbug.com/370694819 [ angle-opengl asan graphite-disabled mac ] Pixel_WebGPUDestroyed_OnscreenCanvas* [ Failure ]
 
-# Failure on Mac ARM
-crbug.com/412679361 [ mac mac-arm64 ] Pixel_WebGPUCopyExternalImageWebGPUCanvas [ Failure ]
-
 # Flaky on Win10 Intel with bad image
 crbug.com/413062240 [ win10 intel-0x4680 passthrough ] Pixel_WebGPUDestroyed_OnscreenCanvas_CopyExternalImageToTexture [ RetryOnFailure ]
 
diff --git a/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
index 1e7e40a3..d83181619 100644
--- a/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/power_measurement_expectations.txt
@@ -1,7 +1,7 @@
 # BEGIN TAG HEADER (autogenerated, see validate_tag_consistency.py)
 # OS
 # tags: [ android android-oreo android-pie android-r android-s android-t
-#             android-14
+#             android-14 android-15 android-16
 #         chromeos
 #         fuchsia
 #         linux ubuntu
@@ -11,6 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
+#         android-brya
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
@@ -37,7 +38,8 @@
 #         google google-0xffff google-0xc0de
 #         imagination
 #         intel intel-gen-9 intel-gen-12 intel-0xa2e intel-0xd26 intel-0xa011
-#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x5912 intel-0x9bc5
+#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912
+#             intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0xfe9 nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783
 #         qualcomm qualcomm-0x41333430 qualcomm-0x36333630 qualcomm-0x36334330 ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
index b38dac1..21681883 100644
--- a/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/screenshot_sync_expectations.txt
@@ -1,7 +1,7 @@
 # BEGIN TAG HEADER (autogenerated, see validate_tag_consistency.py)
 # OS
 # tags: [ android android-oreo android-pie android-r android-s android-t
-#             android-14
+#             android-14 android-15 android-16
 #         chromeos
 #         fuchsia
 #         linux ubuntu
@@ -11,6 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
+#         android-brya
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
@@ -37,7 +38,8 @@
 #         google google-0xffff google-0xc0de
 #         imagination
 #         intel intel-gen-9 intel-gen-12 intel-0xa2e intel-0xd26 intel-0xa011
-#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x5912 intel-0x9bc5
+#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912
+#             intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0xfe9 nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783
 #         qualcomm qualcomm-0x41333430 qualcomm-0x36333630 qualcomm-0x36334330 ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
index 3c4dc39..85c38f5b 100644
--- a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
@@ -1,7 +1,7 @@
 # BEGIN TAG HEADER (autogenerated, see validate_tag_consistency.py)
 # OS
 # tags: [ android android-oreo android-pie android-r android-s android-t
-#             android-14
+#             android-14 android-15 android-16
 #         chromeos
 #         fuchsia
 #         linux ubuntu
@@ -11,6 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
+#         android-brya
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
@@ -37,7 +38,8 @@
 #         google google-0xffff google-0xc0de
 #         imagination
 #         intel intel-gen-9 intel-gen-12 intel-0xa2e intel-0xd26 intel-0xa011
-#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x5912 intel-0x9bc5
+#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912
+#             intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0xfe9 nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783
 #         qualcomm qualcomm-0x41333430 qualcomm-0x36333630 qualcomm-0x36334330 ]
@@ -211,18 +213,18 @@
 crbug.com/329138770 [ win graphite-enabled ] SwapChainTraceTest_CanvasLowLatencyWebGLAlphaFalse [ Failure ]
 crbug.com/329138770 [ win graphite-enabled ] SwapChainTraceTest_CanvasLowLatencyWebGLDrawImage [ Failure ]
 crbug.com/329138770 [ win graphite-enabled ] SwapChainTraceTest_CanvasLowLatencyWebGLRoundedCorners [ Failure ]
-crbug.com/329138770 [ graphite-enabled no-clang-coverage nvidia-0x2783 nvidia_lt_535.183.01 release-x64 target-cpu-64 win11 ] WebGPUCachingTraceTest_ComputePipelineCrossOriginsCacheMisses [ Failure ]
 crbug.com/329138770 [ graphite-enabled win10 ] WebGPUCachingTraceTest_ComputePipelineCrossOriginsCacheMisses [ Failure ]
-crbug.com/329138770 [ graphite-enabled no-clang-coverage nvidia-0x2783 nvidia_lt_535.183.01 release-x64 target-cpu-64 win11 ] WebGPUCachingTraceTest_ComputePipelineDifferentOrigins [ Failure ]
 crbug.com/329138770 [ graphite-enabled win10 ] WebGPUCachingTraceTest_ComputePipelineDifferentOrigins [ Failure ]
-crbug.com/329138770 [ graphite-enabled no-clang-coverage nvidia-0x2783 nvidia_lt_535.183.01 release-x64 target-cpu-64 win11 ] WebGPUCachingTraceTest_ComputePipelineIncognito [ Failure ]
 crbug.com/329138770 [ graphite-enabled win10 ] WebGPUCachingTraceTest_ComputePipelineIncognito [ Failure ]
-crbug.com/329138770 [ graphite-enabled no-clang-coverage nvidia-0x2783 nvidia_lt_535.183.01 release-x64 target-cpu-64 win11 ] WebGPUCachingTraceTest_RenderPipelineCrossOriginsCacheMisses [ Failure ]
 crbug.com/329138770 [ graphite-enabled win10 ] WebGPUCachingTraceTest_RenderPipelineCrossOriginsCacheMisses [ Failure ]
-crbug.com/329138770 [ graphite-enabled no-clang-coverage nvidia-0x2783 nvidia_lt_535.183.01 release-x64 target-cpu-64 win11 ] WebGPUCachingTraceTest_RenderPipelineDifferentOrigins [ Failure ]
 crbug.com/329138770 [ graphite-enabled win10 ] WebGPUCachingTraceTest_RenderPipelineDifferentOrigins [ Failure ]
-crbug.com/329138770 [ graphite-enabled no-clang-coverage nvidia-0x2783 nvidia_lt_535.183.01 release-x64 target-cpu-64 win11 ] WebGPUCachingTraceTest_RenderPipelineIncognito [ Failure ]
 crbug.com/329138770 [ graphite-enabled win10 ] WebGPUCachingTraceTest_RenderPipelineIncognito [ Failure ]
+crbug.com/329138770 [ graphite-enabled win11 nvidia ] WebGPUCachingTraceTest_ComputePipelineCrossOriginsCacheMisses [ Failure ]
+crbug.com/329138770 [ graphite-enabled win11 nvidia ] WebGPUCachingTraceTest_ComputePipelineDifferentOrigins [ Failure ]
+crbug.com/329138770 [ graphite-enabled win11 nvidia ] WebGPUCachingTraceTest_ComputePipelineIncognito [ Failure ]
+crbug.com/329138770 [ graphite-enabled win11 nvidia ] WebGPUCachingTraceTest_RenderPipelineCrossOriginsCacheMisses [ Failure ]
+crbug.com/329138770 [ graphite-enabled win11 nvidia ] WebGPUCachingTraceTest_RenderPipelineDifferentOrigins [ Failure ]
+crbug.com/329138770 [ graphite-enabled win11 nvidia ] WebGPUCachingTraceTest_RenderPipelineIncognito [ Failure ]
 
 # Win/AMD RX 7600 failures
 crbug.com/402989071 [ win11 amd-0x7480 ] TraceTest_MediaFoundationD3D11VideoCapture [ Failure ]
diff --git a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
index 01f1e5a..6ec33ae5 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webcodecs_expectations.txt
@@ -1,7 +1,7 @@
 # BEGIN TAG HEADER (autogenerated, see validate_tag_consistency.py)
 # OS
 # tags: [ android android-oreo android-pie android-r android-s android-t
-#             android-14
+#             android-14 android-15 android-16
 #         chromeos
 #         fuchsia
 #         linux ubuntu
@@ -11,6 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
+#         android-brya
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
@@ -37,7 +38,8 @@
 #         google google-0xffff google-0xc0de
 #         imagination
 #         intel intel-gen-9 intel-gen-12 intel-0xa2e intel-0xd26 intel-0xa011
-#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x5912 intel-0x9bc5
+#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912
+#             intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0xfe9 nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783
 #         qualcomm qualcomm-0x41333430 qualcomm-0x36333630 qualcomm-0x36334330 ]
@@ -146,6 +148,20 @@
 # Same with prefer-hardware
 crbug.com/371802469 [ amd-0x7340 angle-opengl asan graphite-disabled mac ] WebCodecs_Encode_camera_hvc1.1.6.L123.00_prefer-hardware [ Failure ]
 
+# Flaky crashes that started when upgrading to Mac 15.4
+crbug.com/416294710 [ sequoia angle-opengl graphite-disabled intel-0x3e9b ] WebCodecs_ContentHint_hvc1.1.6.L123.00_detail [ Failure ]
+crbug.com/416294710 [ sequoia angle-opengl graphite-disabled intel-0x3e9b ] WebCodecs_ContentHint_hvc1.1.6.L123.00_motion [ Failure ]
+crbug.com/416294710 [ sequoia angle-opengl graphite-disabled intel-0x3e9b ] WebCodecs_EncodeColorSpace_hvc1.1.6.L123.00_prefer-hardware [ Failure ]
+crbug.com/416294710 [ sequoia angle-opengl graphite-disabled intel-0x3e9b ] WebCodecs_EncodeDecode_arraybuffer_hvc1.1.6.L123.00_prefer-hardware [ Failure ]
+crbug.com/416294710 [ sequoia angle-opengl graphite-disabled intel-0x3e9b ] WebCodecs_EncodeDecode_camera_hvc1.1.6.L123.00_prefer-hardware [ Failure ]
+crbug.com/416294710 [ sequoia angle-opengl graphite-disabled intel-0x3e9b ] WebCodecs_EncodeDecode_capture_hvc1.1.6.L123.00_prefer-hardware [ Failure ]
+crbug.com/416294710 [ sequoia angle-opengl graphite-disabled intel-0x3e9b ] WebCodecs_EncodeDecode_hw_decoder_hvc1.1.6.L123.00_prefer-hardware [ Failure ]
+crbug.com/416294710 [ sequoia angle-opengl graphite-disabled intel-0x3e9b ] WebCodecs_EncodeDecode_offscreen_hvc1.1.6.L123.00_prefer-hardware [ Failure ]
+crbug.com/416294710 [ sequoia angle-opengl graphite-disabled intel-0x3e9b ] WebCodecs_EncodeDecode_sw_decoder_hvc1.1.6.L123.00_prefer-hardware [ Failure ]
+crbug.com/416294710 [ sequoia angle-opengl graphite-disabled intel-0x3e9b ] WebCodecs_Encode_arraybuffer_hvc1.1.6.L123.00_prefer-hardware [ Failure ]
+crbug.com/416294710 [ sequoia angle-opengl graphite-disabled intel-0x3e9b ] WebCodecs_Encode_camera_hvc1.1.6.L123.00_prefer-hardware [ Failure ]
+crbug.com/416294710 [ sequoia angle-opengl graphite-disabled intel-0x3e9b ] WebCodecs_Encode_capture_hvc1.1.6.L123.00_prefer-hardware [ Failure ]
+
 crbug.com/389978730 [ win11 nvidia-0x2783 ] WebCodecs_EncodeColorSpace_av01.0.04M.08_prefer-hardware [ Failure ]
 
 # Win/AMD RX 7600 Failures
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
index 71b92177..6dc4074 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl2_conformance_expectations.txt
@@ -1,7 +1,7 @@
 # BEGIN TAG HEADER (autogenerated, see validate_tag_consistency.py)
 # OS
 # tags: [ android android-oreo android-pie android-r android-s android-t
-#             android-14
+#             android-14 android-15 android-16
 #         chromeos
 #         fuchsia
 #         linux ubuntu
@@ -11,6 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
+#         android-brya
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
@@ -37,7 +38,8 @@
 #         google google-0xffff google-0xc0de
 #         imagination
 #         intel intel-gen-9 intel-gen-12 intel-0xa2e intel-0xd26 intel-0xa011
-#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x5912 intel-0x9bc5
+#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912
+#             intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0xfe9 nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783
 #         qualcomm qualcomm-0x41333430 qualcomm-0x36333630 qualcomm-0x36334330 ]
@@ -791,9 +793,6 @@
 ## Mac ASAN ##
 
 
-# Flaky failure due to possible synchronization issues
-crbug.com/412679364 [ mac mac-arm64 ] conformance/textures/canvas/tex-2d-alpha-alpha-unsigned_byte.html [ RetryOnFailure ]
-
 ####################
 # Linux failures   #
 ####################
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
index 01f66e3..dedf32f 100644
--- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
+++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -1,7 +1,7 @@
 # BEGIN TAG HEADER (autogenerated, see validate_tag_consistency.py)
 # OS
 # tags: [ android android-oreo android-pie android-r android-s android-t
-#             android-14
+#             android-14 android-15 android-16
 #         chromeos
 #         fuchsia
 #         linux ubuntu
@@ -11,6 +11,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
+#         android-brya
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
@@ -37,7 +38,8 @@
 #         google google-0xffff google-0xc0de
 #         imagination
 #         intel intel-gen-9 intel-gen-12 intel-0xa2e intel-0xd26 intel-0xa011
-#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x5912 intel-0x9bc5
+#             intel-0x3e92 intel-0x3e9b intel-0x4680 intel-0x46a8 intel-0x5912
+#             intel-0x9bc5
 #         microsoft microsoft-0xffff
 #         nvidia nvidia-0xfe9 nvidia-0x1cb3 nvidia-0x2184 nvidia-0x2783
 #         qualcomm qualcomm-0x41333430 qualcomm-0x36333630 qualcomm-0x36334330 ]
@@ -499,7 +501,8 @@
 
 ## Vulkan / Win / NVIDIA / Passthrough command decoder ##
 
-crbug.com/40786644 [ win nvidia-0x2783 angle-vulkan passthrough ] conformance/glsl/bugs/pow-with-constant-exponent-should-not-crash.html [ Failure ]
+crbug.com/40786644 [ win11 nvidia angle-vulkan passthrough ] conformance/glsl/bugs/pow-with-constant-exponent-should-not-crash.html [ Failure ]
+crbug.com/415086850 [ win11 nvidia-0x2184 angle-vulkan passthrough ] conformance/ogles/GL/mod/mod_001_to_008.html [ Failure ]
 
 ## Win / Adreno 690 / D3D11
 crbug.com/1523062 [ win11 qualcomm-0x41333430 angle-d3d11 ] conformance/rendering/bind-framebuffer-flush-bug.html [ Failure ]
diff --git a/content/test/gpu/gpu_tests/trace_integration_test.py b/content/test/gpu/gpu_tests/trace_integration_test.py
index 221bae2f..6f56389 100644
--- a/content/test/gpu/gpu_tests/trace_integration_test.py
+++ b/content/test/gpu/gpu_tests/trace_integration_test.py
@@ -589,22 +589,21 @@
       self._RunActualGpuTraceTest(test_path, params)
     elif isinstance(params, _CacheTraceTestArguments):
       # Create a new temporary directory for each cache test that is run.
-      cache_profile_dir = tempfile.TemporaryDirectory()
+      with tempfile.TemporaryDirectory() as cache_profile_dir:
+        # Run the first load page and get the number of expected cache hits.
+        load_params = params.GenerateFirstLoadTest()
+        results =\
+          self._RunActualGpuTraceTest(test_path,
+                                      load_params,
+                                      profile_dir=cache_profile_dir,
+                                      profile_type='exact')
 
-      # Run the first load page and get the number of expected cache hits.
-      load_params = params.GenerateFirstLoadTest()
-      results =\
-        self._RunActualGpuTraceTest(test_path,
-                                    load_params,
-                                    profile_dir=cache_profile_dir.name,
-                                    profile_type='exact')
-
-      # Generate and run the cache hit tests using the seeded cache dir.
-      for (hit_path, trace_params) in params.GenerateCacheHitTests(results):
-        self._RunActualGpuTraceTest(hit_path,
-                                    trace_params,
-                                    profile_dir=cache_profile_dir.name,
-                                    profile_type='clean')
+        # Generate and run the cache hit tests using the seeded cache dir.
+        for (hit_path, trace_params) in params.GenerateCacheHitTests(results):
+          self._RunActualGpuTraceTest(hit_path,
+                                      trace_params,
+                                      profile_dir=cache_profile_dir,
+                                      profile_type='clean')
 
   @classmethod
   def SetUpProcess(cls) -> None:
diff --git a/content/test/gpu/validate_tag_consistency.py b/content/test/gpu/validate_tag_consistency.py
index fc0b4868..d32c7c7 100755
--- a/content/test/gpu/validate_tag_consistency.py
+++ b/content/test/gpu/validate_tag_consistency.py
@@ -33,6 +33,8 @@
             'android-s',
             'android-t',
             'android-14',
+            'android-15',
+            'android-16',
         ],
         'chromeos': [],
         'fuchsia': [],
@@ -106,6 +108,7 @@
             'intel-0x3e92',
             'intel-0x3e9b',
             'intel-0x4680',
+            'intel-0x46a8',
             'intel-0x5912',
             'intel-0x9bc5',
         ],
@@ -188,6 +191,7 @@
 # tags: [ android-nexus-5x android-pixel-2 android-pixel-4
 #             android-pixel-6 android-shield-android-tv android-sm-a137f
 #             android-sm-a236b android-sm-s911u1
+#         android-brya
 #         chromeos-board-amd64-generic chromeos-board-eve chromeos-board-jacuzzi
 #             chromeos-board-octopus chromeos-board-volteer
 #         fuchsia-board-astro fuchsia-board-nelson fuchsia-board-sherlock
diff --git a/content/utility/utility_main.cc b/content/utility/utility_main.cc
index 19ada6e..7ca43d0e 100644
--- a/content/utility/utility_main.cc
+++ b/content/utility/utility_main.cc
@@ -45,8 +45,6 @@
 #include "content/common/gpu_pre_sandbox_hook_linux.h"
 #include "content/public/common/content_descriptor_keys.h"
 #include "content/utility/speech/speech_recognition_sandbox_hook_linux.h"
-#include "media/gpu/sandbox/hardware_video_decoding_sandbox_hook_linux.h"
-#include "media/gpu/sandbox/hardware_video_encoding_sandbox_hook_linux.h"
 #include "sandbox/policy/linux/sandbox_linux.h"
 #include "services/audio/audio_sandbox_hook_linux.h"
 #include "services/network/network_sandbox_hook_linux.h"
@@ -54,6 +52,8 @@
 
 #if BUILDFLAG(USE_LINUX_VIDEO_ACCELERATION)
 #include "gpu/config/gpu_info_collector.h"
+#include "media/gpu/sandbox/hardware_video_decoding_sandbox_hook_linux.h"
+#include "media/gpu/sandbox/hardware_video_encoding_sandbox_hook_linux.h"
 // gn check is not smart enough to realize that this include is guarded behind
 // some BUILDFLAG()s and the BUILD.gn dependencies correctly account for that.
 #include "third_party/angle/src/gpu_info_util/SystemInfo.h"  //nogncheck
diff --git a/docs/android_emulator.md b/docs/android_emulator.md
index 91844dc4..448d980 100644
--- a/docs/android_emulator.md
+++ b/docs/android_emulator.md
@@ -229,9 +229,7 @@
 
 ### Using Your Own Emulator Image
 
-By far the easiest way to set up emulator images is to use Android Studio.
-If you don't have an [Android Studio project](android_studio.md) already, you
-can create a blank one to be able to reach the Virtual Device Manager screen.
+You can also set up emulator images in Android Studio.
 
 Refer to: https://developer.android.com/studio/run/managing-avds.html
 
@@ -240,52 +238,62 @@
  * Emulator configs and data partition images are stored within
    `~/.android/avd/`.
 
+To get started, launch any Android Studio Project. If you don't have an [Android
+Studio project](android_studio.md) already, you can create a blank one. Go to
+the menu on the right-hand side of the window and click to launch the Device
+Manager. In the Device Manager window, click the "+" icon and **Create Virtual
+Device**.
+
 #### Creating an Image
 
-##### Choosing a Skin
+You'll see a long list of Pixel phone names. You need to 2 decisions here:
 
-Choose a skin with a small screen for better performance (unless you care about
-testing large screens).
+1. Which form factor (phone/tablet/etc.)? If you're not sure, a **phone** target
+   is usually fine for most development.
+2. Which device model skin? Any Pixel skin is usually sufficient, but pay
+   attention to the "API" column if you need a specific OS version. You can
+   refer to
+   https://developer.android.com/guide/topics/manifest/uses-sdk-element.html to
+   convert between API numbers and the OS release number (ex. API 34 refers to
+   the Android 14 release).
 
-##### Choosing an Image
+##### Device settings
 
-Android Studio's image labels roughly translate to the following:
+You need to choose 4 things on the "Device" tab:
 
-| AVD "Target" | Virtual Device Configuration tab | GMS? | Build Properties |
-| --- | --- | --- | --- |
-| Google Play | "Recommended" (the default tab) | This has GMS | `user`/`release-keys` |
-| Google APIs | "x86 Images" | This has GMS | `userdebug`/`dev-keys` |
-| No label | "x86 Images" | AOSP image, does not have GMS | `eng`/`test-keys` |
+1. Name: give this emulator a name to help you remember what this emulator is
+   for (ex. "Android 15").
+2. API: choose an API version (Android operating system version) for the emulator.
+3. Services: you'll probably want to choose **Google APIs** for most
+   development, since this gives you a `userdebug` emulator with GMS Core APIs
+   installed. See the table below if you need something different.
+4. System image: select "Google APIs Intel x86\_64" (or whichever row has the
+   ⭐ icon)
 
-*** promo
-**Tip:** if you're not sure which to use, choose **Google APIs** under the **x86
-Images** tab in the Virtual Device Configuration wizard.
-***
+Choosing different **Services** for the emulator:
 
-##### Configuration
+| Services |  GMS? | Build Properties |
+| --- | --- | --- |
+| Google Play | This has GMS and the Play Store | `user`/`release-keys` |
+| Google APIs | This has GMS | `userdebug`/`dev-keys` |
+| Android Open Source | AOSP image, does not have GMS | `eng`/`test-keys` |
 
-"Show Advanced Settings" > scroll down:
-* Set internal storage to 4000MB (component builds are really big).
-* Set SD card to 1000MB (our tests push a lot of files to /sdcard).
+![Device settings](/docs/images/android_device_manager_device_tab.png)
 
-##### Known Issues
+##### Additional settings
 
- * Our test & installer scripts do not work with pre-MR1 Jelly Bean.
- * Component builds do not work on pre-KitKat (due to the OS having a max
-   number of shared libraries).
- * Jelly Bean and KitKat images sometimes forget to mount /sdcard :(.
-   * This causes tests to fail.
-   * To ensure it's there: `adb -s emulator-5554 shell mount` (look for /sdcard)
-   * Can often be fixed by editing `~/.android/avd/YOUR_DEVICE/config.ini`.
-     * Look for `hw.sdCard=no` and set it to `yes`
- * The "Google APIs" Android L and M emulator images are configured to expect
-   the "AOSP" WebView package (`com.android.webview`). This does not resemble
-   production devices with GMS, which expect the ["Google WebView"
-   configuration](/android_webview/docs/webview-providers.md#webview-provider-options)
-   (`com.google.android.webview` on L and M). See [Removing preinstalled
-   WebView](/android_webview/docs/build-instructions.md#Removing-preinstalled-WebView)
-   if you need to install a local build or official build.
+1. Internal storage: this needs to be at least 4000 MB (4 GB)
+   (component builds are really big).
+2. Expanded storage: this needs to be at least 1000 MB (1 GB)
+   (our tests push a lot of files to /sdcard).
 
+After you have configured everything, you can click "Finish" to create the AVD
+(emulator).
+After it's been created, you can launch the AVD from Device Manager by clicking
+the play button or you can launch it from the commandline (see instructions
+below).
+
+![Additional settings](/docs/images/android_device_manager_additional_settings_tab.png)
 
 #### Starting an Emulator from the Command Line
 
diff --git a/docs/images/android_device_manager_additional_settings_tab.png b/docs/images/android_device_manager_additional_settings_tab.png
new file mode 100644
index 0000000..df2ed166
--- /dev/null
+++ b/docs/images/android_device_manager_additional_settings_tab.png
Binary files differ
diff --git a/docs/images/android_device_manager_device_tab.png b/docs/images/android_device_manager_device_tab.png
new file mode 100644
index 0000000..6536976
--- /dev/null
+++ b/docs/images/android_device_manager_device_tab.png
Binary files differ
diff --git a/docs/testing/run_web_platform_tests_on_android.md b/docs/testing/run_web_platform_tests_on_android.md
index 3f47baa..fe8f134 100644
--- a/docs/testing/run_web_platform_tests_on_android.md
+++ b/docs/testing/run_web_platform_tests_on_android.md
@@ -117,11 +117,8 @@
 
 ## Test expectations and Baselines
 
-The
-[MobileTestExpectations](../../third_party/blink/web_tests/MobileTestExpectations)
-file contains the list of all known Chrome Android and Chrome WebView specific
-test failures, and it inherits or overrides test expectations from the default
-[TestExpectations](../../third_party/blink/web_tests/TestExpectations) file.
+Expected failures on Chrome Android or WebView should be added to the default
+[TestExpectations](../../third_party/blink/web_tests/TestExpectations) file with the modifier "Android" or "Webview" respectively.
 
 For baselines:
 * Chrome Android specific baselines reside at
diff --git a/docs/testing/web_test_expectations.md b/docs/testing/web_test_expectations.md
index 6ef74c26..3fcdb25 100644
--- a/docs/testing/web_test_expectations.md
+++ b/docs/testing/web_test_expectations.md
@@ -217,8 +217,6 @@
   Tests that fail under Chrome for Testing
 * [LeakExpectations](../../third_party/blink/web_tests/LeakExpectations):
   Tests that have memory leaks under the leak checker.
-* [MobileTestExpectations](../../third_party/blink/web_tests/MobileTestExpectations)
-  Tests that fails under Chrome Android and Chrome WebView platform.
 * [MSANExpectations](../../third_party/blink/web_tests/MSANExpectations):
   Tests that fail under MSAN.
 * [NeverFixTests](../../third_party/blink/web_tests/NeverFixTests): Tests
@@ -284,8 +282,9 @@
   applicable to that file.
 * If specified, modifiers can be one of `Fuchsia`, `Mac`, `Mac11`,
   `Mac11-arm64`, `Mac12`, `Mac12-arm64`, `Mac13`, `Mac13-arm64`, `Mac14`,
-  `Mac14-arm64`, `Mac15`, `Mac15-arm64`, `Linux`, `Chrome`, `Win`, `Win10.20h2`,
-  `Win11`, `iOS17-Simulator`, and, optionally, `Release`, or `Debug`.
+  `Mac14-arm64`, `Mac15`, `Mac15-arm64`, `Linux`, `Win`, `Win10.20h2`,
+  `Win11`, `Win11-arm64`, `Android`, `Webview`, `iOS17-Simulator`, and,
+  optionally, `Release`, or `Debug`.
   Check the `# tags: ...` comments [at the top of each
   file](/third_party/blink/web_tests/TestExpectations#1) to see which modifiers
   that file supports.
diff --git a/docs/use_counter_wiki.md b/docs/use_counter_wiki.md
index 3b9fbdb..168110f 100644
--- a/docs/use_counter_wiki.md
+++ b/docs/use_counter_wiki.md
@@ -156,63 +156,44 @@
 
 ### UseCounter Feature in HTTP Archive
 
-HTTP Archive crawls the top 10K sites on the web and records everything from
+HTTP Archive crawls the top sites on the web and records everything from
 request and response headers. The data is available on Google BigQuery.
 
-You can find pages that trigger a particular UseCounter using the following
-script:
+You can find usage and sample pages that trigger a particular UseCounter using
+the following script:
 
 ```sql
 SELECT
-  DATE(yyyymmdd) AS date,
+  date,
   client AS platform,
-  num_url AS url_count,
-  pct_urls AS urls_percentile,
+  num_urls AS url_count,
+  pct_urls AS urls_percent,
   sample_urls AS url
-FROM [httparchive:blink_features.usage]
-WHERE feature = 'MyFeature'
-ORDER BY url_percentile DESC
+FROM `httparchive.blink_features.usage`
+WHERE
+  feature = 'MyFeature' AND
+  date = (SELECT MAX(date) FROM `httparchive.blink_features.usage`)
+ORDER BY date DESC
 ```
-OR
+
+
+Or to see or filter my more data available in the HTTP Archive you can query the main `httparchive.crawl.pages` table like this:
 
 ```sql
-SELECT
-  url
-FROM [httparchive:pages.yyyy_mm_dd_mobile]
+SELECT DISTINCT
+  client,
+  page,
+  rank
+FROM
+  `httparchive.crawl.pages`,
+  UNNEST (features) As feats
 WHERE
-  JSON_EXTRACT(payload, '$._blinkFeatureFirstUsed.Features.MyFeature') IS NOT
-  NULL
-LIMIT 500
+  date = '2024-11-01' AND     -- update date to latest month
+  feats.feature = 'MyFeature' -- update feature
+ORDER BY
+  rank
 ```
 
-You can also find pages that trigger a particular CSS property (during parsing):
-
-```sql
-SELECT
-  url
-FROM [httparchive:pages.yyyy_mm_dd_mobile]
-WHERE
-  JSON_EXTRACT(payload, '$._blinkFeatureFirstUsed.CSSFeatures.MyCSSProperty')
-  IS NOT NULL
-LIMIT 500
-```
-
-To find pages that trigger a UseCounter and sort by page rank:
-
-```sql
-SELECT
-  IFNULL(runs.rank, 1000000) AS rank,
-  har.url AS url,
-FROM [httparchive:latest.pages_desktop] AS har
-LEFT JOIN [httparchive:runs.latest_pages] AS runs
-  ON har.url = runs.url
-WHERE
-  JSON_EXTRACT(payload, '$._blinkFeatureFirstUsed.Features.MyFeature') IS NOT
-  NULL
-ORDER BY rank;
-```
-
-
 ### UMA Usage on Fraction of Users
 You may also see the fraction of users that trigger your feature at lease once a
 day on [UMA Usage dashboard](https://goto.google.com/uma-usecounter-peruser).
diff --git a/docs/website b/docs/website
index b2558d3..406e2b2 160000
--- a/docs/website
+++ b/docs/website
@@ -1 +1 @@
-Subproject commit b2558d301fc88f8e4671024c47960ff1ee82cf34
+Subproject commit 406e2b2832f3d734a2ba4f8fa93fb78deecb16d2
diff --git a/extensions/browser/api/power/BUILD.gn b/extensions/browser/api/power/BUILD.gn
index 279c3aae..35ab8d7c 100644
--- a/extensions/browser/api/power/BUILD.gn
+++ b/extensions/browser/api/power/BUILD.gn
@@ -16,15 +16,18 @@
 
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
-  deps = [
-    "//content/public/browser",
-    "//content/public/common",
+  public_deps = [
+    "//base",
+    "//build:chromeos_buildflags",
+    "//extensions/browser:browser_sources",
     "//extensions/common",
     "//extensions/common/api",
     "//mojo/public/cpp/bindings",
     "//services/device/public/mojom",
   ]
 
+  deps = [ "//content/public/browser" ]
+
   if (is_chromeos) {
     sources += [
       "activity_reporter_delegate.cc",
@@ -33,8 +36,6 @@
       "activity_reporter_delegate_ash.h",
     ]
 
-    deps += [ "//build:chromeos_buildflags" ]
+    deps += [ "//ui/base" ]
   }
-
-  public_deps = [ "//extensions/browser:browser_sources" ]
 }
diff --git a/extensions/browser/api/runtime/BUILD.gn b/extensions/browser/api/runtime/BUILD.gn
index ea4fe0bf..8851d80 100644
--- a/extensions/browser/api/runtime/BUILD.gn
+++ b/extensions/browser/api/runtime/BUILD.gn
@@ -17,13 +17,20 @@
 
   configs += [ "//build/config/compiler:wexit_time_destructors" ]
 
-  deps = [
-    "//components/prefs",
-    "//components/sessions",
-    "//content/public/browser",
+  public_deps = [
+    "//base",
+    "//extensions/browser:browser_sources",
     "//extensions/common",
     "//extensions/common/api",
   ]
 
-  public_deps = [ "//extensions/browser:browser_sources" ]
+  deps = [
+    "//components/prefs",
+    "//components/sessions",
+    "//content/public/browser",
+    "//extensions/common:common_constants",
+    "//extensions/common:mojom",
+    "//storage/browser",
+    "//url",
+  ]
 }
diff --git a/extensions/browser/json_file_sanitizer.cc b/extensions/browser/json_file_sanitizer.cc
index 7a17c45..df0dc36 100644
--- a/extensions/browser/json_file_sanitizer.cc
+++ b/extensions/browser/json_file_sanitizer.cc
@@ -6,145 +6,107 @@
 
 #include <optional>
 #include <string>
+#include <utility>
 
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
 #include "base/json/json_reader.h"
 #include "base/json/json_writer.h"
 #include "base/task/sequenced_task_runner.h"
+#include "base/types/expected.h"
+#include "base/values.h"
 #include "extensions/browser/extension_file_task_runner.h"
-#include "services/data_decoder/public/cpp/data_decoder.h"
 
 namespace extensions {
 
-namespace {
-
-// Reads the file in |path| and then deletes it.
-// Returns a tuple containing: the file content, whether the read was
-// successful, whether the delete was successful.
-std::tuple<std::string, bool, bool> ReadAndDeleteTextFile(
-    const base::FilePath& path) {
-  std::string contents;
-  bool read_success = base::ReadFileToString(path, &contents);
-  bool delete_success = base::DeleteFile(path);
-  return std::make_tuple(contents, read_success, delete_success);
-}
-
-bool WriteStringToFile(const std::string& contents,
-                       const base::FilePath& file_path) {
-  return base::WriteFile(file_path, contents);
-}
-
-}  // namespace
-
 // static
 std::unique_ptr<JsonFileSanitizer> JsonFileSanitizer::CreateAndStart(
-    data_decoder::DataDecoder* decoder,
     const std::set<base::FilePath>& file_paths,
     Callback callback,
     const scoped_refptr<base::SequencedTaskRunner>& io_task_runner) {
   // Note we can't use std::make_unique as we want to keep the constructor
   // private.
   std::unique_ptr<JsonFileSanitizer> sanitizer(
-      new JsonFileSanitizer(file_paths, std::move(callback), io_task_runner));
-  sanitizer->Start(decoder);
+      new JsonFileSanitizer(std::move(callback), io_task_runner));
+  sanitizer->Start(file_paths);
   return sanitizer;
 }
 
 JsonFileSanitizer::JsonFileSanitizer(
-    const std::set<base::FilePath>& file_paths,
     Callback callback,
     const scoped_refptr<base::SequencedTaskRunner>& io_task_runner)
-    : file_paths_(file_paths),
-      callback_(std::move(callback)),
-      io_task_runner_(io_task_runner) {}
+    : callback_(std::move(callback)), io_task_runner_(io_task_runner) {}
 
 JsonFileSanitizer::~JsonFileSanitizer() = default;
 
-void JsonFileSanitizer::Start(data_decoder::DataDecoder* decoder) {
-  if (file_paths_.empty()) {
+void JsonFileSanitizer::Start(const std::set<base::FilePath>& file_paths) {
+  if (file_paths.empty()) {
     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
         FROM_HERE, base::BindOnce(&JsonFileSanitizer::ReportSuccess,
                                   weak_factory_.GetWeakPtr()));
     return;
   }
 
-  decoder->GetService()->BindJsonParser(
-      json_parser_.BindNewPipeAndPassReceiver());
-
-  for (const base::FilePath& path : file_paths_) {
+  remaining_callbacks_ = file_paths.size();
+  for (const base::FilePath& path : file_paths) {
     io_task_runner_->PostTaskAndReplyWithResult(
-        FROM_HERE, base::BindOnce(&ReadAndDeleteTextFile, path),
-        base::BindOnce(&JsonFileSanitizer::JsonFileRead,
-                       weak_factory_.GetWeakPtr(), path));
+        FROM_HERE, base::BindOnce(&JsonFileSanitizer::ProcessFile, path),
+        base::BindOnce(&JsonFileSanitizer::OnProcessedFile,
+                       weak_factory_.GetWeakPtr()));
   }
 }
 
-void JsonFileSanitizer::JsonFileRead(
-    const base::FilePath& file_path,
-    std::tuple<std::string, bool, bool> read_and_delete_result) {
-  if (!std::get<1>(read_and_delete_result)) {
-    ReportError(Status::kFileReadError, std::string());
-    return;
-  }
-  if (!std::get<2>(read_and_delete_result)) {
-    ReportError(Status::kFileDeleteError, std::string());
-    return;
-  }
-  json_parser_->Parse(std::get<0>(read_and_delete_result),
-                      base::JSON_PARSE_CHROMIUM_EXTENSIONS,
-                      base::BindOnce(&JsonFileSanitizer::JsonParsingDone,
-                                     weak_factory_.GetWeakPtr(), file_path));
-}
+base::expected<void, JsonFileSanitizer::Error> JsonFileSanitizer::ProcessFile(
+    const base::FilePath& path) {
+  std::string contents;
+  bool read_success = base::ReadFileToString(path, &contents);
+  bool delete_success = base::DeleteFile(path);
 
-void JsonFileSanitizer::JsonParsingDone(
-    const base::FilePath& file_path,
-    std::optional<base::Value> json_value,
-    const std::optional<std::string>& error) {
-  if (!json_value || !json_value->is_dict()) {
-    ReportError(Status::kDecodingError, error ? *error : std::string());
-    return;
+  if (!read_success) {
+    return base::unexpected(Error::kFileReadError);
+  }
+
+  if (!delete_success) {
+    return base::unexpected(Error::kFileDeleteError);
+  }
+
+  std::optional<base::Value> result = base::JSONReader::Read(contents);
+  if (!result.has_value() || !result->is_dict()) {
+    return base::unexpected(Error::kDecodingError);
   }
 
   // Reserialize the JSON and write it back to the original file.
   std::optional<std::string> json_string = base::WriteJsonWithOptions(
-      *json_value, base::JSONWriter::OPTIONS_PRETTY_PRINT);
+      *result, base::JSONWriter::OPTIONS_PRETTY_PRINT);
   if (!json_string) {
-    ReportError(Status::kSerializingError, std::string());
-    return;
+    return base::unexpected(Error::kSerializingError);
   }
 
-  io_task_runner_->PostTaskAndReplyWithResult(
-      FROM_HERE,
-      base::BindOnce(&WriteStringToFile, std::move(*json_string), file_path),
-      base::BindOnce(&JsonFileSanitizer::JsonFileWritten,
-                     weak_factory_.GetWeakPtr(), file_path));
+  if (!base::WriteFile(path, *json_string)) {
+    return base::unexpected(Error::kFileWriteError);
+  }
+
+  return base::ok();
 }
 
-void JsonFileSanitizer::JsonFileWritten(const base::FilePath& file_path,
-                                        bool success) {
-  if (!success) {
-    ReportError(Status::kFileWriteError, std::string());
-    return;
-  }
-  // We have finished with this JSON file.
-  size_t removed_count = file_paths_.erase(file_path);
-  DCHECK_EQ(1U, removed_count);
-
-  if (file_paths_.empty()) {
-    // This was the last path, we are done.
-    ReportSuccess();
+void JsonFileSanitizer::OnProcessedFile(base::expected<void, Error> result) {
+  if (result.has_value()) {
+    if (--remaining_callbacks_ == 0) {
+      ReportSuccess();
+    }
+  } else {
+    ReportError(result.error());
   }
 }
 
 void JsonFileSanitizer::ReportSuccess() {
-  std::move(callback_).Run(Status::kSuccess, std::string());
+  std::move(callback_).Run(base::ok());
 }
 
-void JsonFileSanitizer::ReportError(Status status, const std::string& error) {
+void JsonFileSanitizer::ReportError(Error error) {
   // Prevent any other task from reporting, we want to notify only once.
   weak_factory_.InvalidateWeakPtrs();
-  std::move(callback_).Run(status, error);
+  std::move(callback_).Run(base::unexpected(error));
 }
 
 }  // namespace extensions
diff --git a/extensions/browser/json_file_sanitizer.h b/extensions/browser/json_file_sanitizer.h
index 1d1a59d..f592d49 100644
--- a/extensions/browser/json_file_sanitizer.h
+++ b/extensions/browser/json_file_sanitizer.h
@@ -9,18 +9,13 @@
 #include <optional>
 #include <set>
 #include <string>
-#include <tuple>
 
 #include "base/files/file_path.h"
 #include "base/memory/weak_ptr.h"
 #include "base/task/sequenced_task_runner.h"
+#include "base/types/expected.h"
 #include "base/values.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "services/data_decoder/public/mojom/json_parser.mojom.h"
-
-namespace data_decoder {
-class DataDecoder;
-}
 
 namespace extensions {
 
@@ -32,8 +27,7 @@
 // is not the case.
 class JsonFileSanitizer {
  public:
-  enum class Status {
-    kSuccess = 0,
+  enum class Error {
     kFileReadError,
     kFileDeleteError,
     kDecodingError,
@@ -41,23 +35,17 @@
     kFileWriteError,
   };
 
-  // Callback invoked when the JSON sanitization is is done. If status is an
-  // error, `error_msg` contains the error message.
-  using Callback =
-      base::OnceCallback<void(Status status, const std::string& error_msg)>;
+  // Callback invoked when the JSON sanitization is is done.
+  using Callback = base::OnceCallback<void(base::expected<void, Error>)>;
 
   // Creates a JsonFileSanitizer and starts the sanitization of the JSON files
   // in `file_paths`.
-  // `decoder` should be a DataDecoder which can be used to talk to a Data
-  // Decoder service instance. It must be live on the calling sequence and
-  // it is not retained beyond the extent of this call.
   // `callback` is invoked asynchronously when all JSON files have been
   // sanitized or if an error occurred.
   // If the returned JsonFileSanitizer instance is deleted before `callback` was
   // invoked, then `callback` is never invoked and the sanitization stops
   // promptly (some background tasks may still run).
   static std::unique_ptr<JsonFileSanitizer> CreateAndStart(
-      data_decoder::DataDecoder* decoder,
       const std::set<base::FilePath>& file_paths,
       Callback callback,
       const scoped_refptr<base::SequencedTaskRunner>& io_task_runner);
@@ -69,29 +57,22 @@
 
  private:
   JsonFileSanitizer(
-      const std::set<base::FilePath>& file_paths,
       Callback callback,
       const scoped_refptr<base::SequencedTaskRunner>& io_task_runner);
 
-  void Start(data_decoder::DataDecoder* decoder);
+  void Start(const std::set<base::FilePath>& file_paths);
 
-  void JsonFileRead(const base::FilePath& file_path,
-                    std::tuple<std::string, bool, bool> read_and_delete_result);
+  // Note: unlike all other methods, this executes on `io_task_runner_`.
+  static base::expected<void, Error> ProcessFile(const base::FilePath& path);
 
-  void JsonParsingDone(const base::FilePath& file_path,
-                       std::optional<base::Value> json_value,
-                       const std::optional<std::string>& error);
-
-  void JsonFileWritten(const base::FilePath& file_path, bool success);
-
+  void OnProcessedFile(base::expected<void, Error> result);
   void ReportSuccess();
+  void ReportError(Error error);
 
-  void ReportError(Status status, const std::string& path);
-
-  std::set<base::FilePath> file_paths_;
+  size_t remaining_callbacks_ = 0;
   Callback callback_;
   scoped_refptr<base::SequencedTaskRunner> io_task_runner_;
-  mojo::Remote<data_decoder::mojom::JsonParser> json_parser_;
+
   base::WeakPtrFactory<JsonFileSanitizer> weak_factory_{this};
 };
 
diff --git a/extensions/browser/json_file_sanitizer_unittest.cc b/extensions/browser/json_file_sanitizer_unittest.cc
index 7d493b3..becd8186 100644
--- a/extensions/browser/json_file_sanitizer_unittest.cc
+++ b/extensions/browser/json_file_sanitizer_unittest.cc
@@ -12,12 +12,15 @@
 #include "base/functional/bind.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/test/gmock_expected_support.h"
+#include "base/types/expected.h"
 #include "content/public/test/browser_task_environment.h"
 #include "extensions/browser/extension_file_task_runner.h"
-#include "services/data_decoder/public/cpp/data_decoder.h"
-#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using ::base::test::ErrorIs;
+using ::base::test::HasValue;
+
 namespace extensions {
 
 namespace {
@@ -55,35 +58,28 @@
 
   void CreateAndStartSanitizer(const std::set<base::FilePath>& file_paths) {
     sanitizer_ = JsonFileSanitizer::CreateAndStart(
-        &data_decoder_, file_paths,
+        file_paths,
         base::BindOnce(&JsonFileSanitizerTest::SanitizationDone,
                        base::Unretained(this)),
         GetExtensionFileTaskRunner());
   }
 
-  JsonFileSanitizer::Status last_reported_status() const {
+  base::expected<void, JsonFileSanitizer::Error> last_reported_status() const {
     return last_status_;
   }
 
-  const std::string& last_reported_error() const { return last_error_; }
-
  private:
   void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
 
-  void SanitizationDone(JsonFileSanitizer::Status status,
-                        const std::string& error_msg) {
+  void SanitizationDone(base::expected<void, JsonFileSanitizer::Error> status) {
     last_status_ = status;
-    last_error_ = error_msg;
     if (done_callback_) {
       std::move(done_callback_).Run();
     }
   }
 
   content::BrowserTaskEnvironment task_environment_;
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
-  data_decoder::DataDecoder data_decoder_;
-  JsonFileSanitizer::Status last_status_;
-  std::string last_error_;
+  base::expected<void, JsonFileSanitizer::Error> last_status_;
   base::OnceClosure done_callback_;
   std::unique_ptr<JsonFileSanitizer> sanitizer_;
   base::ScopedTempDir temp_dir_;
@@ -94,8 +90,7 @@
 TEST_F(JsonFileSanitizerTest, NoFilesProvided) {
   CreateAndStartSanitizer(std::set<base::FilePath>());
   WaitForSanitizationDone();
-  EXPECT_EQ(last_reported_status(), JsonFileSanitizer::Status::kSuccess);
-  EXPECT_TRUE(last_reported_error().empty());
+  EXPECT_THAT(last_reported_status(), HasValue());
 }
 
 TEST_F(JsonFileSanitizerTest, ValidCase) {
@@ -113,8 +108,7 @@
   }
   CreateAndStartSanitizer(paths);
   WaitForSanitizationDone();
-  EXPECT_EQ(last_reported_status(), JsonFileSanitizer::Status::kSuccess);
-  EXPECT_TRUE(last_reported_error().empty());
+  EXPECT_THAT(last_reported_status(), HasValue());
   // Make sure the JSON files are there and non empty.
   for (const auto& path : paths) {
     std::optional<int64_t> file_size = base::GetFileSize(path);
@@ -133,7 +127,8 @@
   base::FilePath invalid_path = CreateFilePath(kNonExistingName);
   CreateAndStartSanitizer({good_path, invalid_path});
   WaitForSanitizationDone();
-  EXPECT_EQ(last_reported_status(), JsonFileSanitizer::Status::kFileReadError);
+  EXPECT_THAT(last_reported_status(),
+              ErrorIs(JsonFileSanitizer::Error::kFileReadError));
 }
 
 TEST_F(JsonFileSanitizerTest, InvalidJson) {
@@ -147,8 +142,8 @@
   CreateInvalidJsonFile(badd_path);
   CreateAndStartSanitizer({good_path, badd_path});
   WaitForSanitizationDone();
-  EXPECT_EQ(last_reported_status(), JsonFileSanitizer::Status::kDecodingError);
-  EXPECT_FALSE(last_reported_error().empty());
+  EXPECT_THAT(last_reported_status(),
+              ErrorIs(JsonFileSanitizer::Error::kDecodingError));
 }
 
 }  // namespace extensions
diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc
index 895ad5f..dd94c82 100644
--- a/extensions/browser/sandboxed_unpacker.cc
+++ b/extensions/browser/sandboxed_unpacker.cc
@@ -43,6 +43,7 @@
 #include "extensions/browser/install/crx_install_error.h"
 #include "extensions/browser/install/sandboxed_unpacker_failure_reason.h"
 #include "extensions/browser/install_stage.h"
+#include "extensions/browser/json_file_sanitizer.h"
 #include "extensions/browser/ruleset_parse_result.h"
 #include "extensions/browser/verified_contents.h"
 #include "extensions/browser/zipfile_installer.h"
@@ -179,7 +180,6 @@
   void CleanUp() {
     image_sanitizer_.reset();
     json_file_sanitizer_.reset();
-    json_parser_.reset();
   }
 
   data_decoder::DataDecoder* GetDataDecoder() { return &data_decoder_; }
@@ -197,48 +197,24 @@
   }
 
   void CreateJsonFileSanitizer(
-      const std::set<base::FilePath>& message_catalog_paths,
+      std::set<base::FilePath> message_catalog_paths,
       JsonFileSanitizer::Callback callback,
       const scoped_refptr<base::SequencedTaskRunner>& unpacker_io_task_runner) {
     json_file_sanitizer_ = JsonFileSanitizer::CreateAndStart(
-        GetDataDecoder(), message_catalog_paths, std::move(callback),
+        std::move(message_catalog_paths), std::move(callback),
         unpacker_io_task_runner);
   }
 
-  data_decoder::mojom::JsonParser* GetJsonParserPtr(
-      SandboxedUnpacker* unpacker) {
-    if (!json_parser_) {
-      data_decoder_.GetService()->BindJsonParser(
-          json_parser_.BindNewPipeAndPassReceiver());
-      json_parser_.set_disconnect_handler(base::BindOnce(
-          &SandboxedUnpacker::ReportFailure, unpacker,
-          SandboxedUnpackerFailureReason::
-              UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL,
-          l10n_util::GetStringFUTF16(
-              IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
-              u"UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL") +
-              u". " +
-              l10n_util::GetStringUTF16(
-                  IDS_EXTENSION_INSTALL_PROCESS_CRASHED)));
-    }
-
-    return json_parser_.get();
-  }
-
  private:
   // Controls our own lazily started, isolated instance of the Data Decoder
   // service so that multiple decode operations related to this
-  // SandboxedUnpacker can share a single instance.
+  // SandboxedUnpacker can share a single instance. Only used for image
+  // sanitization.
   data_decoder::DataDecoder data_decoder_;
 
-  // The JSONParser remote from the data decoder service.
-  mojo::Remote<data_decoder::mojom::JsonParser> json_parser_;
-
   // The ImageSanitizer used to clean-up images.
   std::unique_ptr<ImageSanitizer> image_sanitizer_;
 
-  // Used during the message catalog rewriting phase to sanitize the extension
-  // provided message catalogs.
   std::unique_ptr<JsonFileSanitizer> json_file_sanitizer_;
 };
 
@@ -516,27 +492,27 @@
 
   base::FilePath manifest_path = extension_root_.Append(kManifestFilename);
 
-  ParseJsonFile(manifest_path,
-                base::BindOnce(&SandboxedUnpacker::ReadManifestDone, this));
+  // This calls `ReadManifestDone()` on completion.
+  ParseJsonFile(manifest_path);
 }
 
 void SandboxedUnpacker::ReadManifestDone(
-    std::optional<base::Value> manifest,
-    const std::optional<std::string>& error) {
+    base::expected<base::Value, std::string> result) {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
-  if (error) {
-    ReportUnpackExtensionFailed(*error);
+  if (!result.has_value()) {
+    ReportUnpackExtensionFailed(result.error());
     return;
   }
-  if (!manifest || !manifest->is_dict()) {
+  const base::Value::Dict* dict = result->GetIfDict();
+  if (!dict) {
     ReportUnpackExtensionFailed(manifest_errors::kInvalidManifest);
     return;
   }
 
   std::string error_msg;
   scoped_refptr<Extension> extension(
-      Extension::Create(extension_root_, location_, manifest->GetDict(),
-                        creation_flags_, extension_id_, &error_msg));
+      Extension::Create(extension_root_, location_, *dict, creation_flags_,
+                        extension_id_, &error_msg));
   if (!extension) {
     ReportUnpackExtensionFailed(error_msg);
     return;
@@ -549,7 +525,7 @@
   }
   extension->AddInstallWarnings(std::move(warnings));
 
-  UnpackExtensionSucceeded(std::move(manifest.value()).TakeDict());
+  UnpackExtensionSucceeded(std::move(result).value().TakeDict());
 }
 
 void SandboxedUnpacker::UnpackExtensionSucceeded(base::Value::Dict manifest) {
@@ -680,8 +656,7 @@
 void SandboxedUnpacker::ReadMessageCatalogs() {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
   if (LocaleInfo::GetDefaultLocale(extension_.get()).empty()) {
-    MessageCatalogsSanitized(JsonFileSanitizer::Status::kSuccess,
-                             std::string());
+    MessageCatalogsSanitized(base::ok());
     return;
   }
 
@@ -698,6 +673,7 @@
 void SandboxedUnpacker::SanitizeMessageCatalogs(
     const std::set<base::FilePath>& message_catalog_paths) {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
+
   io_thread_state_->CreateJsonFileSanitizer(
       message_catalog_paths,
       base::BindOnce(&SandboxedUnpacker::MessageCatalogsSanitized, this),
@@ -705,10 +681,9 @@
 }
 
 void SandboxedUnpacker::MessageCatalogsSanitized(
-    JsonFileSanitizer::Status status,
-    const std::string& error_msg) {
+    base::expected<void, JsonFileSanitizer::Error> result) {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
-  if (status == JsonFileSanitizer::Status::kSuccess) {
+  if (result.has_value()) {
     IndexAndPersistJSONRulesetsIfNeeded();
     return;
   }
@@ -716,27 +691,25 @@
   SandboxedUnpackerFailureReason failure_reason =
       SandboxedUnpackerFailureReason::UNPACKER_CLIENT_FAILED;
   std::u16string error;
-  switch (status) {
-    case JsonFileSanitizer::Status::kFileReadError:
-    case JsonFileSanitizer::Status::kDecodingError:
+  switch (result.error()) {
+    case JsonFileSanitizer::Error::kFileReadError:
+    case JsonFileSanitizer::Error::kDecodingError:
       failure_reason = SandboxedUnpackerFailureReason::INVALID_CATALOG_DATA;
       error = l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
                                          u"INVALID_CATALOG_DATA");
       break;
-    case JsonFileSanitizer::Status::kSerializingError:
+    case JsonFileSanitizer::Error::kSerializingError:
       failure_reason =
           SandboxedUnpackerFailureReason::ERROR_SERIALIZING_CATALOG;
       error = l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
                                          u"ERROR_SERIALIZING_CATALOG");
       break;
-    case JsonFileSanitizer::Status::kFileDeleteError:
-    case JsonFileSanitizer::Status::kFileWriteError:
+    case JsonFileSanitizer::Error::kFileDeleteError:
+    case JsonFileSanitizer::Error::kFileWriteError:
       failure_reason = SandboxedUnpackerFailureReason::ERROR_SAVING_CATALOG;
       error = l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
                                          u"ERROR_SAVING_CATALOG");
       break;
-    default:
-      NOTREACHED();
   }
 
   ReportFailure(failure_reason, error);
@@ -817,11 +790,6 @@
   ReportSuccess();
 }
 
-data_decoder::mojom::JsonParser* SandboxedUnpacker::GetJsonParserPtr() {
-  DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
-  return io_thread_state_->GetJsonParserPtr(this);
-}
-
 void SandboxedUnpacker::ReportUnpackExtensionFailed(std::string_view error) {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
   ReportFailure(SandboxedUnpackerFailureReason::UNPACKER_CLIENT_FAILED,
@@ -1082,20 +1050,18 @@
   io_thread_state_->CleanUp();
 }
 
-void SandboxedUnpacker::ParseJsonFile(
-    const base::FilePath& path,
-    data_decoder::mojom::JsonParser::ParseCallback callback) {
+void SandboxedUnpacker::ParseJsonFile(const base::FilePath& path) {
   DCHECK(unpacker_io_task_runner_->RunsTasksInCurrentSequence());
   std::string contents;
   if (!base::ReadFileToString(path, &contents)) {
-    std::move(callback).Run(
-        /*value=*/std::nullopt,
-        /*error=*/std::optional<std::string>("File doesn't exist."));
+    ReadManifestDone(base::unexpected("File doesn't exist."));
     return;
   }
 
-  GetJsonParserPtr()->Parse(contents, base::JSON_PARSE_CHROMIUM_EXTENSIONS,
-                            std::move(callback));
+  base::JSONReader::Result result =
+      base::JSONReader::ReadAndReturnValueWithError(contents);
+  ReadManifestDone(std::move(result).transform_error(
+      [](const base::JSONReader::Error& error) { return error.ToString(); }));
 }
 
 }  // namespace extensions
diff --git a/extensions/browser/sandboxed_unpacker.h b/extensions/browser/sandboxed_unpacker.h
index 27776ca..329495b 100644
--- a/extensions/browser/sandboxed_unpacker.h
+++ b/extensions/browser/sandboxed_unpacker.h
@@ -15,6 +15,7 @@
 #include "base/memory/ref_counted_delete_on_sequence.h"
 #include "base/memory/scoped_refptr.h"
 #include "base/memory/weak_ptr.h"
+#include "base/types/expected.h"
 #include "base/values.h"
 #include "extensions/browser/api/declarative_net_request/install_index_helper.h"
 #include "extensions/browser/content_verifier/content_verifier_key.h"
@@ -27,7 +28,6 @@
 #include "extensions/common/mojom/manifest.mojom-shared.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "services/data_decoder/public/cpp/data_decoder.h"
-#include "services/data_decoder/public/mojom/json_parser.mojom.h"
 
 class SkBitmap;
 
@@ -200,8 +200,7 @@
 
   // Unpacks the extension in directory and returns the manifest.
   void Unpack(const base::FilePath& directory);
-  void ReadManifestDone(std::optional<base::Value> manifest,
-                        const std::optional<std::string>& error);
+  void ReadManifestDone(base::expected<base::Value, std::string> result);
   void UnpackExtensionSucceeded(base::Value::Dict manifest);
 
   // Helper which calls ReportFailure.
@@ -218,8 +217,8 @@
   void SanitizeMessageCatalogs(
       const std::set<base::FilePath>& message_catalog_paths);
 
-  void MessageCatalogsSanitized(JsonFileSanitizer::Status status,
-                                const std::string& error_msg);
+  void MessageCatalogsSanitized(
+      base::expected<void, JsonFileSanitizer::Error> result);
 
   // Reports unpack success or failure, or unzip failure.
   void ReportSuccess();
@@ -252,14 +251,10 @@
 
   void MaybeComputeHashes(bool should_compute_hashes);
 
-  // Returns a JsonParser that can be used on the `unpacker_io_task_runner`.
-  data_decoder::mojom::JsonParser* GetJsonParserPtr();
-
-  // Parses the JSON file at `path` and invokes `callback` when done. `callback`
-  // is called with a null parameter if parsing failed.
+  // Parses the JSON file at `path` and invokes `ReadManifestDone()` with the
+  // result.
   // This must be called from the `unpacker_io_task_runner_`.
-  void ParseJsonFile(const base::FilePath& path,
-                     data_decoder::mojom::JsonParser::ParseCallback callback);
+  void ParseJsonFile(const base::FilePath& path);
 
   // If we unpacked a CRX file, we hold on to the path name for use
   // in various histograms.
diff --git a/extensions/browser/service_worker/service_worker_task_queue.cc b/extensions/browser/service_worker/service_worker_task_queue.cc
index d42d069..0c296f5b 100644
--- a/extensions/browser/service_worker/service_worker_task_queue.cc
+++ b/extensions/browser/service_worker/service_worker_task_queue.cc
@@ -550,10 +550,6 @@
 
   const ExtensionId& extension_id = worker_info.scope.host();
 
-  // Stop tracking the worker for extension API purposes.
-  ProcessManager::Get(browser_context_)
-      ->StopTrackingServiceWorkerRunningInstance(extension_id, version_id);
-
   // Remove worker running state information for event dispatching from the task
   // queue.
   std::optional<base::UnguessableToken> activation_token =
@@ -567,13 +563,35 @@
   WorkerState* worker_state = GetWorkerState(context_id);
   // If the extension is still activated, worker state should still exist.
   CHECK(worker_state);
-  // Untrack all the worker state because once a worker begin stopping or stops,
-  // a new instance must start before the worker can be considered ready to
-  // receive tasks/events again and the renderer stop notifications are not 100%
-  // reliable.
-  worker_state->SetBrowserState(BrowserState::kInitial);
-  worker_state->SetRendererState(RendererState::kNotActive);
-  worker_state->ResetWorkerId();
+
+  // Check that the version ID of the worker that is stopping refers to an
+  // extension service worker that is tracked by this class. Service workers
+  // registered for subscopes via `navigation.serviceWorker.register()` rather
+  // than being declared in the manifest's background section are not allowed
+  // to use extensions API, and should be ignored here. See crbug.com/395536907.
+  // NOTE: We may have already reset worker ID and renderer state, but not
+  // browser state, if `DidStopServiceWorkerContext()` was called first.
+  // In that case, we reset browser state here.
+  bool has_already_reset_renderer_state =
+      !worker_state->worker_id() &&
+      worker_state->renderer_state() == RendererState::kNotActive;
+  if (has_already_reset_renderer_state ||
+      worker_state->worker_id()->version_id == version_id) {
+    // Stop tracking the worker for extension API purposes.
+    ProcessManager::Get(browser_context_)
+        ->StopTrackingServiceWorkerRunningInstance(extension_id, version_id);
+    // Untrack all the worker state because once a worker begin stopping or
+    // stops, a new instance must start before the worker can be considered
+    // ready to receive tasks/events again and the renderer stop notifications
+    // are not 100% reliable.
+    worker_state->SetBrowserState(BrowserState::kInitial);
+    worker_state->SetRendererState(RendererState::kNotActive);
+    worker_state->ResetWorkerId();
+  }
+
+  if (g_test_observer) {
+    g_test_observer->UntrackServiceWorkerState(worker_info.scope);
+  }
 }
 
 void ServiceWorkerTaskQueue::RegisterServiceWorker(
diff --git a/extensions/browser/service_worker/service_worker_task_queue.h b/extensions/browser/service_worker/service_worker_task_queue.h
index 42f2f8cf..a892a08 100644
--- a/extensions/browser/service_worker/service_worker_task_queue.h
+++ b/extensions/browser/service_worker/service_worker_task_queue.h
@@ -380,6 +380,13 @@
     // is preparing to terminate.
     virtual void DidStopServiceWorkerContext(const ExtensionId& extension_id) {}
 
+    // Called when UntrackServiceWorkerState() is invoked for a worker
+    // associated with `scope` (because it's stopping or has stopped).
+    // This notification occurs even if the worker is a sub-scope worker and
+    // does not result in altering the ServiceWorkerTaskQueue's tracking state
+    // for the primary extension service worker.
+    virtual void UntrackServiceWorkerState(const GURL& scope) {}
+
     // Called when a service worker registered for the extension with the
     // `extension_id` has been unregistered in the //content layer.
     virtual void WorkerUnregistered(const ExtensionId& extension_id) {}
diff --git a/extensions/browser/service_worker/service_worker_test_utils.cc b/extensions/browser/service_worker/service_worker_test_utils.cc
index 4a474342..aae7254c 100644
--- a/extensions/browser/service_worker/service_worker_test_utils.cc
+++ b/extensions/browser/service_worker/service_worker_test_utils.cc
@@ -246,6 +246,17 @@
   run_loop.Run();
 }
 
+void TestServiceWorkerTaskQueueObserver::WaitForUntrackServiceWorkerState(
+    const GURL& scope) {
+  if (untracked_set_.count(scope) != 0) {
+    return;
+  }
+
+  base::RunLoop run_loop;
+  untrack_quit_closure_ = run_loop.QuitClosure();
+  run_loop.Run();
+}
+
 void TestServiceWorkerTaskQueueObserver::WaitForWorkerContextInitialized(
     const ExtensionId& extension_id) {
   if (inited_set_.count(extension_id) != 0) {
@@ -374,5 +385,13 @@
   }
 }
 
+void TestServiceWorkerTaskQueueObserver::UntrackServiceWorkerState(
+    const GURL& scope) {
+  untracked_set_.insert(scope);
+  if (untrack_quit_closure_) {
+    std::move(untrack_quit_closure_).Run();
+  }
+}
+
 }  // namespace service_worker_test_utils
 }  // namespace extensions
diff --git a/extensions/browser/service_worker/service_worker_test_utils.h b/extensions/browser/service_worker/service_worker_test_utils.h
index 3c4270c8..fa4e42a9 100644
--- a/extensions/browser/service_worker/service_worker_test_utils.h
+++ b/extensions/browser/service_worker/service_worker_test_utils.h
@@ -179,6 +179,7 @@
       const ExtensionId& extension_id);
   void WaitForOnActivateExtension(const ExtensionId& extension_id);
   bool WaitForRegistrationMismatchMitigation(const ExtensionId& extension_id);
+  void WaitForUntrackServiceWorkerState(const GURL& scope);
 
   std::optional<bool> WillRegisterServiceWorker(
       const ExtensionId& extension_id) const;
@@ -198,6 +199,7 @@
                                      bool success) override;
   void RequestedWorkerStart(const ExtensionId& extension_id) override;
   void DidStopServiceWorkerContext(const ExtensionId& extension_id) override;
+  void UntrackServiceWorkerState(const GURL& scope) override;
 
  private:
   std::map<ExtensionId, bool> activated_map_;
@@ -214,7 +216,11 @@
 
   std::set<ExtensionId> stopped_set_;
 
+  std::set<GURL> untracked_set_;
+
   base::OnceClosure quit_closure_;
+
+  base::OnceClosure untrack_quit_closure_;
 };
 
 }  // namespace service_worker_test_utils
diff --git a/extensions/browser/zipfile_installer.cc b/extensions/browser/zipfile_installer.cc
index db4a5c55..ee8d675 100644
--- a/extensions/browser/zipfile_installer.cc
+++ b/extensions/browser/zipfile_installer.cc
@@ -4,6 +4,7 @@
 
 #include "extensions/browser/zipfile_installer.h"
 
+#include <optional>
 #include <variant>
 
 #include "base/containers/contains.h"
@@ -15,6 +16,7 @@
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/sequenced_task_runner.h"
+#include "base/values.h"
 #include "components/services/unzip/content/unzip_service.h"
 #include "components/services/unzip/public/cpp/unzip.h"
 #include "components/services/unzip/public/mojom/unzipper.mojom.h"
@@ -24,8 +26,6 @@
 #include "extensions/common/extension_features.h"
 #include "extensions/common/manifest.h"
 #include "extensions/strings/grit/extensions_strings.h"
-#include "services/data_decoder/public/cpp/data_decoder.h"
-#include "services/data_decoder/public/mojom/json_parser.mojom.h"
 #include "ui/base/l10n/l10n_util.h"
 
 namespace extensions {
@@ -178,34 +178,7 @@
     return;
   }
 
-  // Create a DataDecoder to specify custom parse options to the JSON
-  // parser. The ownership of the |data_decoder| and |json_parser|
-  // transfer to the response callback and are deleted after it runs.
-  auto data_decoder = std::make_unique<data_decoder::DataDecoder>();
-  mojo::Remote<data_decoder::mojom::JsonParser> json_parser;
-  data_decoder->GetService()->BindJsonParser(
-      json_parser.BindNewPipeAndPassReceiver());
-  json_parser.set_disconnect_handler(
-      base::BindOnce(&ZipFileInstaller::ManifestParsed, this, unzip_dir,
-                     std::nullopt, "Data Decoder terminated unexpectedly"));
-  auto* json_parser_ptr = json_parser.get();
-  json_parser_ptr->Parse(
-      *manifest_content, base::JSON_PARSE_CHROMIUM_EXTENSIONS,
-      base::BindOnce(
-          [](std::unique_ptr<data_decoder::DataDecoder>,
-             mojo::Remote<data_decoder::mojom::JsonParser>,
-             scoped_refptr<ZipFileInstaller> installer,
-             const base::FilePath& unzip_dir, std::optional<base::Value> value,
-             const std::optional<std::string>& error) {
-            installer->ManifestParsed(unzip_dir, std::move(value), error);
-          },
-          std::move(data_decoder), std::move(json_parser),
-          base::WrapRefCounted(this), unzip_dir));
-}
-
-void ZipFileInstaller::ManifestParsed(const base::FilePath& unzip_dir,
-                                      std::optional<base::Value> result,
-                                      const std::optional<std::string>& error) {
+  std::optional<base::Value> result = base::JSONReader::Read(*manifest_content);
   if (!result || !result->is_dict()) {
     ReportFailure(std::string(kExtensionHandlerFileUnzipError));
     return;
diff --git a/extensions/browser/zipfile_installer.h b/extensions/browser/zipfile_installer.h
index 9b4c69ce..3c9d5f9 100644
--- a/extensions/browser/zipfile_installer.h
+++ b/extensions/browser/zipfile_installer.h
@@ -17,7 +17,6 @@
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
 #include "base/task/sequenced_task_runner.h"
-#include "base/values.h"
 
 namespace extensions {
 
@@ -89,9 +88,6 @@
   void ManifestUnzipped(const base::FilePath& unzip_dir, bool success);
   void ManifestRead(const base::FilePath& unzip_dir,
                     std::optional<std::string> manifest_content);
-  void ManifestParsed(const base::FilePath& unzip_dir,
-                      std::optional<base::Value> result,
-                      const std::optional<std::string>& error);
   void UnzipDone(const base::FilePath& unzip_dir, bool success);
 
   // On failure, report the `error` reason.
diff --git a/extensions/common/switches.cc b/extensions/common/switches.cc
index f51ad1e..d6c57c49 100644
--- a/extensions/common/switches.cc
+++ b/extensions/common/switches.cc
@@ -43,4 +43,10 @@
 const char kAllowFutureManifestVersion[] = "allow-future-manifest-version";
 const char kExtensionTestApiOnWebPages[] = "extension-test-api-on-web-pages";
 
+const char kZeroStatePromoIphVariantParamName[] =
+    "extension-zero-state-iph-variant";
+const char kZeroStatePromoCustomActionIph[] = "custom-action-iph";
+const char kZeroStatePromoCustomUiChipIph[] = "custom-ui-chip-iph";
+const char kZeroStatePromoCustomUiPlainLinkIph[] = "custom-ui-plain-link-iph";
+
 }  // namespace extensions::switches
diff --git a/extensions/common/switches.h b/extensions/common/switches.h
index 61319f9..56816333 100644
--- a/extensions/common/switches.h
+++ b/extensions/common/switches.h
@@ -97,6 +97,23 @@
 // actually use it in browser tests.
 extern const char kExtensionTestApiOnWebPages[];
 
+// The feature parameter name that controls the variant of IPH shown when the
+// user has no extensions installed.
+extern const char kZeroStatePromoIphVariantParamName[];
+
+// When the user has no extensions installed, display a custom action IPH
+// that upon triggering, opens a new tab to the Chrome Web Store.
+extern const char kZeroStatePromoCustomActionIph[];
+
+// When the user has no extensions installed, display a custom UI IPH that
+// presents the user with different collections of extensions to explore,
+// each in a cr-chip button.
+extern const char kZeroStatePromoCustomUiChipIph[];
+
+// When the user has no extensions installed, display a custom UI IPH that
+// presents the user with different collections of extensions to explore,
+// each in a plain text link.
+extern const char kZeroStatePromoCustomUiPlainLinkIph[];
 }  // namespace extensions::switches
 
 #endif  // EXTENSIONS_COMMON_SWITCHES_H_
diff --git a/extensions/renderer/extension_url_loader_throttle.cc b/extensions/renderer/extension_url_loader_throttle.cc
index 1f61f2b..faa7895b 100644
--- a/extensions/renderer/extension_url_loader_throttle.cc
+++ b/extensions/renderer/extension_url_loader_throttle.cc
@@ -34,11 +34,11 @@
 
 void ExtensionURLLoaderThrottle::WillRedirectRequest(
     net::RedirectInfo* redirect_info,
-    const network::mojom::URLResponseHead& /* response_head */,
-    bool* /* defer */,
-    std::vector<std::string>* /* to_be_removed_request_headers */,
-    net::HttpRequestHeaders* /* modified_request_headers */,
-    net::HttpRequestHeaders* /* modified_cors_exempt_request_headers */) {
+    /*response_head=*/const network::mojom::URLResponseHead&,
+    /*defer=*/bool*,
+    /*to_be_removed_request_headers=*/std::vector<std::string>*,
+    /*modified_request_headers=*/net::HttpRequestHeaders*,
+    /*modified_cors_exempt_request_headers=*/net::HttpRequestHeaders*) {
   if (manager_->ShouldRejectRedirect(start_request_url_, *redirect_info)) {
     delegate_->CancelWithError(net::ERR_TEMPORARILY_THROTTLED, kCancelReason);
   }
diff --git a/extensions/renderer/storage_area.cc b/extensions/renderer/storage_area.cc
index 198ea4e..25d693b 100644
--- a/extensions/renderer/storage_area.cc
+++ b/extensions/renderer/storage_area.cc
@@ -286,8 +286,12 @@
 void StorageArea::HandleFunctionCall(const std::string& method_name,
                                      gin::Arguments* arguments) {
   v8::Isolate* isolate = arguments->isolate();
+  // This is only ever called from JavaScript, so we must have entered the
+  // isolate already in this thread.
+  CHECK_EQ(v8::Isolate::GetCurrent(), isolate);
   v8::HandleScope handle_scope(isolate);
   v8::Local<v8::Context> context = arguments->GetHolderCreationContext();
+  CHECK_EQ(isolate, context->GetIsolate());
 
   // The context may have been invalidated, as in the case where this could be
   // a reference to an object from a removed frame.
diff --git a/fuchsia_web/webengine/browser/frame_impl.cc b/fuchsia_web/webengine/browser/frame_impl.cc
index 2c9ef63..c61784e 100644
--- a/fuchsia_web/webengine/browser/frame_impl.cc
+++ b/fuchsia_web/webengine/browser/frame_impl.cc
@@ -201,7 +201,7 @@
   return frame_impl_map;
 }
 
-blink::PermissionType FidlPermissionTypeToContentPermissionType(
+std::optional<blink::PermissionType> FidlPermissionTypeToContentPermissionType(
     fuchsia::web::PermissionType fidl_type) {
   switch (fidl_type) {
     case fuchsia::web::PermissionType::MICROPHONE:
@@ -212,6 +212,8 @@
       return blink::PermissionType::PROTECTED_MEDIA_IDENTIFIER;
     case fuchsia::web::PermissionType::PERSISTENT_STORAGE:
       return blink::PermissionType::DURABLE_STORAGE;
+    default:
+      return std::nullopt;
   }
 }
 
@@ -1270,8 +1272,14 @@
     return;
   }
 
-  blink::PermissionType type =
+  std::optional<blink::PermissionType> type =
       FidlPermissionTypeToContentPermissionType(fidl_permission.type());
+  if (!type) {
+    LOG(ERROR) << "SetPermissionState() called with invalid permission type: "
+               << static_cast<uint16_t>(fidl_permission.type()) << ".";
+    CloseAndDestroyFrame(ZX_ERR_INVALID_ARGS);
+    return;
+  }
 
   blink::mojom::PermissionStatus state =
       (fidl_state == fuchsia::web::PermissionState::GRANTED)
@@ -1281,8 +1289,8 @@
   // TODO(crbug.com/40724536): Remove this once the PermissionManager API is
   // available.
   if (web_origin_string == "*" &&
-      type == blink::PermissionType::PROTECTED_MEDIA_IDENTIFIER) {
-    permission_controller_.SetDefaultPermissionState(type, state);
+      *type == blink::PermissionType::PROTECTED_MEDIA_IDENTIFIER) {
+    permission_controller_.SetDefaultPermissionState(*type, state);
     return;
   }
 
@@ -1295,7 +1303,7 @@
     return;
   }
 
-  permission_controller_.SetPermissionState(type, web_origin.value(), state);
+  permission_controller_.SetPermissionState(*type, web_origin.value(), state);
 }
 
 void FrameImpl::GetPrivateMemorySize(GetPrivateMemorySizeCallback callback) {
diff --git a/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc b/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc
index c0bfdc62..6f1812c 100644
--- a/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc
+++ b/fuchsia_web/webengine/browser/web_engine_content_browser_client.cc
@@ -195,9 +195,6 @@
     content::WebContents* web_contents,
     content::SiteInstance& main_frame_site,
     blink::web_pref::WebPreferences* web_prefs) {
-  // Disable WebSQL support since it is being removed from the web platform
-  // and does not work. See crbug.com/1317431.
-  web_prefs->databases_enabled = false;
 
   // TODO(crbug.com/40245916): Remove once supported in WebEngine.
   web_prefs->disable_webauthn = true;
diff --git a/gpu/command_buffer/client/shared_image_pool.h b/gpu/command_buffer/client/shared_image_pool.h
index 8180021..25fc628 100644
--- a/gpu/command_buffer/client/shared_image_pool.h
+++ b/gpu/command_buffer/client/shared_image_pool.h
@@ -76,7 +76,7 @@
 // addition to the shared image it wraps. This allow clients to create its own
 // custom pool of images of ClientImage type and are not limited to creating
 // pool of only ClientSharedImage. See unittests for example.
-class GPU_EXPORT ClientImage : public base::RefCounted<ClientImage> {
+class GPU_EXPORT ClientImage : public base::RefCountedThreadSafe<ClientImage> {
  public:
   explicit ClientImage(scoped_refptr<ClientSharedImage> shared_image);
 
@@ -96,7 +96,7 @@
   const SharedImagePoolId& GetPoolIdForTesting() const;
 
  protected:
-  friend class base::RefCounted<ClientImage>;
+  friend class base::RefCountedThreadSafe<ClientImage>;
   friend class SharedImagePoolBase;
 
   // Allow each instantiation of SharedImagePool to access `pool_id_`.
diff --git a/infra/config/generated/builders/ci/Dawn Win10 x64 Builder/targets/chromium.dawn.json b/infra/config/generated/builders/ci/Dawn Win10 x64 Builder/targets/chromium.dawn.json
index 0402278..caf3d10 100644
--- a/infra/config/generated/builders/ci/Dawn Win10 x64 Builder/targets/chromium.dawn.json
+++ b/infra/config/generated/builders/ci/Dawn Win10 x64 Builder/targets/chromium.dawn.json
@@ -57,8 +57,8 @@
         "swarming": {
           "dimensions": {
             "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
             "pool": "chromium.tests.gpu"
           },
           "expiration": 21600,
diff --git "a/infra/config/generated/builders/ci/Dawn Win10 x64 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json" "b/infra/config/generated/builders/ci/Dawn Win10 x64 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json"
index 15425165..9666298 100644
--- "a/infra/config/generated/builders/ci/Dawn Win10 x64 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json"
+++ "b/infra/config/generated/builders/ci/Dawn Win10 x64 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json"
@@ -20,8 +20,8 @@
         "swarming": {
           "dimensions": {
             "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
             "pool": "chromium.tests.gpu"
           },
           "expiration": 21600,
diff --git a/infra/config/generated/builders/ci/Dawn Win10 x86 Builder/targets/chromium.dawn.json b/infra/config/generated/builders/ci/Dawn Win10 x86 Builder/targets/chromium.dawn.json
index 622f57e7..7d51189 100644
--- a/infra/config/generated/builders/ci/Dawn Win10 x86 Builder/targets/chromium.dawn.json
+++ b/infra/config/generated/builders/ci/Dawn Win10 x86 Builder/targets/chromium.dawn.json
@@ -57,8 +57,8 @@
         "swarming": {
           "dimensions": {
             "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
             "pool": "chromium.tests.gpu"
           },
           "expiration": 21600,
diff --git "a/infra/config/generated/builders/ci/Dawn Win10 x86 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json" "b/infra/config/generated/builders/ci/Dawn Win10 x86 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json"
index 6305478..054a68fd 100644
--- "a/infra/config/generated/builders/ci/Dawn Win10 x86 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json"
+++ "b/infra/config/generated/builders/ci/Dawn Win10 x86 Experimental Release \050NVIDIA\051/targets/chromium.dawn.json"
@@ -20,8 +20,8 @@
         "swarming": {
           "dimensions": {
             "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
             "pool": "chromium.tests.gpu"
           },
           "expiration": 21600,
diff --git a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json
index 78e7c40..204fcf0 100644
--- a/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/ci/GPU FYI Win x64 Builder/targets/chromium.gpu.fyi.json
@@ -1,10 +1,380 @@
 {
   "GPU FYI Win x64 Builder": {},
   "Win10 FYI x64 Exp Release (NVIDIA)": {
+    "gtest_tests": [
+      {
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "angle_unittests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "angle_unittests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=passthrough",
+          "--use-gl=angle",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--git-revision=${got_revision}",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_unittests",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_unittests",
+        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
+      },
+      {
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gpu_unittests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gpu_unittests",
+        "test_id_prefix": "ninja://gpu:gpu_unittests/"
+      },
+      {
+        "args": [
+          "--gtest_filter=MediaFoundationEncryptedMediaTest*",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "media_foundation_browser_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      },
+      {
+        "args": [
+          "--gtest_filter=WebNN*",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "services_webnn_unittests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "services_unittests",
+        "test_id_prefix": "ninja://services:services_unittests/"
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--gtest_filter=TabCaptureApiPixelTest.EndToEnd*"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "tab_capture_end2end_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      },
+      {
+        "args": [
+          "--ignore-runtime-requirements=*"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "xr_browser_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "xr_browser_tests",
+        "test_id_prefix": "ninja://chrome/test:xr_browser_tests/"
+      }
+    ],
     "isolated_scripts": [
       {
         "args": [
-          "noop_sleep",
+          "context_lost",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --enable-features=SkiaGraphite",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "context_lost_passthrough_graphite_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "context_lost",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "context_lost_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "expected_color",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --enable-features=SkiaGraphite",
+          "--enforce-browser-version",
+          "--git-revision=${got_revision}",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "expected_color_pixel_passthrough_graphite_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "expected_color",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--enforce-browser-version",
+          "--git-revision=${got_revision}",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "expected_color_pixel_passthrough_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "gpu_process",
           "--show-stdout",
           "--browser=release_x64",
           "--passthrough",
@@ -17,13 +387,17 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "noop_sleep_tests",
+        "name": "gpu_process_launch_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
             "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
             "pool": "chromium.tests.gpu"
           },
           "expiration": 21600,
@@ -34,6 +408,514 @@
         },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "hardware_accelerated_feature",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "hardware_accelerated_feature_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "info_collection",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--expected-vendor-id",
+          "10de",
+          "--expected-device-id",
+          "2184",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "info_collection_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "--gtest-benchmark-name=passthrough_command_buffer_perftests",
+          "-v",
+          "--use-cmd-decoder=passthrough",
+          "--use-angle=gl-null",
+          "--fast-run"
+        ],
+        "merge": {
+          "args": [
+            "--smoke-test-mode"
+          ],
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "passthrough_command_buffer_perftests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "command_buffer_perftests",
+        "test_id_prefix": "ninja://gpu:command_buffer_perftests/"
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --enable-features=SkiaGraphite",
+          "--enforce-browser-version",
+          "--git-revision=${got_revision}",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "pixel_skia_gold_passthrough_graphite_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--enforce-browser-version",
+          "--git-revision=${got_revision}",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "pixel_skia_gold_passthrough_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --enable-features=SkiaGraphite",
+          "--enforce-browser-version",
+          "--dont-restore-color-profile-after-test",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "screenshot_sync_passthrough_graphite_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--enforce-browser-version",
+          "--dont-restore-color-profile-after-test",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "screenshot_sync_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "trace_test",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=1"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "trace_test",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webcodecs",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webcodecs_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl2_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d11 --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_win_runtimes.json",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl2_conformance_d3d11_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 20
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl1_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d11 --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl1_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d9 --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_d3d9_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl1_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=vulkan --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_vulkan_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
     ]
   },
diff --git "a/infra/config/generated/builders/ci/Win10 FYI x64 Exp Release \050NVIDIA\051/targets/chromium.gpu.fyi.json" "b/infra/config/generated/builders/ci/Win10 FYI x64 Exp Release \050NVIDIA\051/targets/chromium.gpu.fyi.json"
index e7e366a..d812ebe 100644
--- "a/infra/config/generated/builders/ci/Win10 FYI x64 Exp Release \050NVIDIA\051/targets/chromium.gpu.fyi.json"
+++ "b/infra/config/generated/builders/ci/Win10 FYI x64 Exp Release \050NVIDIA\051/targets/chromium.gpu.fyi.json"
@@ -1,9 +1,379 @@
 {
   "Win10 FYI x64 Exp Release (NVIDIA)": {
+    "gtest_tests": [
+      {
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "angle_unittests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "angle_unittests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=passthrough",
+          "--use-gl=angle",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--git-revision=${got_revision}",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_unittests",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_unittests",
+        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
+      },
+      {
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gpu_unittests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gpu_unittests",
+        "test_id_prefix": "ninja://gpu:gpu_unittests/"
+      },
+      {
+        "args": [
+          "--gtest_filter=MediaFoundationEncryptedMediaTest*",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "media_foundation_browser_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      },
+      {
+        "args": [
+          "--gtest_filter=WebNN*",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "services_webnn_unittests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "services_unittests",
+        "test_id_prefix": "ninja://services:services_unittests/"
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--gtest_filter=TabCaptureApiPixelTest.EndToEnd*"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "tab_capture_end2end_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      },
+      {
+        "args": [
+          "--ignore-runtime-requirements=*"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "xr_browser_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "xr_browser_tests",
+        "test_id_prefix": "ninja://chrome/test:xr_browser_tests/"
+      }
+    ],
     "isolated_scripts": [
       {
         "args": [
-          "noop_sleep",
+          "context_lost",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --enable-features=SkiaGraphite",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "context_lost_passthrough_graphite_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "context_lost",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "context_lost_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "expected_color",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --enable-features=SkiaGraphite",
+          "--enforce-browser-version",
+          "--git-revision=${got_revision}",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "expected_color_pixel_passthrough_graphite_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "expected_color",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--enforce-browser-version",
+          "--git-revision=${got_revision}",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "expected_color_pixel_passthrough_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "gpu_process",
           "--show-stdout",
           "--browser=release_x64",
           "--passthrough",
@@ -16,13 +386,17 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "noop_sleep_tests",
+        "name": "gpu_process_launch_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
             "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
             "pool": "chromium.tests.gpu"
           },
           "expiration": 21600,
@@ -33,6 +407,514 @@
         },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "hardware_accelerated_feature",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "hardware_accelerated_feature_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "info_collection",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--expected-vendor-id",
+          "10de",
+          "--expected-device-id",
+          "2184",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "info_collection_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "--gtest-benchmark-name=passthrough_command_buffer_perftests",
+          "-v",
+          "--use-cmd-decoder=passthrough",
+          "--use-angle=gl-null",
+          "--fast-run"
+        ],
+        "merge": {
+          "args": [
+            "--smoke-test-mode"
+          ],
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "passthrough_command_buffer_perftests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "command_buffer_perftests",
+        "test_id_prefix": "ninja://gpu:command_buffer_perftests/"
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --enable-features=SkiaGraphite",
+          "--enforce-browser-version",
+          "--git-revision=${got_revision}",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "pixel_skia_gold_passthrough_graphite_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--enforce-browser-version",
+          "--git-revision=${got_revision}",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "pixel_skia_gold_passthrough_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --enable-features=SkiaGraphite",
+          "--enforce-browser-version",
+          "--dont-restore-color-profile-after-test",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "screenshot_sync_passthrough_graphite_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--enforce-browser-version",
+          "--dont-restore-color-profile-after-test",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "screenshot_sync_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "trace_test",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=1"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "trace_test",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webcodecs",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webcodecs_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl2_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d11 --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_win_runtimes.json",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl2_conformance_d3d11_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 20
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl1_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d11 --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl1_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d9 --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_d3d9_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl1_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=vulkan --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_vulkan_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
     ]
   }
diff --git a/infra/config/generated/builders/ci/fuchsia-x64-perf-cast-receiver-rel/properties.json b/infra/config/generated/builders/ci/fuchsia-x64-perf-cast-receiver-rel/properties.json
index 6121704..91d83d7 100644
--- a/infra/config/generated/builders/ci/fuchsia-x64-perf-cast-receiver-rel/properties.json
+++ b/infra/config/generated/builders/ci/fuchsia-x64-perf-cast-receiver-rel/properties.json
@@ -27,6 +27,7 @@
               },
               "legacy_gclient_config": {
                 "apply_configs": [
+                  "checkout_pgo_profiles",
                   "fuchsia_x64"
                 ],
                 "config": "chromium"
diff --git a/infra/config/generated/builders/ci/linux-blink-asan-rel/targets/chromium.memory.json b/infra/config/generated/builders/ci/linux-blink-asan-rel/targets/chromium.memory.json
index 1140c37..ff85ab7 100644
--- a/infra/config/generated/builders/ci/linux-blink-asan-rel/targets/chromium.memory.json
+++ b/infra/config/generated/builders/ci/linux-blink-asan-rel/targets/chromium.memory.json
@@ -61,7 +61,8 @@
       },
       {
         "args": [
-          "--test-launcher-filter-file=../../third_party/blink/web_tests/TestLists/chrome.filter"
+          "--test-launcher-filter-file=../../third_party/blink/web_tests/TestLists/chrome.filter",
+          "-j6"
         ],
         "merge": {
           "args": [
diff --git a/infra/config/generated/builders/try/dawn-try-win-x64-nvidia-exp/targets/chromium.dawn.json b/infra/config/generated/builders/try/dawn-try-win-x64-nvidia-exp/targets/chromium.dawn.json
index 81f0273..e8ad0bc9 100644
--- a/infra/config/generated/builders/try/dawn-try-win-x64-nvidia-exp/targets/chromium.dawn.json
+++ b/infra/config/generated/builders/try/dawn-try-win-x64-nvidia-exp/targets/chromium.dawn.json
@@ -21,8 +21,8 @@
         "swarming": {
           "dimensions": {
             "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
             "pool": "chromium.tests.gpu"
           },
           "expiration": 21600,
diff --git a/infra/config/generated/builders/try/dawn-try-win-x86-nvidia-exp/targets/chromium.dawn.json b/infra/config/generated/builders/try/dawn-try-win-x86-nvidia-exp/targets/chromium.dawn.json
index 0279679..e4dbbe9 100644
--- a/infra/config/generated/builders/try/dawn-try-win-x86-nvidia-exp/targets/chromium.dawn.json
+++ b/infra/config/generated/builders/try/dawn-try-win-x86-nvidia-exp/targets/chromium.dawn.json
@@ -21,8 +21,8 @@
         "swarming": {
           "dimensions": {
             "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
             "pool": "chromium.tests.gpu"
           },
           "expiration": 21600,
diff --git a/infra/config/generated/builders/try/fuchsia-x64-perf-cast-receiver-rel/properties.json b/infra/config/generated/builders/try/fuchsia-x64-perf-cast-receiver-rel/properties.json
index e642ae1..bbe0f00c 100644
--- a/infra/config/generated/builders/try/fuchsia-x64-perf-cast-receiver-rel/properties.json
+++ b/infra/config/generated/builders/try/fuchsia-x64-perf-cast-receiver-rel/properties.json
@@ -27,6 +27,7 @@
               },
               "legacy_gclient_config": {
                 "apply_configs": [
+                  "checkout_pgo_profiles",
                   "fuchsia_x64"
                 ],
                 "config": "chromium"
diff --git a/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-exp-64/targets/chromium.gpu.fyi.json b/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-exp-64/targets/chromium.gpu.fyi.json
index fff5eca..2778b654 100644
--- a/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-exp-64/targets/chromium.gpu.fyi.json
+++ b/infra/config/generated/builders/try/gpu-fyi-try-win10-nvidia-exp-64/targets/chromium.gpu.fyi.json
@@ -1,10 +1,380 @@
 {
   "GPU FYI Win x64 Builder": {},
   "Win10 FYI x64 Exp Release (NVIDIA)": {
+    "gtest_tests": [
+      {
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "angle_unittests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "angle_unittests",
+        "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/",
+        "use_isolated_scripts_api": true
+      },
+      {
+        "args": [
+          "--use-cmd-decoder=passthrough",
+          "--use-gl=angle",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_tests_passthrough",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "gl_tests",
+        "test_id_prefix": "ninja://gpu:gl_tests/"
+      },
+      {
+        "args": [
+          "--use-gpu-in-tests",
+          "--git-revision=${got_revision}",
+          "--test-launcher-filter-file=../../testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gl_unittests",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gl_unittests",
+        "test_id_prefix": "ninja://ui/gl:gl_unittests/"
+      },
+      {
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "gpu_unittests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "gpu_unittests",
+        "test_id_prefix": "ninja://gpu:gpu_unittests/"
+      },
+      {
+        "args": [
+          "--gtest_filter=MediaFoundationEncryptedMediaTest*",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "media_foundation_browser_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      },
+      {
+        "args": [
+          "--gtest_filter=WebNN*",
+          "--use-gpu-in-tests"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "services_webnn_unittests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "services_unittests",
+        "test_id_prefix": "ninja://services:services_unittests/"
+      },
+      {
+        "args": [
+          "--enable-gpu",
+          "--test-launcher-bot-mode",
+          "--test-launcher-jobs=1",
+          "--gtest_filter=TabCaptureApiPixelTest.EndToEnd*"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "tab_capture_end2end_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "browser_tests",
+        "test_id_prefix": "ninja://chrome/test:browser_tests/"
+      },
+      {
+        "args": [
+          "--ignore-runtime-requirements=*"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "name": "xr_browser_tests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "xr_browser_tests",
+        "test_id_prefix": "ninja://chrome/test:xr_browser_tests/"
+      }
+    ],
     "isolated_scripts": [
       {
         "args": [
-          "noop_sleep",
+          "context_lost",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --enable-features=SkiaGraphite",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "context_lost_passthrough_graphite_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "context_lost",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "context_lost_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "expected_color",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --enable-features=SkiaGraphite",
+          "--enforce-browser-version",
+          "--git-revision=${got_revision}",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "expected_color_pixel_passthrough_graphite_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "expected_color",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--enforce-browser-version",
+          "--git-revision=${got_revision}",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "expected_color_pixel_passthrough_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "gpu_process",
           "--show-stdout",
           "--browser=release_x64",
           "--passthrough",
@@ -17,13 +387,17 @@
         "merge": {
           "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
         },
-        "name": "noop_sleep_tests",
+        "name": "gpu_process_launch_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
         "swarming": {
           "containment_type": "AUTO",
           "dimensions": {
             "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
             "pool": "chromium.tests.gpu"
           },
           "expiration": 21600,
@@ -34,6 +408,514 @@
         },
         "test": "telemetry_gpu_integration_test",
         "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "hardware_accelerated_feature",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "hardware_accelerated_feature_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "info_collection",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--expected-vendor-id",
+          "10de",
+          "--expected-device-id",
+          "2184",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "info_collection_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "--gtest-benchmark-name=passthrough_command_buffer_perftests",
+          "-v",
+          "--use-cmd-decoder=passthrough",
+          "--use-angle=gl-null",
+          "--fast-run"
+        ],
+        "merge": {
+          "args": [
+            "--smoke-test-mode"
+          ],
+          "script": "//tools/perf/process_perf_results.py"
+        },
+        "name": "passthrough_command_buffer_perftests",
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "command_buffer_perftests",
+        "test_id_prefix": "ninja://gpu:command_buffer_perftests/"
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --enable-features=SkiaGraphite",
+          "--enforce-browser-version",
+          "--git-revision=${got_revision}",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "pixel_skia_gold_passthrough_graphite_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "pixel",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--enforce-browser-version",
+          "--git-revision=${got_revision}",
+          "--dont-restore-color-profile-after-test",
+          "--test-machine-name",
+          "${buildername}",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "pixel_skia_gold_passthrough_test",
+        "precommit_args": [
+          "--gerrit-issue=${patch_issue}",
+          "--gerrit-patchset=${patch_set}",
+          "--buildbucket-id=${buildbucket_build_id}"
+        ],
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --enable-features=SkiaGraphite",
+          "--enforce-browser-version",
+          "--dont-restore-color-profile-after-test",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "screenshot_sync_passthrough_graphite_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "screenshot_sync",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle",
+          "--enforce-browser-version",
+          "--dont-restore-color-profile-after-test",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "screenshot_sync_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "trace_test",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=1"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "trace_test",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webcodecs",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
+          "--enforce-browser-version",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webcodecs_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl2_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d11 --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--webgl-conformance-version=2.0.1",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_win_runtimes.json",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl2_conformance_d3d11_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 20
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl1_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d11 --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_d3d11_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl1_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=d3d9 --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_d3d9_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
+      },
+      {
+        "args": [
+          "webgl1_conformance",
+          "--show-stdout",
+          "--browser=release_x64",
+          "--passthrough",
+          "-v",
+          "--stable-jobs",
+          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-cmd-decoder=passthrough --use-gl=angle --use-angle=vulkan --force_high_performance_gpu",
+          "--enforce-browser-version",
+          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl1_conformance_win_runtimes.json",
+          "--jobs=4"
+        ],
+        "merge": {
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "webgl_conformance_vulkan_passthrough_tests",
+        "resultdb": {
+          "enable": true,
+          "has_native_resultdb_integration": true
+        },
+        "swarming": {
+          "containment_type": "AUTO",
+          "dimensions": {
+            "display_attached": "1",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
+            "pool": "chromium.tests.gpu"
+          },
+          "expiration": 21600,
+          "hard_timeout": 1800,
+          "idempotent": false,
+          "io_timeout": 1800,
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 2
+        },
+        "test": "telemetry_gpu_integration_test",
+        "test_id_prefix": "ninja://chrome/test:telemetry_gpu_integration_test/"
       }
     ]
   }
diff --git a/infra/config/generated/builders/try/linux-blink-asan-rel/targets/chromium.memory.json b/infra/config/generated/builders/try/linux-blink-asan-rel/targets/chromium.memory.json
index 1140c37..ff85ab7 100644
--- a/infra/config/generated/builders/try/linux-blink-asan-rel/targets/chromium.memory.json
+++ b/infra/config/generated/builders/try/linux-blink-asan-rel/targets/chromium.memory.json
@@ -61,7 +61,8 @@
       },
       {
         "args": [
-          "--test-launcher-filter-file=../../third_party/blink/web_tests/TestLists/chrome.filter"
+          "--test-launcher-filter-file=../../third_party/blink/web_tests/TestLists/chrome.filter",
+          "-j6"
         ],
         "merge": {
           "args": [
diff --git a/infra/config/generated/builders/try/linux_chromium_asan_rel_ng/gn-args.json b/infra/config/generated/builders/try/linux_chromium_asan_rel_ng/gn-args.json
index 9b1f1e5..b315f28 100644
--- a/infra/config/generated/builders/try/linux_chromium_asan_rel_ng/gn-args.json
+++ b/infra/config/generated/builders/try/linux_chromium_asan_rel_ng/gn-args.json
@@ -6,7 +6,7 @@
     "is_component_build": false,
     "is_debug": false,
     "is_lsan": true,
-    "symbol_level": 1,
+    "symbol_level": 0,
     "target_cpu": "x64",
     "target_os": "linux",
     "use_reclient": false,
diff --git a/infra/config/generated/builders/try/linux_chromium_tsan_rel_ng/gn-args.json b/infra/config/generated/builders/try/linux_chromium_tsan_rel_ng/gn-args.json
index b4e11861..b9c48870 100644
--- a/infra/config/generated/builders/try/linux_chromium_tsan_rel_ng/gn-args.json
+++ b/infra/config/generated/builders/try/linux_chromium_tsan_rel_ng/gn-args.json
@@ -5,7 +5,7 @@
     "is_component_build": false,
     "is_debug": false,
     "is_tsan": true,
-    "symbol_level": 1,
+    "symbol_level": 0,
     "target_cpu": "x64",
     "target_os": "linux",
     "use_reclient": false,
diff --git a/infra/config/generated/builders/try/mac15-arm64-rel/gn-args.json b/infra/config/generated/builders/try/mac15-arm64-rel/gn-args.json
index cff2012d..4f54d2b 100644
--- a/infra/config/generated/builders/try/mac15-arm64-rel/gn-args.json
+++ b/infra/config/generated/builders/try/mac15-arm64-rel/gn-args.json
@@ -8,7 +8,7 @@
     "symbol_level": 0,
     "target_cpu": "arm64",
     "target_os": "mac",
-    "use_reclient": true,
+    "use_reclient": false,
     "use_remoteexec": true,
     "use_siso": true
   }
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg
index 0b1bb08..b0893d88 100644
--- a/infra/config/generated/luci/luci-milo.cfg
+++ b/infra/config/generated/luci/luci-milo.cfg
@@ -16459,6 +16459,11 @@
     short_name: "dbg"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Win10 FYI x64 Exp Release (NVIDIA)"
+    category: "chromium.gpu.fyi|Windows|10|x64|Nvidia"
+    short_name: "exp"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Win10 FYI x64 Release (NVIDIA)"
     category: "chromium.gpu.fyi|Windows|10|x64|Nvidia"
     short_name: "rel"
@@ -17472,6 +17477,11 @@
     short_name: "dbg"
   }
   builders {
+    name: "buildbucket/luci.chromium.ci/Win10 FYI x64 Exp Release (NVIDIA)"
+    category: "Windows|10|x64|Nvidia"
+    short_name: "exp"
+  }
+  builders {
     name: "buildbucket/luci.chromium.ci/Win10 FYI x64 Release XR Perf (NVIDIA)"
     category: "Windows|10|x64|Nvidia"
     short_name: "xr"
diff --git a/infra/config/generated/testing/mixins.pyl b/infra/config/generated/testing/mixins.pyl
index dd9fc79..159ef3a 100644
--- a/infra/config/generated/testing/mixins.pyl
+++ b/infra/config/generated/testing/mixins.pyl
@@ -261,6 +261,7 @@
     ],
     'android_args': [
       '$$MAGIC_SUBSTITUTION_GPUTelemetryNoRootForUnrootedDevices',
+      '$$MAGIC_SUBSTITUTION_AndroidDesktopTelemetryRemote',
       '--initial-find-device-attempts=3',
     ],
     'chromeos_args': [
@@ -866,8 +867,8 @@
     'swarming': {
       'dimensions': {
         'display_attached': '1',
-        'gpu': '10de:2184-31.0.15.4601',
-        'os': 'Windows-10-19045',
+        'gpu': '10de:2184-32.0.15.7602',
+        'os': 'Windows-11-26100',
         'pool': 'chromium.tests.gpu',
       },
     },
diff --git a/infra/config/lib/targets-internal/magic_args.star b/infra/config/lib/targets-internal/magic_args.star
index cdfaedc..ed99878 100644
--- a/infra/config/lib/targets-internal/magic_args.star
+++ b/infra/config/lib/targets-internal/magic_args.star
@@ -33,6 +33,10 @@
 
 # LINT.IfChange
 
+_ANDROID_DESKTOP_BOARD_GPUS = {
+    "brya": _gpu_device(vendor = "8086", device = "46a8"),
+}
+
 _CROS_BOARD_GPUS = {
     "volteer": _gpu_device(vendor = "8086", device = "9a49"),
 }
@@ -68,11 +72,21 @@
         fail("dimensions is not set")
     return dimensions
 
+def _is_android_desktop(spec_value, settings):
+    """Helper function to determine if the test will be running on Android Desktop."""
+    is_android = settings.os_type == common.os_type.ANDROID
+    return is_android and _get_android_desktop_board_name(spec_value)
+
 def _is_skylab(settings):
     """Helper function to determine if the test will be running on skylab."""
     return (settings.browser_config == common.browser_config.CROS_CHROME and
             not settings.use_swarming)
 
+def _get_android_desktop_board_name(spec_value):
+    """Helper function to determine what Android Desktop board is being used."""
+    dimensions = _get_dimensions(spec_value)
+    return dimensions.get("label-board")
+
 def _get_cros_board_name(spec_value):
     """Helper function to determine what ChromeOS board is being used."""
     dimensions = _get_dimensions(spec_value)
@@ -85,6 +99,17 @@
 
     return dimensions.get("device_type", "amd64-generic")
 
+def _android_desktop_telemetry_remote(_, settings, spec_value):
+    """Substitutes the correct Android Desktop remote Telemetry arguments."""
+    if settings.os_type != common.os_type.ANDROID:
+        fail("Ran an Android Desktop-specific substitution on a non-Android builder")
+    if not _get_android_desktop_board_name(spec_value):
+        return []
+    return [
+        "--device=variable_lab_dut_hostname",
+        "--connect-to-device-over-network",
+    ]
+
 def _cros_telemetry_remote(_, settings, spec_value):
     """Substitutes the correct CrOS remote Telemetry arguments.
 
@@ -149,6 +174,8 @@
     We only ever trigger tests on a single vendor type per builder definition,
     so multiple found vendors is an error.
     """
+    if _is_android_desktop(spec_value, settings):
+        return _gpu_expected_vendor_id_android_desktop(spec_value)
     if _is_skylab(settings):
         return _gpu_expected_vendor_id_skylab(spec_value)
     gpus = _get_gpus(spec_value)
@@ -174,6 +201,14 @@
 
     return ["--expected-vendor-id", vendor_ids.pop()]
 
+def _gpu_expected_vendor_id_android_desktop(spec_value):
+    board = _get_android_desktop_board_name(spec_value)
+    if not board:
+        fail("Failed to get board for Android Desktop test")
+    gpu_device = _ANDROID_DESKTOP_BOARD_GPUS.get(board)
+    vendor_id = gpu_device.vendor if gpu_device else "0"
+    return ["--expected-vendor-id", vendor_id]
+
 def _gpu_expected_vendor_id_skylab(spec_value):
     cros_board = spec_value.get("cros_board")
     if cros_board == None:
@@ -188,6 +223,8 @@
     Most configurations only need one expected GPU, but heterogeneous pools
     (e.g. HD 630 and UHD 630 machines) require multiple.
     """
+    if _is_android_desktop(spec_value, settings):
+        return _gpu_expected_device_id_android_desktop(spec_value)
     if _is_skylab(settings):
         return _gpu_expected_device_id_skylab(spec_value)
     gpus = _get_gpus(spec_value)
@@ -217,6 +254,14 @@
         retval.extend(["--expected-device-id", device_id])
     return retval
 
+def _gpu_expected_device_id_android_desktop(spec_value):
+    board = _get_android_desktop_board_name(spec_value)
+    if not board:
+        fail("Failed to get board for Android Desktop test")
+    gpu_device = _ANDROID_DESKTOP_BOARD_GPUS.get(board)
+    device_id = gpu_device.device if gpu_device else "0"
+    return ["--expected-device-id", device_id]
+
 def _gpu_expected_device_id_skylab(spec_value):
     cros_board = spec_value.get("cros_board")
     if cros_board == None:
@@ -372,6 +417,10 @@
     )
 
 magic_args = struct(
+    ANDROID_DESKTOP_TELEMETRY_REMOTE = _placeholder(
+        pyl_arg_value = "$$MAGIC_SUBSTITUTION_AndroidDesktopTelemetryRemote",
+        function = _android_desktop_telemetry_remote,
+    ),
     CROS_TELEMETRY_REMOTE = _placeholder(
         pyl_arg_value = "$$MAGIC_SUBSTITUTION_ChromeOSTelemetryRemote",
         function = _cros_telemetry_remote,
diff --git a/infra/config/migration/values.py b/infra/config/migration/values.py
index 86baaf0..db37367 100644
--- a/infra/config/migration/values.py
+++ b/infra/config/migration/values.py
@@ -77,6 +77,8 @@
 
 
 _MAGIC_ARG_MAPPING = {
+    '$$MAGIC_SUBSTITUTION_AndroidDesktopTelemetryRemote':
+    'ANDROID_DESKTOP_TELEMETRY_REMOTE',
     '$$MAGIC_SUBSTITUTION_ChromeOSTelemetryRemote': 'CROS_TELEMETRY_REMOTE',
     '$$MAGIC_SUBSTITUTION_ChromeOSGtestFilterFile': 'CROS_GTEST_FILTER_FILE',
     '$$MAGIC_SUBSTITUTION_GPUExpectedVendorId': 'GPU_EXPECTED_VENDOR_ID',
diff --git a/infra/config/subprojects/chromium/ci/chromium.fuchsia.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fuchsia.fyi.star
index f8b5b39..3b3127f5 100644
--- a/infra/config/subprojects/chromium/ci/chromium.fuchsia.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.fuchsia.fyi.star
@@ -414,6 +414,7 @@
         gclient_config = builder_config.gclient_config(
             config = "chromium",
             apply_configs = [
+                "checkout_pgo_profiles",
                 "fuchsia_x64",
             ],
         ),
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
index f4df5b1..ba738b8 100644
--- a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -2769,22 +2769,31 @@
         # should be running the same test_suites as
         # 'Win10 FYI x64 Release (NVIDIA)'
         targets = [
-            "gpu_noop_sleep_telemetry_test",
+            "gpu_fyi_win_gtests",
+            "gpu_fyi_win_release_telemetry_tests",
+            "gpu_fyi_win_optional_isolated_scripts",
         ],
         mixins = [
             "limited_capacity_bot",
             "win10_nvidia_gtx_1660_experimental",
         ],
+        per_test_modifications = {
+            "gl_unittests": targets.mixin(
+                args = [
+                    "--test-launcher-filter-file=../../testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter",
+                ],
+            ),
+        },
     ),
     targets_settings = targets.settings(
         browser_config = targets.browser_config.RELEASE_X64,
         os_type = targets.os_type.WINDOWS,
     ),
     # Uncomment this entry when this experimental tester is actually in use.
-    # console_view_entry = consoles.console_view_entry(
-    #     category = "Windows|10|x64|Nvidia",
-    #     short_name = "exp",
-    # ),
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|10|x64|Nvidia",
+        short_name = "exp",
+    ),
     list_view = "chromium.gpu.experimental",
 )
 
diff --git a/infra/config/subprojects/chromium/ci/chromium.memory.star b/infra/config/subprojects/chromium/ci/chromium.memory.star
index 1a5337f..45e8dc4 100644
--- a/infra/config/subprojects/chromium/ci/chromium.memory.star
+++ b/infra/config/subprojects/chromium/ci/chromium.memory.star
@@ -1102,6 +1102,11 @@
             "linux-jammy",
         ],
         per_test_modifications = {
+            "chrome_wpt_tests": targets.mixin(
+                args = [
+                    "-j6",
+                ],
+            ),
             "blink_web_tests": targets.mixin(
                 args = [
                     "--timeout-ms",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
index f3e77aa0..f70002d 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
@@ -698,7 +698,14 @@
         "ci/Linux ASan LSan Builder",
         "ci/Linux ASan LSan Tests (1)",
     ],
-    gn_args = "ci/Linux ASan LSan Builder",
+    gn_args = gn_args.config(
+        configs = [
+            "ci/Linux ASan LSan Builder",
+            # TODO(crbug.com/416191043): Restore symbol_level=1 if/when CAS
+            # errors are fixed.
+            "no_symbols",
+        ],
+    ),
     compilator = "linux_chromium_asan_rel_ng-compilator",
     experiments = {
         # go/nplus1shardsproposal
@@ -935,7 +942,9 @@
         configs = [
             "ci/Linux TSan Builder",
             "release_try_builder",
-            "minimal_symbols",
+            # TODO(crbug.com/416191043): Restore symbol_level=1 if/when CAS
+            # errors are fixed.
+            "no_symbols",
         ],
     ),
     compilator = "linux_chromium_tsan_rel_ng-compilator",
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
index 89f4608e..49e011c8 100644
--- a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -361,7 +361,6 @@
             "arm64",
             "gpu_tests",
             "release_try_builder",
-            "reclient",
             "remoteexec",
             "no_symbols",
             "mac",
diff --git a/infra/config/targets/mixins.star b/infra/config/targets/mixins.star
index a2859e8..65c3539 100644
--- a/infra/config/targets/mixins.star
+++ b/infra/config/targets/mixins.star
@@ -822,6 +822,7 @@
     ],
     android_args = [
         targets.magic_args.GPU_TELEMETRY_NO_ROOT_FOR_UNROOTED_DEVICES,
+        targets.magic_args.ANDROID_DESKTOP_TELEMETRY_REMOTE,
         # See crbug.com/333414298 for context on why this is necessary.
         "--initial-find-device-attempts=3",
     ],
@@ -2389,8 +2390,8 @@
     swarming = targets.swarming(
         dimensions = {
             "display_attached": "1",
-            "gpu": "10de:2184-31.0.15.4601",
-            "os": "Windows-10-19045",
+            "gpu": "10de:2184-32.0.15.7602",
+            "os": "Windows-11-26100",
             "pool": "chromium.tests.gpu",
         },
     ),
diff --git a/internal b/internal
index 20b9f62..ce971f9 160000
--- a/internal
+++ b/internal
@@ -1 +1 @@
-Subproject commit 20b9f62811d76f8f5de0eba491b161f8bb948c6e
+Subproject commit ce971f9a7bc61262bdef47fe1bee00420f0bbfc0
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 677e1246..320250e 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -2437,6 +2437,12 @@
       <message name="IDS_IOS_FIRST_RUN_DEFAULT_BROWSER_SCREEN_SECOND_STEP" desc="Text of the row indicating the second step of setting the default browser [iOS only]">
         Tap <ph name="BEGIN_BOLD">BEGIN_BOLD</ph>Default Browser App<ph name="END_BOLD">END_BOLD</ph>
       </message>
+      <message name="IDS_IOS_FIRST_RUN_GUIDED_TOUR_NTP_IPH_TEXT" desc="Text of the Guided Tour IPH for the NTP step focusing on the tab grid." meaning="Title-cased">
+          Your open tabs and groups
+    </message>
+      <message name="IDS_IOS_FIRST_RUN_GUIDED_TOUR_NTP_IPH_TITLE" desc="Title of the Guided Tour IPH for the NTP step focusing on the tab grid." meaning="Title-cased">
+          See and manage everything you have open
+    </message>
       <message name="IDS_IOS_FIRST_RUN_GUIDED_TOUR_PROMPT_BUTTON_TITLE" desc="Title of the button for the user to start the First Run Guided Tour." meaning="Title-cased">
       Show me around
     </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_GUIDED_TOUR_NTP_IPH_TEXT.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_GUIDED_TOUR_NTP_IPH_TEXT.png.sha1
new file mode 100644
index 0000000..874b81b
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_GUIDED_TOUR_NTP_IPH_TEXT.png.sha1
@@ -0,0 +1 @@
+8772d463239cc54530d8fa3c7109cd39991ee5b8
\ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_GUIDED_TOUR_NTP_IPH_TITLE.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_GUIDED_TOUR_NTP_IPH_TITLE.png.sha1
new file mode 100644
index 0000000..874b81b
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_FIRST_RUN_GUIDED_TOUR_NTP_IPH_TITLE.png.sha1
@@ -0,0 +1 @@
+8772d463239cc54530d8fa3c7109cd39991ee5b8
\ No newline at end of file
diff --git a/ios/chrome/browser/authentication/ui_bundled/change_profile/BUILD.gn b/ios/chrome/browser/authentication/ui_bundled/change_profile/BUILD.gn
index 7d299c3b..dc06cc1 100644
--- a/ios/chrome/browser/authentication/ui_bundled/change_profile/BUILD.gn
+++ b/ios/chrome/browser/authentication/ui_bundled/change_profile/BUILD.gn
@@ -34,8 +34,6 @@
     "change_profile_signout_continuation.mm",
     "change_profile_voice_search_continuation.h",
     "change_profile_voice_search_continuation.mm",
-    "sync_history_continuation.h",
-    "sync_history_continuation.mm",
   ]
 
   deps = [
diff --git a/ios/chrome/browser/authentication/ui_bundled/change_profile/sync_history_continuation.h b/ios/chrome/browser/authentication/ui_bundled/change_profile/sync_history_continuation.h
deleted file mode 100644
index 40b2711..0000000
--- a/ios/chrome/browser/authentication/ui_bundled/change_profile/sync_history_continuation.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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 IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_CHANGE_PROFILE_SYNC_HISTORY_CONTINUATION_H_
-#define IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_CHANGE_PROFILE_SYNC_HISTORY_CONTINUATION_H_
-
-#import "components/signin/public/base/signin_metrics.h"
-#import "ios/chrome/app/change_profile_continuation.h"
-
-// Returns a ChangeProfileSyncHistoryContinuation that opens the history sync
-// opt-in view. Accepts the 'accessPoint' and `optionalHistorySync` (even if it
-// is NO, history sync ui might still be skipped if the user previously approved
-// it).
-ChangeProfileContinuation CreateChangeProfileSyncHistoryContinuation(
-    signin_metrics::AccessPoint accessPoint,
-    BOOL optionalHistorySync);
-
-#endif  // IOS_CHROME_BROWSER_AUTHENTICATION_UI_BUNDLED_CHANGE_PROFILE_SYNC_HISTORY_CONTINUATION_H_
diff --git a/ios/chrome/browser/authentication/ui_bundled/change_profile/sync_history_continuation.mm b/ios/chrome/browser/authentication/ui_bundled/change_profile/sync_history_continuation.mm
deleted file mode 100644
index df0aa4f..0000000
--- a/ios/chrome/browser/authentication/ui_bundled/change_profile/sync_history_continuation.mm
+++ /dev/null
@@ -1,63 +0,0 @@
-// 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 "ios/chrome/browser/authentication/ui_bundled/change_profile/sync_history_continuation.h"
-
-#import "base/functional/callback_helpers.h"
-#import "components/signin/public/base/consent_level.h"
-#import "components/signin/public/identity_manager/identity_manager.h"
-#import "ios/chrome/browser/shared/coordinator/scene/scene_state.h"
-#import "ios/chrome/browser/shared/model/browser/browser.h"
-#import "ios/chrome/browser/shared/model/browser/browser_provider.h"
-#import "ios/chrome/browser/shared/model/browser/browser_provider_interface.h"
-#import "ios/chrome/browser/shared/public/commands/application_commands.h"
-#import "ios/chrome/browser/shared/public/commands/command_dispatcher.h"
-#import "ios/chrome/browser/shared/public/commands/show_signin_command.h"
-#import "ios/chrome/browser/signin/model/identity_manager_factory.h"
-
-namespace {
-
-// Implementation of the continuation opening the history opt-in view.
-void ChangeProfileSyncHistoryContinuation(
-    signin_metrics::AccessPoint accessPoint,
-    BOOL optionalHistorySync,
-    SceneState* scene_state,
-    base::OnceClosure closure) {
-  Browser* browser =
-      scene_state.browserProviderInterface.currentBrowserProvider.browser;
-  CHECK(browser);
-
-  // Check that there is a signed in account.
-  signin::IdentityManager* identity_manager =
-      IdentityManagerFactory::GetForProfile(browser->GetProfile());
-  CHECK(identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin));
-
-  CommandDispatcher* dispatcher = browser->GetCommandDispatcher();
-  id<ApplicationCommands> applicationHandler =
-      HandlerForProtocol(dispatcher, ApplicationCommands);
-
-  // kHistorySync triggers the history sync opt-in. The user must be already
-  // signed in.
-  ShowSigninCommand* command = [[ShowSigninCommand alloc]
-      initWithOperation:AuthenticationOperation::kHistorySync
-               identity:nil
-            accessPoint:accessPoint
-            promoAction:signin_metrics::PromoAction::
-                            PROMO_ACTION_NO_SIGNIN_PROMO
-             completion:nil];
-  command.optionalHistorySync = optionalHistorySync;
-
-  [applicationHandler showSignin:command baseViewController:nil];
-
-  std::move(closure).Run();
-}
-
-}  // namespace
-
-ChangeProfileContinuation CreateChangeProfileSyncHistoryContinuation(
-    signin_metrics::AccessPoint accessPoint,
-    BOOL optionalHistorySync) {
-  return base::BindOnce(&ChangeProfileSyncHistoryContinuation, accessPoint,
-                        optionalHistorySync);
-}
diff --git a/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/form_input_accessory_egtest.mm b/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/form_input_accessory_egtest.mm
index 30b27c03..9bce380 100644
--- a/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/form_input_accessory_egtest.mm
+++ b/ios/chrome/browser/autofill/ui_bundled/form_input_accessory/form_input_accessory_egtest.mm
@@ -120,6 +120,23 @@
   }
 }
 
+// Matcher for the name suggestion chip.
+id<GREYMatcher> KeyboardAccessoryNameSuggestion() {
+  autofill::AutofillProfile profile = autofill::test::GetFullProfile();
+  NSString* name =
+      base::SysUTF16ToNSString(profile.GetRawInfo(autofill::NAME_FULL));
+  if ([AutofillAppInterface isKeyboardAccessoryUpgradeEnabled] &&
+      [ChromeEarlGrey isIPadIdiom]) {
+    // On iPad, the suggestion text is an attributed string containing the state
+    // on the 2nd line.
+    NSString* state = base::SysUTF16ToNSString(
+        profile.GetRawInfo(autofill::ADDRESS_HOME_STATE));
+    return grey_text([NSString stringWithFormat:@"%@\n%@", name, state]);
+  } else {
+    return grey_text(name);
+  }
+}
+
 // Verifies that the number of accepted address suggestions recorded for the
 // given `suggestion_index` is as expected.
 void CheckAddressAutofillSuggestionAcceptedIndexMetricsCount(
@@ -262,6 +279,12 @@
     config.iph_feature_enabled =
         feature_engagement::kIPHAutofillHomeWorkProfileSuggestionFeature.name;
   }
+
+  if ([self isRunningTest:@selector(testReFillAddressFieldsOnForm)]) {
+    config.features_enabled.push_back(kAutofillRefillForFormsIos);
+    config.features_enabled.push_back(
+        autofill::features::kAutofillAcrossIframesIos);
+  }
   return config;
 }
 
@@ -323,6 +346,13 @@
   [ChromeEarlGrey waitForWebStateContainingText:"Profile Autofill"];
 }
 
+// Loads simple address page with refill on localhost.
+- (void)loadRefillAddressPage {
+  [ChromeEarlGrey
+      loadURL:self.testServer->GetURL("/autofill_refill_test.html")];
+  [ChromeEarlGrey waitForWebStateContainingText:"Refill Profile Autofill"];
+}
+
 // Verifies that html field with the `id_attr` attribute has been filled with
 // `value`.
 - (void)verifyFieldWithIdHasBeenFilled:(std::string)id_attr
@@ -776,6 +806,26 @@
       /*suggestion_index=*/0);
 }
 
+// Tests that tapping on a name field of a dinamically expanding address form
+// and accepting the keyboard accessory suggestion automatically autofills the
+// whole address.
+- (void)testReFillAddressFieldsOnForm {
+  [self loadRefillAddressPage];
+
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
+      performAction:chrome_test_util::TapWebElementWithId(kFormName)];
+
+  id<GREYMatcher> name_chip = KeyboardAccessoryNameSuggestion();
+
+  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:name_chip];
+
+  // Autofill the name field to uncover the rest of the address form.
+  [[EarlGrey selectElementWithMatcher:name_chip] performAction:grey_tap()];
+
+  // Verify that the whole address was filled properly.
+  [self verifyAddressInfosHaveBeenFilled:autofill::test::GetFullProfile()];
+}
+
 // Tests the IPH feature for a Home and Work account profile.
 - (void)testAddressHomeAndWorkIPH {
   // Delete the profile that is added on `-setUp`.
diff --git a/ios/chrome/browser/bubble/ui_bundled/BUILD.gn b/ios/chrome/browser/bubble/ui_bundled/BUILD.gn
index 3505ec1..db60a73 100644
--- a/ios/chrome/browser/bubble/ui_bundled/BUILD.gn
+++ b/ios/chrome/browser/bubble/ui_bundled/BUILD.gn
@@ -9,6 +9,7 @@
     "bubble_presenter_coordinator.h",
     "bubble_presenter_coordinator.mm",
     "bubble_presenter_delegate.h",
+    "bubble_view_controller_presenter+Subclassing.h",
     "bubble_view_controller_presenter+Testing.h",
     "bubble_view_controller_presenter.h",
     "bubble_view_controller_presenter.mm",
@@ -85,6 +86,7 @@
     "//ios/chrome/browser/shared/ui/util",
     "//ios/chrome/common:timing",
     "//ios/chrome/common/ui/colors",
+    "//ios/chrome/common/ui/util",
     "//ios/third_party/material_components_ios",
     "//ui/base",
   ]
diff --git a/ios/chrome/browser/bubble/ui_bundled/bubble_view_controller_presenter+Subclassing.h b/ios/chrome/browser/bubble/ui_bundled/bubble_view_controller_presenter+Subclassing.h
new file mode 100644
index 0000000..b673619
--- /dev/null
+++ b/ios/chrome/browser/bubble/ui_bundled/bubble_view_controller_presenter+Subclassing.h
@@ -0,0 +1,47 @@
+// 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 IOS_CHROME_BROWSER_BUBBLE_UI_BUNDLED_BUBBLE_VIEW_CONTROLLER_PRESENTER_SUBCLASSING_H_
+#define IOS_CHROME_BROWSER_BUBBLE_UI_BUNDLED_BUBBLE_VIEW_CONTROLLER_PRESENTER_SUBCLASSING_H_
+
+#import "ios/chrome/browser/bubble/ui_bundled/bubble_view_controller_presenter.h"
+
+// Exposes shared functionality for BubbleViewControllerPresenter subclasses.
+@interface BubbleViewControllerPresenter (Subclassing)
+
+// ViewController this presenter manages.
+@property(nonatomic, strong, readonly)
+    BubbleViewController* bubbleViewController;
+
+// Parent View of `bubbleViewController`.
+@property(nonatomic, strong, readonly) UIView* parentView;
+
+// The frame to which the BubbleView is anchored.
+@property(nonatomic, assign, readonly) CGRect anchorViewFrame;
+
+// Whether the BubbleView is being presented.
+@property(nonatomic, assign) BOOL presenting;
+
+// The block invoked when the bubble is dismissed.
+@property(nonatomic, strong)
+    CallbackWithIPHDismissalReasonType dismissalCallback;
+
+// Calculates the frame of the BubbleView. `rect` is the frame of the bubble's
+// superview. `anchorPoint` is the anchor point of the bubble. `anchorPoint`
+// and `rect` must be in the same coordinates.
+- (CGRect)frameForBubbleInRect:(CGRect)rect atAnchorPoint:(CGPoint)anchorPoint;
+
+// Configures the BubbleViewController in the context of an `anchorPoint` and
+// (optional)`anchorViewFrame` position in the context of the
+// `parentViewController`.
+- (void)configureInParentViewController:(UIViewController*)parentViewController
+                            anchorPoint:(CGPoint)anchorPoint
+                        anchorViewFrame:(CGRect)anchorViewFrame;
+
+// Registers VoiceOver announcement for the BubbleView.
+- (void)registerVoiceOverAnnouncement;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_BUBBLE_UI_BUNDLED_BUBBLE_VIEW_CONTROLLER_PRESENTER_SUBCLASSING_H_
diff --git a/ios/chrome/browser/bubble/ui_bundled/bubble_view_controller_presenter.mm b/ios/chrome/browser/bubble/ui_bundled/bubble_view_controller_presenter.mm
index bc65ca1..246fba4 100644
--- a/ios/chrome/browser/bubble/ui_bundled/bubble_view_controller_presenter.mm
+++ b/ios/chrome/browser/bubble/ui_bundled/bubble_view_controller_presenter.mm
@@ -76,7 +76,7 @@
 // The type of the bubble view's content.
 @property(nonatomic, assign, readonly) BubbleViewType bubbleType;
 // Whether the bubble view controller is presented or dismissed.
-@property(nonatomic, assign, getter=isPresenting) BOOL presenting;
+@property(nonatomic, assign) BOOL presenting;
 // The block invoked when the bubble is dismissed (both via timer and via tap).
 // Is optional.
 @property(nonatomic, strong)
@@ -163,6 +163,24 @@
 - (void)presentInViewController:(UIViewController*)parentViewController
                     anchorPoint:(CGPoint)anchorPoint
                 anchorViewFrame:(CGRect)anchorViewFrame {
+  [self configureInParentViewController:parentViewController
+                            anchorPoint:anchorPoint
+                        anchorViewFrame:anchorViewFrame];
+  [self addGestureRecognizersToParentView:self.parentView];
+
+  [parentViewController addChildViewController:self.bubbleViewController];
+  [self.parentView addSubview:self.bubbleViewController.view];
+  [self.bubbleViewController
+      didMoveToParentViewController:parentViewController];
+  [self.bubbleViewController animateContentIn];
+
+  [self setUpDismissalTimer];
+  [self registerVoiceOverAnnouncement];
+}
+
+- (void)configureInParentViewController:(UIViewController*)parentViewController
+                            anchorPoint:(CGPoint)anchorPoint
+                        anchorViewFrame:(CGRect)anchorViewFrame {
   self.parentView = parentViewController.view;
   _anchorViewFrame = anchorViewFrame;
   CGPoint anchorPointInParent =
@@ -173,38 +191,10 @@
   // The bubble's frame must be set. Call `canPresentInView` to make sure that
   // the frame can be set before calling `presentInViewController`.
   DCHECK(!CGRectIsEmpty(self.bubbleViewController.view.frame));
-
-  [self addGestureRecognizersToParentView:self.parentView];
-
   self.presenting = YES;
-  [parentViewController addChildViewController:self.bubbleViewController];
-  [self.parentView addSubview:self.bubbleViewController.view];
-  [self.bubbleViewController
-      didMoveToParentViewController:parentViewController];
-  [self.bubbleViewController animateContentIn];
+}
 
-  self.bubbleDismissalTimer = [NSTimer
-      scheduledTimerWithTimeInterval:[self bubbleVisibilityDuration]
-                              target:self
-                            selector:@selector(bubbleDismissalTimerFired:)
-                            userInfo:nil
-                             repeats:NO];
-
-  self.userEngaged = YES;
-  self.triggerFollowUpAction = YES;
-  self.engagementTimer =
-      [NSTimer scheduledTimerWithTimeInterval:kBubbleEngagementDuration
-                                       target:self
-                                     selector:@selector(engagementTimerFired:)
-                                     userInfo:nil
-                                      repeats:NO];
-
-  [[NSNotificationCenter defaultCenter]
-      addObserver:self
-         selector:@selector(onKeyboardHide:)
-             name:UIKeyboardWillHideNotification
-           object:nil];
-
+- (void)registerVoiceOverAnnouncement {
   if (self.voiceOverAnnouncement) {
     if (self.bubbleShouldAutoDismissUnderAccessibility) {
       // The VoiceOverAnnouncement should be dispatched after a delay to account
@@ -343,6 +333,33 @@
 
 #pragma mark - Private
 
+// Set up a timer that dismisses the bubble view.
+- (void)setUpDismissalTimer {
+  self.bubbleDismissalTimer = [NSTimer
+      scheduledTimerWithTimeInterval:[self bubbleVisibilityDuration]
+                              target:self
+                            selector:@selector(bubbleDismissalTimerFired:)
+                            userInfo:nil
+                             repeats:NO];
+
+  self.userEngaged = YES;
+  self.triggerFollowUpAction = YES;
+  self.engagementTimer =
+      [NSTimer scheduledTimerWithTimeInterval:kBubbleEngagementDuration
+                                       target:self
+                                     selector:@selector(engagementTimerFired:)
+                                     userInfo:nil
+                                      repeats:NO];
+
+  [[NSNotificationCenter defaultCenter]
+      addObserver:self
+         selector:@selector(onKeyboardHide:)
+             name:UIKeyboardWillHideNotification
+           object:nil];
+}
+
+// Returns the time the bubble view should be shown before being automatically
+// dismissed.
 - (NSTimeInterval)bubbleVisibilityDuration {
   return _customBubbleVisibilityDuration > 0 ? _customBubbleVisibilityDuration
                                              : kBubbleVisibilityDuration;
diff --git a/ios/chrome/browser/bubble/ui_bundled/guided_tour/BUILD.gn b/ios/chrome/browser/bubble/ui_bundled/guided_tour/BUILD.gn
new file mode 100644
index 0000000..840eb0e
--- /dev/null
+++ b/ios/chrome/browser/bubble/ui_bundled/guided_tour/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright 2017 The Chromium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("guided_tour") {
+  sources = [
+    "guided_tour_bubble_view_controller_animator.h",
+    "guided_tour_bubble_view_controller_animator.mm",
+    "guided_tour_bubble_view_controller_presentation_controller.h",
+    "guided_tour_bubble_view_controller_presentation_controller.mm",
+    "guided_tour_bubble_view_controller_presenter.h",
+    "guided_tour_bubble_view_controller_presenter.mm",
+  ]
+  deps = [
+    "//base",
+    "//ios/chrome/browser/bubble/ui_bundled",
+    "//ios/chrome/browser/bubble/ui_bundled:bubble_view",
+  ]
+}
diff --git a/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_animator.h b/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_animator.h
new file mode 100644
index 0000000..ff7b04e4
--- /dev/null
+++ b/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_animator.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 IOS_CHROME_BROWSER_BUBBLE_UI_BUNDLED_GUIDED_TOUR_GUIDED_TOUR_BUBBLE_VIEW_CONTROLLER_ANIMATOR_H_
+#define IOS_CHROME_BROWSER_BUBBLE_UI_BUNDLED_GUIDED_TOUR_GUIDED_TOUR_BUBBLE_VIEW_CONTROLLER_ANIMATOR_H_
+
+#import <UIKit/UIKit.h>
+
+// Presentation and dismissal animation for the
+// GuidedTourBubbleViewControllerPresentationController.
+@interface GuidedTourBubbleViewControllerAnimator
+    : NSObject <UIViewControllerAnimatedTransitioning>
+
+// Whether the animated view is `appearing`.
+@property(nonatomic, assign) BOOL appearing;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_BUBBLE_UI_BUNDLED_GUIDED_TOUR_GUIDED_TOUR_BUBBLE_VIEW_CONTROLLER_ANIMATOR_H_
diff --git a/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_animator.mm b/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_animator.mm
new file mode 100644
index 0000000..0e3a968
--- /dev/null
+++ b/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_animator.mm
@@ -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.
+
+#import "ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_animator.h"
+
+@implementation GuidedTourBubbleViewControllerAnimator
+
+#pragma mark - UIViewControllerAnimatedTransitioning
+
+- (void)animateTransition:
+    (id<UIViewControllerContextTransitioning>)transitionContext {
+  UIViewController* presentedViewController = [transitionContext
+      viewControllerForKey:self.appearing
+                               ? UITransitionContextToViewControllerKey
+                               : UITransitionContextFromViewControllerKey];
+  UIView* presentedView = [transitionContext
+      viewForKey:self.appearing ? UITransitionContextToViewKey
+                                : UITransitionContextFromViewKey];
+
+  UIView* containerView = [transitionContext containerView];
+  if (self.appearing) {
+    [containerView addSubview:presentedView];
+    presentedView.frame =
+        [transitionContext finalFrameForViewController:presentedViewController];
+  }
+
+  if (self.appearing) {
+    presentedView.alpha = 0;
+  }
+
+  [UIView animateWithDuration:.5
+      delay:0
+      usingSpringWithDamping:.85
+      initialSpringVelocity:0
+      options:0
+      animations:^{
+        presentedView.alpha = self.appearing ? 1 : 0;
+      }
+      completion:^(BOOL finished) {
+        [transitionContext completeTransition:YES];
+      }];
+}
+
+- (NSTimeInterval)transitionDuration:
+    (id<UIViewControllerContextTransitioning>)transitionContext {
+  return 0.5;
+}
+
+@end
diff --git a/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presentation_controller.h b/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presentation_controller.h
new file mode 100644
index 0000000..78d4fb35b
--- /dev/null
+++ b/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presentation_controller.h
@@ -0,0 +1,35 @@
+// 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 IOS_CHROME_BROWSER_BUBBLE_UI_BUNDLED_GUIDED_TOUR_GUIDED_TOUR_BUBBLE_VIEW_CONTROLLER_PRESENTATION_CONTROLLER_H_
+#define IOS_CHROME_BROWSER_BUBBLE_UI_BUNDLED_GUIDED_TOUR_GUIDED_TOUR_BUBBLE_VIEW_CONTROLLER_PRESENTATION_CONTROLLER_H_
+
+#import <UIKit/UIKit.h>
+
+// Custom UIPresentationController for a BubbleView with a dimmed background
+// view that has a cutout for the view the BubbleView is anchored to.
+@interface GuidedTourBubbleViewControllerPresentationController
+    : UIPresentationController
+
+// Initializer adding on mandatory initializers to the superclass
+// `presentedViewController` and `presentingViewController`: The
+// `presentedBubbleViewFrame` of the BubbleView that is being presented. The
+// `anchorViewFrame` of the view that the BubbleView is anchored to. The
+// `cornerRadius` of the cutout in the background view.
+- (instancetype)
+    initWithPresentedViewController:(UIViewController*)presentedViewController
+           presentingViewController:(UIViewController*)presentingViewController
+           presentedBubbleViewFrame:(CGRect)presentedBubbleViewFrame
+                    anchorViewFrame:(CGRect)anchorViewFrame
+                       cornerRadius:(CGFloat)cornerRadius
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)
+    initWithPresentedViewController:(UIViewController*)presentedViewController
+           presentingViewController:(UIViewController*)presentingViewController
+    NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_BUBBLE_UI_BUNDLED_GUIDED_TOUR_GUIDED_TOUR_BUBBLE_VIEW_CONTROLLER_PRESENTATION_CONTROLLER_H_
diff --git a/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presentation_controller.mm b/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presentation_controller.mm
new file mode 100644
index 0000000..6e3d573
--- /dev/null
+++ b/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presentation_controller.mm
@@ -0,0 +1,136 @@
+// 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 "ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presentation_controller.h"
+
+@interface GuidedTourBubbleViewControllerPresentationController ()
+
+// The background dimmed view behind the BubbleView.
+@property(nonatomic, strong) UIView* dimmingView;
+
+@end
+
+@implementation GuidedTourBubbleViewControllerPresentationController {
+  CGRect _presentedBubbleViewFrame;
+  CGRect _anchorViewFrame;
+  CGFloat _cornerRadius;
+}
+
+- (instancetype)
+    initWithPresentedViewController:(UIViewController*)presentedViewController
+           presentingViewController:(UIViewController*)presentingViewController
+           presentedBubbleViewFrame:(CGRect)presentedBubbleViewFrame
+                    anchorViewFrame:(CGRect)anchorViewFrame
+                       cornerRadius:(CGFloat)cornerRadius {
+  self = [super initWithPresentedViewController:presentedViewController
+                       presentingViewController:presentingViewController];
+  if (self) {
+    _presentedBubbleViewFrame = presentedBubbleViewFrame;
+    _anchorViewFrame = anchorViewFrame;
+    _cornerRadius = cornerRadius;
+  }
+  return self;
+}
+
+#pragma mark - Lifecycle
+
+- (CGRect)frameOfPresentedViewInContainerView {
+  UIView* containerView = self.containerView;
+  if (!containerView) {
+    return CGRectZero;
+  }
+
+  return _presentedBubbleViewFrame;
+}
+
+- (void)presentationTransitionWillBegin {
+  UIView* containerView = self.containerView;
+  UIViewController* presentedVC = self.presentedViewController;
+  if (!containerView) {
+    return;
+  }
+
+  _dimmingView = [[UIView alloc] initWithFrame:CGRectZero];
+  _dimmingView.backgroundColor =
+      [[UIColor blackColor] colorWithAlphaComponent:0.5];
+  _dimmingView.alpha = 0.0;
+  self.dimmingView.frame = containerView.bounds;
+  [containerView insertSubview:self.dimmingView atIndex:0];
+  [self addSpotlightViewCutOutWithCornerRadius:_cornerRadius];
+
+  id<UIViewControllerTransitionCoordinator> coordinator =
+      presentedVC.transitionCoordinator;
+  if (coordinator) {
+    __weak GuidedTourBubbleViewControllerPresentationController* weakSelf =
+        self;
+    [coordinator
+        animateAlongsideTransition:^(
+            id<UIViewControllerTransitionCoordinatorContext> context) {
+          weakSelf.dimmingView.alpha = 1.0;
+        }
+                        completion:nil];
+  } else {
+    self.dimmingView.alpha = 1.0;
+  }
+}
+
+- (void)dismissalTransitionWillBegin {
+  UIViewController* presentedVC = self.presentedViewController;
+  id<UIViewControllerTransitionCoordinator> coordinator =
+      presentedVC.transitionCoordinator;
+
+  if (coordinator) {
+    __weak GuidedTourBubbleViewControllerPresentationController* weakSelf =
+        self;
+    [coordinator
+        animateAlongsideTransition:^(
+            id<UIViewControllerTransitionCoordinatorContext> context) {
+          weakSelf.dimmingView.alpha = 0.0;
+        }
+                        completion:nil];
+  } else {
+    self.dimmingView.alpha = 0.0;
+  }
+}
+
+- (void)dismissalTransitionDidEnd:(BOOL)completed {
+  if (completed) {
+    [self.dimmingView removeFromSuperview];
+    self.dimmingView = nil;
+  }
+}
+
+- (void)containerViewWillLayoutSubviews {
+  [super containerViewWillLayoutSubviews];
+
+  if (self.containerView) {
+    self.dimmingView.frame = self.containerView.bounds;
+  }
+  self.presentedView.frame = [self frameOfPresentedViewInContainerView];
+}
+
+#pragma mark - Private
+
+// Carves a hole in the background view to spotlight the view anchoring the
+// bubble view.
+- (void)addSpotlightViewCutOutWithCornerRadius:(CGFloat)radius {
+  CAShapeLayer* maskLayer = [CAShapeLayer layer];
+  CGRect backgroundBounds = self.dimmingView.bounds;
+  maskLayer.frame = backgroundBounds;
+
+  // Create the path for the mask
+  // Start with a rectangle covering the whole mask area
+  UIBezierPath* maskPath = [UIBezierPath bezierPathWithRect:backgroundBounds];
+  // Create a path for the spotlight hole.
+  UIBezierPath* spotlightPath =
+      [UIBezierPath bezierPathWithRoundedRect:_anchorViewFrame
+                                 cornerRadius:radius];
+  [maskPath appendPath:spotlightPath];
+
+  maskLayer.path = maskPath.CGPath;
+  maskLayer.fillRule = kCAFillRuleEvenOdd;
+  self.dimmingView.layer.mask = maskLayer;
+}
+
+@end
diff --git a/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presenter.h b/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presenter.h
new file mode 100644
index 0000000..47cec7c
--- /dev/null
+++ b/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presenter.h
@@ -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.
+
+#ifndef IOS_CHROME_BROWSER_BUBBLE_UI_BUNDLED_GUIDED_TOUR_GUIDED_TOUR_BUBBLE_VIEW_CONTROLLER_PRESENTER_H_
+#define IOS_CHROME_BROWSER_BUBBLE_UI_BUNDLED_GUIDED_TOUR_GUIDED_TOUR_BUBBLE_VIEW_CONTROLLER_PRESENTER_H_
+
+#import <UIKit/UIKit.h>
+
+#import "base/ios/block_types.h"
+#import "ios/chrome/browser/bubble/ui_bundled/bubble_view_controller_presenter.h"
+
+// A subclass implementation that presents the BubbleView in front of a
+// background dimmed view with a cutout of the view that the IPH is pointed and
+// anchored to.
+@interface GuidedTourBubbleViewControllerPresenter
+    : BubbleViewControllerPresenter
+
+// Initializes the presenter. `text` is the text displayed by the bubble.
+// `titleString` is the title displayed by the bubble. `arrowDirection` is the
+// direction the bubble's arrow is pointing. `alignment` is the position of the
+// arrow on the bubble. `type` is the type of bubble content. `cornerRadius` is
+// the corner radius of the cutout of the anchor view. `dismissalCallback` is a
+// block invoked when the bubble is dismissed. `completionCallback` is a block
+// invoked when the dismissal finishes.
+- (instancetype)initWithText:(NSString*)text
+                           title:(NSString*)titleString
+                  arrowDirection:(BubbleArrowDirection)arrowDirection
+                       alignment:(BubbleAlignment)alignment
+                      bubbleType:(BubbleViewType)type
+    backgroundCutoutCornerRadius:(CGFloat)cornerRadius
+               dismissalCallback:
+                   (CallbackWithIPHDismissalReasonType)dismissalCallback
+              completionCallback:(ProceduralBlock)completionCallback
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithText:(NSString*)text
+                       title:(NSString*)titleString
+              arrowDirection:(BubbleArrowDirection)arrowDirection
+                   alignment:(BubbleAlignment)alignment
+                  bubbleType:(BubbleViewType)type
+           dismissalCallback:
+               (CallbackWithIPHDismissalReasonType)dismissalCallback
+    NS_UNAVAILABLE;
+
+- (void)dismiss;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_BUBBLE_UI_BUNDLED_GUIDED_TOUR_GUIDED_TOUR_BUBBLE_VIEW_CONTROLLER_PRESENTER_H_
diff --git a/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presenter.mm b/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presenter.mm
new file mode 100644
index 0000000..4ee04d7
--- /dev/null
+++ b/ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presenter.mm
@@ -0,0 +1,128 @@
+// 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 "ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presenter.h"
+
+#import "base/metrics/histogram_functions.h"
+#import "ios/chrome/browser/bubble/ui_bundled/bubble_view_controller.h"
+#import "ios/chrome/browser/bubble/ui_bundled/bubble_view_controller_presenter+Subclassing.h"
+#import "ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_animator.h"
+#import "ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presentation_controller.h"
+
+@interface GuidedTourBubbleViewControllerPresenter () <
+    UIViewControllerTransitioningDelegate>
+@end
+
+@implementation GuidedTourBubbleViewControllerPresenter {
+  UIViewController* _parentViewController;
+  CGPoint _anchorPointInParent;
+  ProceduralBlock _completionCallback;
+  CGFloat _cornerRadius;
+}
+
+- (instancetype)initWithText:(NSString*)text
+                           title:(NSString*)titleString
+                  arrowDirection:(BubbleArrowDirection)arrowDirection
+                       alignment:(BubbleAlignment)alignment
+                      bubbleType:(BubbleViewType)type
+    backgroundCutoutCornerRadius:(CGFloat)cornerRadius
+               dismissalCallback:
+                   (CallbackWithIPHDismissalReasonType)dismissalCallback
+              completionCallback:(ProceduralBlock)completionCallback {
+  self = [super initWithText:text
+                       title:titleString
+              arrowDirection:arrowDirection
+                   alignment:alignment
+                  bubbleType:type
+           dismissalCallback:dismissalCallback];
+  if (self) {
+    _completionCallback = completionCallback;
+    _cornerRadius = cornerRadius;
+  }
+  return self;
+}
+
+- (void)presentInViewController:(UIViewController*)parentViewController
+                    anchorPoint:(CGPoint)anchorPoint
+                anchorViewFrame:(CGRect)anchorViewFrame {
+  [self configureInParentViewController:parentViewController
+                            anchorPoint:anchorPoint
+                        anchorViewFrame:anchorViewFrame];
+  _parentViewController = parentViewController;
+  _anchorPointInParent = [self.parentView.window convertPoint:anchorPoint
+                                                       toView:self.parentView];
+
+  self.bubbleViewController.modalPresentationStyle = UIModalPresentationCustom;
+  self.bubbleViewController.transitioningDelegate = self;
+  [parentViewController presentViewController:self.bubbleViewController
+                                     animated:YES
+                                   completion:nil];
+
+  [self registerVoiceOverAnnouncement];
+}
+
+- (void)dismiss {
+  if (!self.presenting) {
+    return;
+  }
+  __weak GuidedTourBubbleViewControllerPresenter* weakSelf = self;
+  [_parentViewController
+      dismissViewControllerAnimated:YES
+                         completion:^{
+                           GuidedTourBubbleViewControllerPresenter* strongSelf =
+                               weakSelf;
+                           if (strongSelf) {
+                             strongSelf.dismissalCallback(
+                                 IPHDismissalReasonType::kTappedIPH);
+                           }
+                         }];
+  self.presenting = NO;
+  _completionCallback();
+}
+
+#pragma mark - BubbleViewDelegate
+
+- (void)didTapNextButton {
+  [self dismiss];
+}
+
+#pragma mark - UIViewControllerTransitioningDelegate
+
+- (nullable UIPresentationController*)
+    presentationControllerForPresentedViewController:
+        (UIViewController*)presented
+                            presentingViewController:
+                                (nullable UIViewController*)presenting
+                                sourceViewController:(UIViewController*)source {
+  GuidedTourBubbleViewControllerPresentationController* controller =
+      [[GuidedTourBubbleViewControllerPresentationController alloc]
+          initWithPresentedViewController:self.bubbleViewController
+                 presentingViewController:_parentViewController
+                 presentedBubbleViewFrame:
+                     [self frameForBubbleInRect:self.parentView.bounds
+                                  atAnchorPoint:_anchorPointInParent]
+                          anchorViewFrame:self.anchorViewFrame
+                             cornerRadius:_cornerRadius];
+  return controller;
+}
+
+- (id<UIViewControllerAnimatedTransitioning>)
+    animationControllerForPresentedController:(UIViewController*)presented
+                         presentingController:(UIViewController*)presenting
+                             sourceController:(UIViewController*)source {
+  GuidedTourBubbleViewControllerAnimator* animator =
+      [[GuidedTourBubbleViewControllerAnimator alloc] init];
+  animator.appearing = YES;
+  return animator;
+}
+
+- (id<UIViewControllerAnimatedTransitioning>)
+    animationControllerForDismissedController:(UIViewController*)dismissed {
+  GuidedTourBubbleViewControllerAnimator* animator =
+      [[GuidedTourBubbleViewControllerAnimator alloc] init];
+  animator.appearing = NO;
+  return animator;
+}
+
+@end
diff --git a/ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_coordinator.mm b/ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_coordinator.mm
index dbe6b080..c2d123f 100644
--- a/ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_coordinator.mm
+++ b/ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_coordinator.mm
@@ -125,14 +125,12 @@
       if (IOSPasskeysM2Enabled()) {
         // Show the prompt to allow the app to be turned on as a credential
         // provider.
+        __weak __typeof(self) weakSelf = self;
         [ASSettingsHelper
             requestToTurnOnCredentialProviderExtensionWithCompletionHandler:^(
                 BOOL appWasEnabledForAutoFill) {
-              // Record the user's decision.
-              RecordTurnOnCredentialProviderExtensionPromptOutcome(
-                  TurnOnCredentialProviderExtensionPromptSource::
-                      kCredentialProviderExtensionPromo,
-                  appWasEnabledForAutoFill);
+              [weakSelf recordTurnOnCredentialProviderExtensionPromptOutcome:
+                            appWasEnabledForAutoFill];
             }];
         [self recordAction:IOSCredentialProviderPromoAction::kTurnOnAutofill];
         return;
@@ -223,4 +221,13 @@
       static_cast<int>(action));
 }
 
+// Records whether the user has accepted the in-app prompt to set the app as a
+// credential provider.
+- (void)recordTurnOnCredentialProviderExtensionPromptOutcome:(BOOL)outcome {
+  RecordTurnOnCredentialProviderExtensionPromptOutcome(
+      TurnOnCredentialProviderExtensionPromptSource::
+          kCredentialProviderExtensionPromo,
+      outcome);
+}
+
 @end
diff --git a/ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_coordinator_unittest.mm b/ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_coordinator_unittest.mm
index 8cf32c6..618f06a 100644
--- a/ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_coordinator_unittest.mm
+++ b/ios/chrome/browser/credential_provider_promo/ui_bundled/credential_provider_promo_coordinator_unittest.mm
@@ -238,9 +238,8 @@
 
 // Tests that tapping the "Turn on AutoFill…" primary action results in
 // recording the right histogram.
-// TODO(crbug.com/404244113): Test is flaky.
 TEST_F(CredentialProviderPromoCoordinatorTest,
-       FLAKY_CredentialProviderPromoTurnOnAutoFillPromptOutcomeRecorded) {
+       CredentialProviderPromoTurnOnAutoFillPromptOutcomeRecorded) {
   // The "Turn on AutoFill…" action is only available on iOS 18+.
   if (@available(iOS 18.0, *)) {
     // Enable the Passkeys M2 feature.
diff --git a/ios/chrome/browser/enterprise/connectors/connectors_service.h b/ios/chrome/browser/enterprise/connectors/connectors_service.h
index 7c8d5921..f64bef2 100644
--- a/ios/chrome/browser/enterprise/connectors/connectors_service.h
+++ b/ios/chrome/browser/enterprise/connectors/connectors_service.h
@@ -10,6 +10,8 @@
 #import "components/keyed_service/core/keyed_service.h"
 #import "ios/chrome/browser/enterprise/connectors/connectors_manager.h"
 
+class ProfileIOS;
+
 namespace policy {
 class UserCloudPolicyManager;
 }  // namespace policy
@@ -26,10 +28,7 @@
 // - OnSecurityEventEnterpriseConnectors
 class ConnectorsService : public ConnectorsServiceBase, public KeyedService {
  public:
-  ConnectorsService(bool off_the_record,
-                    PrefService* pref_service,
-                    policy::UserCloudPolicyManager* user_cloud_policy_manager,
-                    signin::IdentityManager* identity_manager);
+  ConnectorsService(ProfileIOS* profile);
   ~ConnectorsService() override;
 
   // Returns the CBCM domain or profile domain that enables connector policies.
@@ -60,9 +59,7 @@
   FRIEND_TEST_ALL_PREFIXES(ConnectorsServiceTest, GetBrowserDmToken);
   FRIEND_TEST_ALL_PREFIXES(ConnectorsServiceTest, ConnectorsEnabled);
 
-  bool off_the_record_;
-  raw_ptr<PrefService> prefs_;
-  raw_ptr<policy::UserCloudPolicyManager> user_cloud_policy_manager_;
+  raw_ptr<ProfileIOS> profile_;
   std::unique_ptr<ConnectorsManager> connectors_manager_;
   // Unowned pointer used for retrieving the management domain for connectors
   // policies. Can be null for incognito profiles.
diff --git a/ios/chrome/browser/enterprise/connectors/connectors_service.mm b/ios/chrome/browser/enterprise/connectors/connectors_service.mm
index 8fbe013..38b50919 100644
--- a/ios/chrome/browser/enterprise/connectors/connectors_service.mm
+++ b/ios/chrome/browser/enterprise/connectors/connectors_service.mm
@@ -19,6 +19,8 @@
 #import "ios/chrome/browser/enterprise/connectors/features.h"
 #import "ios/chrome/browser/policy/model/browser_policy_connector_ios.h"
 #import "ios/chrome/browser/shared/model/application_context/application_context.h"
+#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
+#import "ios/chrome/browser/signin/model/identity_manager_factory.h"
 
 namespace {
 std::string GetDomainFromEmail(const std::string& email) {
@@ -33,20 +35,13 @@
 
 namespace enterprise_connectors {
 
-ConnectorsService::ConnectorsService(
-    bool off_the_record,
-    PrefService* pref_service,
-    policy::UserCloudPolicyManager* user_cloud_policy_manager,
-    signin::IdentityManager* identity_manager)
-    : off_the_record_(off_the_record),
-      prefs_(pref_service),
-      user_cloud_policy_manager_(user_cloud_policy_manager),
-      connectors_manager_(
-          std::make_unique<ConnectorsManager>(pref_service,
-                                              GetServiceProviderConfig())),
-      identity_manager_(identity_manager) {
-  DCHECK(prefs_);
-  CHECK(off_the_record_ || identity_manager_ != nullptr);
+ConnectorsService::ConnectorsService(ProfileIOS* profile) : profile_(profile) {
+  CHECK(profile_);
+  identity_manager_ = IdentityManagerFactory::GetForProfile(profile);
+
+  CHECK(profile_->IsOffTheRecord() || identity_manager_ != nullptr);
+  connectors_manager_ = std::make_unique<ConnectorsManager>(
+      profile_->GetPrefs(), GetServiceProviderConfig());
 }
 
 ConnectorsService::~ConnectorsService() = default;
@@ -116,8 +111,8 @@
 
 std::optional<ConnectorsServiceBase::DmToken> ConnectorsService::GetDmToken(
     const char* scope_pref) const {
-  policy::PolicyScope scope =
-      static_cast<policy::PolicyScope>(prefs_->GetInteger(scope_pref));
+  policy::PolicyScope scope = static_cast<policy::PolicyScope>(
+      profile_->GetPrefs()->GetInteger(scope_pref));
   if (scope == policy::PolicyScope::POLICY_SCOPE_USER) {
     auto profile_dm_token = GetProfileDmToken();
     if (profile_dm_token) {
@@ -137,15 +132,15 @@
 }
 
 bool ConnectorsService::ConnectorsEnabled() const {
-  return !off_the_record_;
+  return !profile_->IsOffTheRecord();
 }
 
 PrefService* ConnectorsService::GetPrefs() {
-  return prefs_;
+  return profile_->GetPrefs();
 }
 
 const PrefService* ConnectorsService::GetPrefs() const {
-  return prefs_;
+  return profile_->GetPrefs();
 }
 
 ConnectorsManagerBase* ConnectorsService::GetConnectorsManagerBase() {
@@ -159,7 +154,7 @@
 
 policy::CloudPolicyManager*
 ConnectorsService::GetManagedUserCloudPolicyManager() const {
-  return user_cloud_policy_manager_.get();
+  return profile_->GetUserCloudPolicyManager();
 }
 
 std::unique_ptr<ClientMetadata> ConnectorsService::BuildClientMetadata(
diff --git a/ios/chrome/browser/enterprise/connectors/connectors_service_factory.mm b/ios/chrome/browser/enterprise/connectors/connectors_service_factory.mm
index 72e37b8..baa8c72daf 100644
--- a/ios/chrome/browser/enterprise/connectors/connectors_service_factory.mm
+++ b/ios/chrome/browser/enterprise/connectors/connectors_service_factory.mm
@@ -6,7 +6,6 @@
 
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/model/profile/profile_keyed_service_factory_ios.h"
-#import "ios/chrome/browser/signin/model/identity_manager_factory.h"
 
 namespace enterprise_connectors {
 
@@ -26,7 +25,6 @@
 ConnectorsServiceFactory::ConnectorsServiceFactory()
     : ProfileKeyedServiceFactoryIOS("ConnectorsService",
                                     ProfileSelection::kOwnInstanceInIncognito) {
-  DependsOn(IdentityManagerFactory::GetInstance());
 }
 
 ConnectorsServiceFactory::~ConnectorsServiceFactory() = default;
@@ -34,10 +32,7 @@
 std::unique_ptr<KeyedService> ConnectorsServiceFactory::BuildServiceInstanceFor(
     web::BrowserState* browser_state) const {
   auto* profile = ProfileIOS::FromBrowserState(browser_state);
-  return std::make_unique<ConnectorsService>(
-      profile->IsOffTheRecord(), profile->GetPrefs(),
-      profile->GetUserCloudPolicyManager(),
-      IdentityManagerFactory::GetForProfile(profile));
+  return std::make_unique<ConnectorsService>(profile);
 }
 
 }  // namespace enterprise_connectors
diff --git a/ios/chrome/browser/enterprise/connectors/connectors_service_unittest.mm b/ios/chrome/browser/enterprise/connectors/connectors_service_unittest.mm
index 6cd65561..3e0f1c93 100644
--- a/ios/chrome/browser/enterprise/connectors/connectors_service_unittest.mm
+++ b/ios/chrome/browser/enterprise/connectors/connectors_service_unittest.mm
@@ -75,9 +75,6 @@
     fake_browser_dm_token_storage_.SetDMToken(kTestBrowserDmToken);
     fake_browser_dm_token_storage_.SetClientId(kTestClientId);
 
-    // Setup required to register connectors prefs with `pref_service_`.
-    RegisterProfilePrefs(prefs()->registry());
-
     auto policy_data = std::make_unique<enterprise_management::PolicyData>();
     policy_data->set_managed_by(kTestMachineDomain);
 
@@ -99,8 +96,6 @@
         ->SetMachineLevelUserCloudPolicyManagerForTesting(manager_.get());
   }
 
-  TestingPrefServiceSimple* prefs() { return &pref_service_; }
-
   TestProfileIOS* profile() { return profile_.get(); }
 
   signin::IdentityManager* identity_manager() {
@@ -114,7 +109,6 @@
 
  private:
   web::WebTaskEnvironment task_environment_;
-  TestingPrefServiceSimple pref_service_;
   std::unique_ptr<TestProfileIOS> profile_;
   policy::FakeBrowserDMTokenStorage fake_browser_dm_token_storage_;
   std::unique_ptr<policy::MachineLevelUserCloudPolicyManager> manager_;
@@ -123,12 +117,8 @@
 }  // namespace
 
 TEST_F(ConnectorsServiceTest, GetPrefs) {
-  ConnectorsService connectors_service{/*off_the_record=*/false, prefs(),
-                                       /*user_cloud_policy_client=*/nullptr,
-                                       identity_manager()};
-  const ConnectorsService const_connectors_service{
-      /*off_the_record=*/false, prefs(),
-      /*user_cloud_policy_client=*/nullptr, identity_manager()};
+  ConnectorsService connectors_service{profile()};
+  const ConnectorsService const_connectors_service{profile()};
 
   PrefService* prefs = connectors_service.GetPrefs();
   const PrefService* const_prefs = const_connectors_service.GetPrefs();
@@ -139,12 +129,9 @@
 }
 
 TEST_F(ConnectorsServiceTest, GetProfileDmToken) {
-  prefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
-                      policy::POLICY_SCOPE_USER);
-  ConnectorsService connectors_service{
-      /*off_the_record=*/false, prefs(),
-      /*user_cloud_policy_client=*/profile()->GetUserCloudPolicyManager(),
-      identity_manager()};
+  profile()->GetPrefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
+                                    policy::POLICY_SCOPE_USER);
+  ConnectorsService connectors_service{profile()};
 
   auto profile_dm_token =
       connectors_service.GetDmToken(kEnterpriseRealTimeUrlCheckScope);
@@ -154,12 +141,9 @@
 }
 
 TEST_F(ConnectorsServiceTest, GetBrowserDmToken) {
-  prefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
-                      policy::POLICY_SCOPE_MACHINE);
-  ConnectorsService connectors_service{
-      /*off_the_record=*/false, prefs(),
-      /*user_cloud_policy_client=*/profile()->GetUserCloudPolicyManager(),
-      identity_manager()};
+  profile()->GetPrefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
+                                    policy::POLICY_SCOPE_MACHINE);
+  ConnectorsService connectors_service{profile()};
 
   auto browser_dm_token =
       connectors_service.GetDmToken(kEnterpriseRealTimeUrlCheckScope);
@@ -177,25 +161,13 @@
   ASSERT_FALSE(ConnectorsServiceFactory::GetForProfile(
                    profile()->GetOffTheRecordProfile())
                    ->ConnectorsEnabled());
-  ASSERT_TRUE(
-      ConnectorsService(
-          /*off_the_record=*/false, prefs(),
-          /*user_cloud_policy_client=*/profile()->GetUserCloudPolicyManager(),
-          identity_manager())
-          .ConnectorsEnabled());
-  ASSERT_FALSE(
-      ConnectorsService(
-          /*off_the_record=*/true, prefs(),
-          /*user_cloud_policy_client=*/profile()->GetUserCloudPolicyManager(),
-          /*identity_manager=*/nullptr)
-          .ConnectorsEnabled());
+  ASSERT_TRUE(ConnectorsService(profile()).ConnectorsEnabled());
+  ASSERT_FALSE(ConnectorsService(profile()->GetOffTheRecordProfile())
+                   .ConnectorsEnabled());
 }
 
 TEST_F(ConnectorsServiceTest, RealTimeUrlCheck) {
-  auto service = ConnectorsService(
-      /*off_the_record=*/false, prefs(),
-      /*user_cloud_policy_client=*/profile()->GetUserCloudPolicyManager(),
-      identity_manager());
+  auto service = ConnectorsService(profile());
 
   ASSERT_FALSE(service.GetDMTokenForRealTimeUrlCheck().has_value());
   ASSERT_EQ(service.GetDMTokenForRealTimeUrlCheck().error(),
@@ -204,19 +176,19 @@
   ASSERT_EQ(service.GetAppliedRealTimeUrlCheck(),
             EnterpriseRealTimeUrlCheckMode::REAL_TIME_CHECK_DISABLED);
 
-  prefs()->SetInteger(
+  profile()->GetPrefs()->SetInteger(
       kEnterpriseRealTimeUrlCheckMode,
       EnterpriseRealTimeUrlCheckMode::REAL_TIME_CHECK_FOR_MAINFRAME_ENABLED);
-  prefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
-                      policy::POLICY_SCOPE_MACHINE);
+  profile()->GetPrefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
+                                    policy::POLICY_SCOPE_MACHINE);
   ASSERT_TRUE(service.GetDMTokenForRealTimeUrlCheck().has_value());
   ASSERT_EQ(*service.GetDMTokenForRealTimeUrlCheck(), kTestBrowserDmToken);
   ASSERT_EQ(
       service.GetAppliedRealTimeUrlCheck(),
       EnterpriseRealTimeUrlCheckMode::REAL_TIME_CHECK_FOR_MAINFRAME_ENABLED);
 
-  prefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
-                      policy::POLICY_SCOPE_USER);
+  profile()->GetPrefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
+                                    policy::POLICY_SCOPE_USER);
   ASSERT_TRUE(service.GetDMTokenForRealTimeUrlCheck().has_value());
   ASSERT_EQ(*service.GetDMTokenForRealTimeUrlCheck(), kTestProfileDmToken);
   ASSERT_EQ(
@@ -225,10 +197,7 @@
 }
 
 TEST_F(ConnectorsServiceTest, RealTimeUrlCheck_OffTheRecord) {
-  auto service = ConnectorsService(
-      /*off_the_record=*/true, prefs(),
-      /*user_cloud_policy_client=*/profile()->GetUserCloudPolicyManager(),
-      /*identity_manager=*/nullptr);
+  auto service = ConnectorsService(profile()->GetOffTheRecordProfile());
 
   ASSERT_FALSE(service.GetDMTokenForRealTimeUrlCheck().has_value());
   ASSERT_EQ(service.GetDMTokenForRealTimeUrlCheck().error(),
@@ -237,11 +206,11 @@
   ASSERT_EQ(service.GetAppliedRealTimeUrlCheck(),
             EnterpriseRealTimeUrlCheckMode::REAL_TIME_CHECK_DISABLED);
 
-  prefs()->SetInteger(
+  profile()->GetPrefs()->SetInteger(
       kEnterpriseRealTimeUrlCheckMode,
       EnterpriseRealTimeUrlCheckMode::REAL_TIME_CHECK_FOR_MAINFRAME_ENABLED);
-  prefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
-                      policy::POLICY_SCOPE_MACHINE);
+  profile()->GetPrefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
+                                    policy::POLICY_SCOPE_MACHINE);
   ASSERT_FALSE(service.GetDMTokenForRealTimeUrlCheck().has_value());
   ASSERT_EQ(service.GetDMTokenForRealTimeUrlCheck().error(),
             ConnectorsServiceBase::NoDMTokenForRealTimeUrlCheckReason::
@@ -249,8 +218,8 @@
   ASSERT_EQ(service.GetAppliedRealTimeUrlCheck(),
             EnterpriseRealTimeUrlCheckMode::REAL_TIME_CHECK_DISABLED);
 
-  prefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
-                      policy::POLICY_SCOPE_USER);
+  profile()->GetPrefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
+                                    policy::POLICY_SCOPE_USER);
   ASSERT_FALSE(service.GetDMTokenForRealTimeUrlCheck().has_value());
   ASSERT_EQ(service.GetDMTokenForRealTimeUrlCheck().error(),
             ConnectorsServiceBase::NoDMTokenForRealTimeUrlCheckReason::
@@ -260,15 +229,12 @@
 }
 
 TEST_F(ConnectorsServiceTest, ReportingSettings) {
-  auto service = ConnectorsService(
-      /*off_the_record=*/false, prefs(),
-      /*user_cloud_policy_client=*/profile()->GetUserCloudPolicyManager(),
-      identity_manager());
+  auto service = ConnectorsService(profile());
 
   EXPECT_FALSE(service.GetReportingSettings());
   EXPECT_TRUE(service.GetReportingServiceProviderNames().empty());
 
-  test::SetOnSecurityEventReporting(prefs(), /*enabled=*/true);
+  test::SetOnSecurityEventReporting(profile()->GetPrefs(), /*enabled=*/true);
 
   auto settings = service.GetReportingSettings();
   EXPECT_TRUE(settings.has_value());
@@ -282,7 +248,7 @@
   EXPECT_EQ(provider_names, std::vector<std::string>({"google"}));
 
   test::SetOnSecurityEventReporting(
-      prefs(), /*enabled=*/true, /*enabled_event_names=*/{},
+      profile()->GetPrefs(), /*enabled=*/true, /*enabled_event_names=*/{},
       /*enabled_opt_in_events=*/{}, /*machine_scope=*/false);
 
   settings = service.GetReportingSettings();
@@ -298,21 +264,18 @@
 }
 
 TEST_F(ConnectorsServiceTest, ReportingSettings_OffTheRecord) {
-  auto service = ConnectorsService(
-      /*off_the_record=*/true, prefs(),
-      /*user_cloud_policy_client=*/profile()->GetUserCloudPolicyManager(),
-      /*identity_manager=*/nullptr);
+  auto service = ConnectorsService(profile()->GetOffTheRecordProfile());
 
   EXPECT_FALSE(service.GetReportingSettings());
   EXPECT_TRUE(service.GetReportingServiceProviderNames().empty());
 
-  test::SetOnSecurityEventReporting(prefs(), /*enabled=*/true);
+  test::SetOnSecurityEventReporting(profile()->GetPrefs(), /*enabled=*/true);
 
   EXPECT_FALSE(service.GetReportingSettings());
   EXPECT_TRUE(service.GetReportingServiceProviderNames().empty());
 
   test::SetOnSecurityEventReporting(
-      prefs(), /*enabled=*/true, /*enabled_event_names=*/{},
+      profile()->GetPrefs(), /*enabled=*/true, /*enabled_event_names=*/{},
       /*enabled_opt_in_events=*/{}, /*machine_scope=*/false);
 
   EXPECT_FALSE(service.GetReportingSettings());
@@ -320,52 +283,45 @@
 }
 
 TEST_F(ConnectorsServiceTest, GetManagementDomain_UrlFilteringEnabled) {
-  auto service = ConnectorsService(
-      /*off_the_record=*/false, prefs(),
-      /*user_cloud_policy_client=*/profile()->GetUserCloudPolicyManager(),
-      identity_manager());
+  auto service = ConnectorsService(profile());
 
   ASSERT_EQ(service.GetManagementDomain(), std::string());
 
   base::test::ScopedFeatureList feature(kIOSEnterpriseRealtimeUrlFiltering);
-  prefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
-                      policy::POLICY_SCOPE_USER);
+  profile()->GetPrefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
+                                    policy::POLICY_SCOPE_USER);
 
   MakePrimaryAccountAvailable(kTestProfileEmail);
 
   ASSERT_EQ(service.GetManagementDomain(), kTestProfileDomain);
 
-  prefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
-                      policy::POLICY_SCOPE_MACHINE);
+  profile()->GetPrefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
+                                    policy::POLICY_SCOPE_MACHINE);
 
   ASSERT_EQ(service.GetManagementDomain(), kTestMachineDomain);
 }
 
 TEST_F(ConnectorsServiceTest, GetManagementDomain_EventReportingEnabled) {
-  auto service = ConnectorsService(
-      /*off_the_record=*/false, prefs(),
-      /*user_cloud_policy_client=*/profile()->GetUserCloudPolicyManager(),
-      identity_manager());
+  auto service = ConnectorsService(profile());
 
   ASSERT_EQ(service.GetManagementDomain(), std::string());
 
   base::test::ScopedFeatureList feature(kEnterpriseRealtimeEventReportingOnIOS);
-  prefs()->SetInteger(kOnSecurityEventScopePref, policy::POLICY_SCOPE_USER);
+  profile()->GetPrefs()->SetInteger(kOnSecurityEventScopePref,
+                                    policy::POLICY_SCOPE_USER);
 
   MakePrimaryAccountAvailable(kTestProfileEmail);
 
   ASSERT_EQ(service.GetManagementDomain(), kTestProfileDomain);
 
-  prefs()->SetInteger(kOnSecurityEventScopePref, policy::POLICY_SCOPE_MACHINE);
+  profile()->GetPrefs()->SetInteger(kOnSecurityEventScopePref,
+                                    policy::POLICY_SCOPE_MACHINE);
 
   ASSERT_EQ(service.GetManagementDomain(), kTestMachineDomain);
 }
 
 TEST_F(ConnectorsServiceTest, GetManagementDomain_MachinePolicyHasPrecedence) {
-  auto service = ConnectorsService(
-      /*off_the_record=*/false, prefs(),
-      /*user_cloud_policy_client=*/profile()->GetUserCloudPolicyManager(),
-      identity_manager());
+  auto service = ConnectorsService(profile());
 
   ASSERT_EQ(service.GetManagementDomain(), std::string());
 
@@ -374,9 +330,10 @@
       /*enabled_features=*/{kEnterpriseRealtimeEventReportingOnIOS,
                             kIOSEnterpriseRealtimeUrlFiltering},
       /*disabled_features=*/{});
-  prefs()->SetInteger(kOnSecurityEventScopePref, policy::POLICY_SCOPE_USER);
-  prefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
-                      policy::POLICY_SCOPE_MACHINE);
+  profile()->GetPrefs()->SetInteger(kOnSecurityEventScopePref,
+                                    policy::POLICY_SCOPE_USER);
+  profile()->GetPrefs()->SetInteger(kEnterpriseRealTimeUrlCheckScope,
+                                    policy::POLICY_SCOPE_MACHINE);
 
   MakePrimaryAccountAvailable(kTestProfileEmail);
 
@@ -384,10 +341,7 @@
 }
 
 TEST_F(ConnectorsServiceTest, GetManagementDomain_OffTheRecord) {
-  auto service = ConnectorsService(
-      /*off_the_record=*/true, prefs(),
-      /*user_cloud_policy_client=*/profile()->GetUserCloudPolicyManager(),
-      /*identity_manager=*/nullptr);
+  auto service = ConnectorsService(profile()->GetOffTheRecordProfile());
 
   ASSERT_EQ(service.GetManagementDomain(), std::string());
 }
diff --git a/ios/chrome/browser/first_run/ui_bundled/guided_tour/BUILD.gn b/ios/chrome/browser/first_run/ui_bundled/guided_tour/BUILD.gn
index e71300e..e1c5b28 100644
--- a/ios/chrome/browser/first_run/ui_bundled/guided_tour/BUILD.gn
+++ b/ios/chrome/browser/first_run/ui_bundled/guided_tour/BUILD.gn
@@ -4,6 +4,8 @@
 
 source_set("guided_tour") {
   sources = [
+    "guided_tour_coordinator.h",
+    "guided_tour_coordinator.mm",
     "guided_tour_promo_coordinator.h",
     "guided_tour_promo_coordinator.mm",
     "guided_tour_promo_view_controller.h",
@@ -12,8 +14,14 @@
   deps = [
     "//base",
     "//ios/chrome/app/strings",
+    "//ios/chrome/browser/bubble/ui_bundled/guided_tour",
     "//ios/chrome/browser/shared/coordinator/chrome_coordinator",
+    "//ios/chrome/browser/shared/coordinator/layout_guide",
+    "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/ui/symbols",
+    "//ios/chrome/browser/shared/ui/util",
+    "//ios/chrome/browser/shared/ui/util:util_swift",
+    "//ios/chrome/browser/toolbar/ui_bundled/buttons",
     "//ios/chrome/common/ui/promo_style",
     "//ui/base",
   ]
diff --git a/ios/chrome/browser/first_run/ui_bundled/guided_tour/DEPS b/ios/chrome/browser/first_run/ui_bundled/guided_tour/DEPS
new file mode 100644
index 0000000..9ddff46
--- /dev/null
+++ b/ios/chrome/browser/first_run/ui_bundled/guided_tour/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presenter.h",
+  "+ios/chrome/browser/toolbar/ui_bundled/buttons/toolbar_button.h",
+]
diff --git a/ios/chrome/browser/first_run/ui_bundled/guided_tour/guided_tour_coordinator.h b/ios/chrome/browser/first_run/ui_bundled/guided_tour/guided_tour_coordinator.h
new file mode 100644
index 0000000..0c962e4
--- /dev/null
+++ b/ios/chrome/browser/first_run/ui_bundled/guided_tour/guided_tour_coordinator.h
@@ -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.
+
+#ifndef IOS_CHROME_BROWSER_FIRST_RUN_UI_BUNDLED_GUIDED_TOUR_GUIDED_TOUR_COORDINATOR_H_
+#define IOS_CHROME_BROWSER_FIRST_RUN_UI_BUNDLED_GUIDED_TOUR_GUIDED_TOUR_COORDINATOR_H_
+
+#import "base/ios/block_types.h"
+#import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h"
+#import "ios/chrome/browser/shared/public/commands/guided_tour_commands.h"
+
+// Delegate for GuidedTourCoordinator to handle user actions.
+@protocol GuidedTourCoordinatorDelegate
+
+// Indicates to the delegate that the user tapped on the next button for `step`.
+- (void)nextTappedForStep:(GuidedTourStep)step;
+
+// Indicates to the delegate that the `step` was dismissed.
+- (void)stepCompleted:(GuidedTourStep)step;
+
+@end
+
+// Coordinator to present a Guided Tour step.
+@interface GuidedTourCoordinator : ChromeCoordinator
+
+// Initializes a GuidedTourCoordinator with `baseViewController`,
+// `browser`, and `delegate`.
+- (instancetype)initWithStep:(GuidedTourStep)step
+          baseViewController:(UIViewController*)baseViewController
+                     browser:(Browser*)browser
+                    delegate:(id<GuidedTourCoordinatorDelegate>)delegate
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController
+                                   browser:(Browser*)browser NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_FIRST_RUN_UI_BUNDLED_GUIDED_TOUR_GUIDED_TOUR_COORDINATOR_H_
diff --git a/ios/chrome/browser/first_run/ui_bundled/guided_tour/guided_tour_coordinator.mm b/ios/chrome/browser/first_run/ui_bundled/guided_tour/guided_tour_coordinator.mm
new file mode 100644
index 0000000..373ed4e4
--- /dev/null
+++ b/ios/chrome/browser/first_run/ui_bundled/guided_tour/guided_tour_coordinator.mm
@@ -0,0 +1,138 @@
+// 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 "ios/chrome/browser/first_run/ui_bundled/guided_tour/guided_tour_coordinator.h"
+
+#import "base/notreached.h"
+#import "ios/chrome/browser/bubble/ui_bundled/guided_tour/guided_tour_bubble_view_controller_presenter.h"
+#import "ios/chrome/browser/shared/coordinator/layout_guide/layout_guide_util.h"
+#import "ios/chrome/browser/shared/ui/util/layout_guide_names.h"
+#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
+#import "ios/chrome/browser/shared/ui/util/util_swift.h"
+#import "ios/chrome/browser/toolbar/ui_bundled/buttons/toolbar_button.h"
+#import "ios/chrome/grit/ios_strings.h"
+#import "ui/base/l10n/l10n_util_mac.h"
+
+namespace {
+// Corner radius of the Tab Grid button spotlight cutout for the NTP step.
+const CGFloat kNTPTabGridButtonSpotlightCornerRadius = 7.0f;
+}  // namespace
+
+@implementation GuidedTourCoordinator {
+  GuidedTourStep _step;
+  __weak id<GuidedTourCoordinatorDelegate> _delegate;
+  GuidedTourBubbleViewControllerPresenter* _presenter;
+}
+
+- (instancetype)initWithStep:(GuidedTourStep)step
+          baseViewController:(UIViewController*)baseViewController
+                     browser:(Browser*)browser
+                    delegate:(id<GuidedTourCoordinatorDelegate>)delegate {
+  if ((self = [super initWithBaseViewController:baseViewController
+                                        browser:browser])) {
+    _step = step;
+    _delegate = delegate;
+  }
+  return self;
+}
+
+#pragma mark - ChromeCoordinator
+
+- (void)start {
+  __weak GuidedTourCoordinator* weakSelf = self;
+  BubbleArrowDirection direction = IsSplitToolbarMode(self.baseViewController)
+                                       ? BubbleArrowDirectionDown
+                                       : BubbleArrowDirectionUp;
+  _presenter = [[GuidedTourBubbleViewControllerPresenter alloc]
+      initWithText:[self bodyString]
+      title:[self titleString]
+      arrowDirection:direction
+      alignment:BubbleAlignmentBottomOrTrailing
+      bubbleType:BubbleViewTypeRich
+      backgroundCutoutCornerRadius:[self backgroundCutoutCornerRadius]
+      dismissalCallback:^(IPHDismissalReasonType reason) {
+        [weakSelf dismissFinished];
+      }
+      completionCallback:^{
+        [weakSelf nextTapped];
+      }];
+
+  UIView* anchorView = [self anchorView];
+  CGPoint anchorPoint = [self anchorPointForAnchorView:anchorView];
+  CGPoint anchorViewOrigin =
+      [anchorView.superview convertPoint:anchorView.frame.origin toView:nil];
+  CGRect anchorViewFrame =
+      CGRectMake(anchorViewOrigin.x, anchorViewOrigin.y,
+                 anchorView.frame.size.width, anchorView.frame.size.height);
+
+  [_presenter presentInViewController:self.baseViewController
+                          anchorPoint:anchorPoint
+                      anchorViewFrame:anchorViewFrame];
+}
+
+- (void)stop {
+  [_presenter dismiss];
+  _presenter = nil;
+}
+
+#pragma mark - Private
+
+// Returns the view to which the bubble view will be anchored.
+- (UIView*)anchorView {
+  if (_step == GuidedTourStepNTP) {
+    ToolbarButton* tabSwitcherButton =
+        static_cast<ToolbarButton*>([LayoutGuideCenterForBrowser(self.browser)
+            referencedViewUnderName:kTabSwitcherGuide]);
+    return tabSwitcherButton.spotlightView;
+  }
+  NOTREACHED() << "A layout guide view needs to be fetched for each step";
+}
+
+// Returns the anchor point in `anchorView` to which the bubble view will be
+// anchored.
+- (CGPoint)anchorPointForAnchorView:(UIView*)anchorView {
+  CGPoint anchorPoint;
+  if (IsSplitToolbarMode(self.baseViewController)) {
+    anchorPoint = CGPointMake(CGRectGetMidX(anchorView.frame),
+                              CGRectGetMinY(anchorView.frame));
+  } else {
+    anchorPoint = CGPointMake(CGRectGetMidX(anchorView.frame),
+                              CGRectGetMaxY(anchorView.frame));
+  }
+  return [anchorView.superview convertPoint:anchorPoint toView:nil];
+}
+
+// Handle the user tapping on the next button.
+- (void)nextTapped {
+  [_delegate nextTappedForStep:_step];
+}
+
+// Handle the dismissal completion of this step.
+- (void)dismissFinished {
+  [_delegate stepCompleted:_step];
+}
+
+// Returns the title string used for this step's Bubble View.
+- (NSString*)titleString {
+  if (_step == GuidedTourStepNTP) {
+    return l10n_util::GetNSString(IDS_IOS_FIRST_RUN_GUIDED_TOUR_NTP_IPH_TITLE);
+  }
+  return @"";
+}
+
+// Returns the main text string for this step's Bubble View.
+- (NSString*)bodyString {
+  if (_step == GuidedTourStepNTP) {
+    return l10n_util::GetNSString(IDS_IOS_FIRST_RUN_GUIDED_TOUR_NTP_IPH_TEXT);
+  }
+  return @"";
+}
+
+// The corner radius of the spotlight cutout for this Bubble View.
+- (CGFloat)backgroundCutoutCornerRadius {
+  return _step == GuidedTourStepNTP ? kNTPTabGridButtonSpotlightCornerRadius
+                                    : 0;
+}
+
+@end
diff --git a/ios/chrome/browser/home_customization/coordinator/BUILD.gn b/ios/chrome/browser/home_customization/coordinator/BUILD.gn
index eacf8f9a..6c69e630 100644
--- a/ios/chrome/browser/home_customization/coordinator/BUILD.gn
+++ b/ios/chrome/browser/home_customization/coordinator/BUILD.gn
@@ -42,15 +42,21 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "home_customization_coordinator_unittest.mm" ]
+  sources = [
+    "home_customization_coordinator_unittest.mm",
+    "home_customization_mediator_unittest.mm",
+  ]
   frameworks = [ "Foundation.framework" ]
   deps = [
     ":coordinator",
     "//base",
     "//base/test:test_support",
+    "//components/prefs",
     "//ios/chrome/browser/home_customization/ui",
     "//ios/chrome/browser/home_customization/utils",
     "//ios/chrome/browser/shared/model/browser/test:test_support",
+    "//ios/chrome/browser/shared/model/prefs:browser_prefs",
+    "//ios/chrome/browser/shared/model/prefs:pref_names",
     "//ios/chrome/browser/shared/model/profile/test",
     "//ios/chrome/browser/shared/public/features",
     "//ios/chrome/test:test_support",
diff --git a/ios/chrome/browser/home_customization/coordinator/home_customization_mediator_unittest.mm b/ios/chrome/browser/home_customization/coordinator/home_customization_mediator_unittest.mm
new file mode 100644
index 0000000..965d72e
--- /dev/null
+++ b/ios/chrome/browser/home_customization/coordinator/home_customization_mediator_unittest.mm
@@ -0,0 +1,89 @@
+// 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 "ios/chrome/browser/home_customization/coordinator/home_customization_mediator.h"
+
+#import "base/memory/raw_ptr.h"
+#import "base/strings/sys_string_conversions.h"
+#import "base/test/scoped_feature_list.h"
+#import "components/prefs/pref_service.h"
+#import "ios/chrome/browser/home_customization/ui/home_customization_main_consumer.h"
+#import "ios/chrome/browser/home_customization/utils/home_customization_constants.h"
+#import "ios/chrome/browser/shared/model/prefs/browser_prefs.h"
+#import "ios/chrome/browser/shared/model/prefs/pref_names.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/test/ios_chrome_scoped_testing_local_state.h"
+#import "ios/chrome/test/ios_chrome_scoped_testing_variations_service.h"
+#import "ios/web/public/test/web_task_environment.h"
+#import "testing/gtest/include/gtest/gtest.h"
+#import "testing/gtest_mac.h"
+#import "testing/platform_test.h"
+#import "ui/base/l10n/l10n_util_mac.h"
+
+// Fake consumer for the main page of the Customization menu.
+@interface FakeHomeCustomizationMainConsumer
+    : NSObject <HomeCustomizationMainConsumer>
+
+// The toggles, populated by `populateToggles:` to faciliate testing.
+@property(nonatomic, assign) std::map<CustomizationToggleType, BOOL> toggleMap;
+
+@end
+
+@implementation FakeHomeCustomizationMainConsumer
+
+#pragma mark - HomeCustomizationMainConsumer
+
+- (void)populateToggles:(std::map<CustomizationToggleType, BOOL>)toggleMap {
+  _toggleMap = toggleMap;
+}
+
+- (void)
+    populateBackgroundCustomizationConfigurations:
+        (NSMutableDictionary<NSString*, BackgroundCustomizationConfiguration*>*)
+            BackgroundCustomizationConfigurationMap
+                             selectedBackgroundId:
+                                 (NSString*)selectedBackgroundId {
+  // No-op for fake implementation.
+}
+
+@end
+
+// Tests for the Home Customization mediator.
+class HomeCustomizationMediatorUnitTest : public PlatformTest {
+ public:
+  void SetUp() override {
+    profile_ = TestProfileIOS::Builder().Build();
+    pref_service_ = profile_->GetPrefs();
+    mediator_ =
+        [[HomeCustomizationMediator alloc] initWithPrefService:pref_service_];
+  }
+
+ protected:
+  web::WebTaskEnvironment task_environment_;
+  HomeCustomizationMediator* mediator_;
+  raw_ptr<PrefService> pref_service_;
+  std::unique_ptr<TestProfileIOS> profile_;
+};
+
+// Tests that the mediator populates the main page data for its consumer based
+// on the profile's prefs.
+TEST_F(HomeCustomizationMediatorUnitTest, TestMainPageData) {
+  FakeHomeCustomizationMainConsumer* fake_consumer =
+      [[FakeHomeCustomizationMainConsumer alloc] init];
+  mediator_.mainPageConsumer = fake_consumer;
+
+  // Set the prefs.
+  pref_service_->SetBoolean(prefs::kHomeCustomizationMostVisitedEnabled, NO);
+  pref_service_->SetBoolean(prefs::kHomeCustomizationMagicStackEnabled, YES);
+  pref_service_->SetBoolean(prefs::kArticlesForYouEnabled, NO);
+
+  [mediator_ configureMainPageData];
+
+  // Check that the toggles properly reflect the prefs.
+  std::map<CustomizationToggleType, BOOL> toggle_map = fake_consumer.toggleMap;
+  EXPECT_EQ(toggle_map.at(CustomizationToggleType::kMostVisited), NO);
+  EXPECT_EQ(toggle_map.at(CustomizationToggleType::kMagicStack), YES);
+  EXPECT_EQ(toggle_map.at(CustomizationToggleType::kDiscover), NO);
+}
diff --git a/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/BUILD.gn b/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/BUILD.gn
index 52cf1cd..8a60942 100644
--- a/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/BUILD.gn
+++ b/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/BUILD.gn
@@ -22,6 +22,7 @@
     "//components/password_manager/core/browser",
     "//components/password_manager/core/browser/features:password_features",
     "//components/password_manager/ios",
+    "//components/password_manager/ios:features",
     "//components/prefs",
     "//components/segmentation_platform/embedder/home_modules/tips_manager:signal_constants",
     "//ios/chrome/app/strings",
@@ -105,6 +106,7 @@
     ":eg_test_support+eg2",
     "//components/password_manager/core/browser/features:password_features",
     "//components/password_manager/core/common:features",
+    "//components/password_manager/ios:features",
     "//components/url_formatter",
     "//ios/chrome/app/strings",
     "//ios/chrome/browser/authentication/ui_bundled:eg_test_support+eg2",
diff --git a/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/password_suggestion_bottom_sheet_egtest.mm b/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/password_suggestion_bottom_sheet_egtest.mm
index 4848b39e..bf4ac16e 100644
--- a/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/password_suggestion_bottom_sheet_egtest.mm
+++ b/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/password_suggestion_bottom_sheet_egtest.mm
@@ -10,6 +10,7 @@
 #import "base/time/time.h"
 #import "components/password_manager/core/browser/features/password_features.h"
 #import "components/password_manager/core/common/password_manager_features.h"
+#import "components/password_manager/ios/features.h"
 #import "components/url_formatter/elide_url.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey.h"
 #import "ios/chrome/browser/authentication/ui_bundled/signin_earl_grey_ui_test_util.h"
@@ -162,7 +163,10 @@
   }
 
   if ([self isRunningTest:@selector
-            (testOpenPasswordBottomSheetTapUseKeyboardShowKeyboard_V2)]) {
+            (testOpenPasswordBottomSheetTapUseKeyboardShowKeyboard_V2)] ||
+      [self
+          isRunningTest:@selector
+          (testOpenPasswordBottomSheetUsePassword_V2_StatelessFillDataFlow)]) {
     config.features_enabled.push_back(
         password_manager::features::kIOSPasswordBottomSheetV2);
   } else {
@@ -170,6 +174,13 @@
         password_manager::features::kIOSPasswordBottomSheetV2);
   }
 
+  if ([self
+          isRunningTest:@selector
+          (testOpenPasswordBottomSheetUsePassword_V2_StatelessFillDataFlow)]) {
+    config.features_enabled.push_back(
+        password_manager::features::kIOSStatelessFillDataFlow);
+  }
+
   return config;
 }
 
@@ -273,6 +284,54 @@
   [self verifyPasswordFieldsHaveBeenFilled:@"user"];
 }
 
+// Tests that accepting suggestions from the sheet V2 works when the stateless
+// fill data flow feature is enabled. This tests the combination of the 2
+// features.
+- (void)testOpenPasswordBottomSheetUsePassword_V2_StatelessFillDataFlow {
+  [PasswordSuggestionBottomSheetAppInterface setUpMockReauthenticationModule];
+  [PasswordSuggestionBottomSheetAppInterface
+      mockReauthenticationModuleExpectedResult:ReauthenticationResult::
+                                                   kSuccess];
+
+  GURL URL = self.testServer->GetURL("/simple_login_form_empty.html");
+  [PasswordManagerAppInterface
+      storeCredentialWithUsername:@"user"
+                         password:@"password"
+                              URL:net::NSURLWithGURL(URL)];
+  [SigninEarlGrey signinWithFakeIdentity:[FakeSystemIdentity fakeIdentity1]];
+  [self loadLoginPage];
+
+  // Wait a bit to let things settle. Waiting on content to be loaded on the
+  // page isn't 100% reliable as trying to interact with that content at that
+  // moment doesn't always work.
+  base::test::ios::SpinRunLoopWithMinDelay(base::Seconds(1));
+
+  [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()]
+      performAction:chrome_test_util::TapWebElementWithId(kFormPassword)];
+
+  [ChromeEarlGrey
+      waitForUIElementToAppearWithMatcher:grey_accessibilityID(@"user")];
+
+  // Verify that the subtitle string appears.
+  [ChromeEarlGrey waitForUIElementToAppearWithMatcher:SubtitleString(URL)];
+
+  [[EarlGrey selectElementWithMatcher:UsePasswordButton()]
+      performAction:grey_tap()];
+
+  // No histogram logged because there is only 1 credential shown to the user.
+  GREYAssertNil(
+      [MetricsAppInterface
+          expectTotalCount:0
+              forHistogram:@"PasswordManager.TouchToFill.CredentialIndex"],
+      @"Unexpected histogram error for touch to fill credential index");
+
+  // Verify that the acceptance of the password suggestion at index 0 was
+  // correctly recorded.
+  CheckAutofillSuggestionAcceptedIndexMetricsCount(/*suggestion_index=*/0);
+
+  [self verifyPasswordFieldsHaveBeenFilled:@"user"];
+}
+
 // This test verifies that the bottom sheet opens on autofocus events, when the
 // kIOSPasswordBottomSheetAutofocus feature is enabled.
 - (void)testOpenPasswordBottomOnAutofocus {
diff --git a/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/password_suggestion_bottom_sheet_mediator.mm b/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/password_suggestion_bottom_sheet_mediator.mm
index a1a072d7..ebeffa3 100644
--- a/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/password_suggestion_bottom_sheet_mediator.mm
+++ b/ios/chrome/browser/passwords/ui_bundled/bottom_sheet/password_suggestion_bottom_sheet_mediator.mm
@@ -20,6 +20,7 @@
 #import "components/password_manager/core/browser/password_store/password_store_interface.h"
 #import "components/password_manager/core/browser/password_ui_utils.h"
 #import "components/password_manager/core/browser/ui/credential_ui_entry.h"
+#import "components/password_manager/ios/features.h"
 #import "components/password_manager/ios/ios_password_manager_driver_factory.h"
 #import "components/password_manager/ios/shared_password_controller.h"
 #import "components/prefs/pref_service.h"
@@ -83,6 +84,21 @@
           onlyPassword:YES];
 }
 
+// Makes a copy of suggestions with `params` and `provider` set in the copies.
+NSArray<FormSuggestion*>* SetParamsAndProviderInSuggestions(
+    NSArray<FormSuggestion*>* suggestions,
+    const autofill::FormActivityParams& params,
+    id<FormSuggestionProvider> provider) {
+  NSMutableArray<FormSuggestion*>* suggestions_copy =
+      [NSMutableArray<FormSuggestion*> arrayWithCapacity:[suggestions count]];
+  for (FormSuggestion* suggestion in suggestions) {
+    [suggestions_copy addObject:[FormSuggestion copy:suggestion
+                                        andSetParams:params
+                                            provider:provider]];
+  }
+  return suggestions_copy;
+}
+
 }  // namespace
 
 // TODO(crbug.com/372426818): Move this is to its own specific file/module.
@@ -205,7 +221,13 @@
                         webState:webState
                completionHandler:^(NSArray<FormSuggestion*>* suggestions,
                                    id<FormSuggestionProvider> delegate) {
-                 completion(suggestions);
+                 bool stateless = base::FeatureList::IsEnabled(
+                     password_manager::features::kIOSStatelessFillDataFlow);
+                 NSArray<FormSuggestion*>* wrappedSuggestions =
+                     stateless ? SetParamsAndProviderInSuggestions(
+                                     suggestions, params, delegate)
+                               : suggestions;
+                 completion(wrappedSuggestions);
                }];
 }
 
diff --git a/ios/chrome/browser/shared/public/features/features.mm b/ios/chrome/browser/shared/public/features/features.mm
index e5239cd..b5b8e27d 100644
--- a/ios/chrome/browser/shared/public/features/features.mm
+++ b/ios/chrome/browser/shared/public/features/features.mm
@@ -1050,9 +1050,7 @@
   return base::FeatureList::IsEnabled(kFRESignInSecondaryActionLabelUpdate);
 }
 
-BASE_FEATURE(kIOSPasskeysM2,
-             "IOSPasskeysM2",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+BASE_FEATURE(kIOSPasskeysM2, "IOSPasskeysM2", base::FEATURE_ENABLED_BY_DEFAULT);
 
 bool IOSPasskeysM2Enabled() {
   return base::FeatureList::IsEnabled(kIOSPasskeysM2);
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 e2c432fc..6b612ac0 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 @@
-95e6978b4d65d1d36670da01f67a10f1f48f67e0
\ No newline at end of file
+e3edaf782e8f7149c4c2492e3e7a55533b5638ad
\ 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 23389b1e..d3b92eb3 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 @@
-982ed659e3dd4c25f32f0d86ba9fbfe102c3dea7
\ No newline at end of file
+5a24acfa661c59bd8eb53e20d89ba10a23de757e
\ 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 2a68a49..0cead64 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 @@
-f2a63289d3a9e3b4866ed0902ba1b0bfe33c35ba
\ No newline at end of file
+4e7143699ba681818cab8c98ec0d39998d43cc87
\ 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 1fa9c6e..a626e5df 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 @@
-e8956ed1fc5f152f204b51cb58ebbfbc3c55c78f
\ No newline at end of file
+e13e0ffcbdd1b91cfb251441cf1e3eff6f255322
\ 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 03e294e4..e176586 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 @@
-aa56f949981c08348e424c79f73d55639ddadce3
\ No newline at end of file
+54f71374da89594dfdbc25e1f90e897ed3fbbeaa
\ 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 6c034f2d..7f66ac3 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 @@
-4cf4f502f20d97ba754441ef01af032c7a7b19c5
\ No newline at end of file
+a2caf961234900394a8039c7b2c1e74ed7562b1b
\ 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 67ddf1e..803649ae 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 @@
-4436d4d9bdf8d814ae9cf7c8fbc8d0a1d1428cb8
\ No newline at end of file
+4036b5db59d8c153da0c24ad91a780e6061dcaf0
\ 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 d543414..0660c05 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 @@
-4a8bdedb83b8e2f33830a5c8f5b07d5b03e02cf2
\ No newline at end of file
+76bc5701495778b9db3f446869591ec9f45734e6
\ 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 79fded4c..4ab2859 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 @@
-66c5368c4b087ae4496bd5c2d54f1486cf91d7cb
\ No newline at end of file
+cfe872b9fc93298b235d7d423d68ca900e38ead5
\ 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 084377f..047e6e9c 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 @@
-a7df80cf9e95f53cc7412f0cd2b30545ac0bfe15
\ No newline at end of file
+9f334152f35499b5f00f36a8906b28dda901da44
\ 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 858166b1..ad9288ae 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 @@
-2765ec0306e371794906484249320b88e45f2606
\ No newline at end of file
+b6a85dbe1fd2d7d17ee714acd1e9303c682b1c02
\ 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 ea44ffe..4ffa20c 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 @@
-270e580cbf56f9d7c8eef30a39c5c344641060dd
\ No newline at end of file
+f625ed4638257c969b975d98f137717dc71a76af
\ 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 4e0bc07..9152f79f 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 @@
-e16730370c818c8cc6fed42b8c0b76cb99d92c72
\ No newline at end of file
+94db3fa3a60e48af158c566cba149af100116b88
\ 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 755f0f0..eda7b36 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 @@
-e3ef9ff77206196672ba1aaca43052f7a5732043
\ No newline at end of file
+0e3aff95dd9f2f827ab1ae47dbd4dcd51cf3086c
\ 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 58a94585..91dc2a6 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 @@
-3e06f7e8f6da9a67816cda40a7108a02d0e8cca2
\ No newline at end of file
+e0332d07cfc93647eb21f7af0553d8a4449d72ab
\ 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 c470ff9..5c4ec80c 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 @@
-9cd5b03798009c9fb57c8e7ea4b176a0523fbef4
\ No newline at end of file
+cd52aeb6ddf2ea57d809fc7a2ebdd46095b4b7b8
\ 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 0847b31a..9c7f715 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 @@
-a18858a586f02ea18466f98d2c4a44f608d114c1
\ No newline at end of file
+52deadc27942090c40f04674ec314aa856569a02
\ 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 be7ffdc..3ef9efe 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 @@
-ed042fbd62fdfe4c8eabbd9fa74818ad0da3f909
\ No newline at end of file
+362f24d16d131448bf0f2ec87ee5e9d3d0df694c
\ 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 c43b7c6..d714b0b 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 @@
-b6ad5a943a47834d75f2987e641b6decbf04f34e
\ No newline at end of file
+8e61156591295d7e7216a3bb15d6d3ef31b1858b
\ 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 e7b3b25..6fb827c 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 @@
-be77c08b55b4dc65f1b80b2848cafcdf1d4e2755
\ No newline at end of file
+5f397699b0b9d48a8dccfafa637991051a737b7b
\ 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 feb1647d..3c10a54 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 @@
-0f000f849843c189d82a71d3d490629b127b184c
\ No newline at end of file
+30cfdb6241dfe222a5796a2608824ccd9c238f54
\ 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 3ddfbfb..8c916e3 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 @@
-81a9ae657c8b476a6c4a58f38c7aa446faf85d74
\ No newline at end of file
+8dbc98f17be818d6f35848a9f3d63f7039f2f20f
\ 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 b7b7719..9adb7d4 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 @@
-c8751ab6cebf149cf901a07dd461eeb5685bccb8
\ No newline at end of file
+9ff82179f542204d5776257bceeaa0d16d42ad3f
\ 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 5b520ac..5977791 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 @@
-89c8e1967608d6960879fefd092eec22925d422b
\ No newline at end of file
+76dc804eaaedb9bd7d6051cf98c2cf9aef1b826b
\ 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 4f27820..d0208d6 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 @@
-766d5cbbe8b80bd71b90d3a8fac1e07a25d1eab0
\ No newline at end of file
+0accc764651e6aa91a7f91e10b5176cacbc7bba8
\ 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 5542d7e..2265367 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 @@
-602c8a893511f9d23a754a71ad71b915a3d5d578
\ No newline at end of file
+fa2a6fc90c741236ee1c3e1d239d3a4a4fb6aa8c
\ 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 fd9761a3..751f790d 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 @@
-2976328b9622721acd4337b186c38cea392ddbc3
\ No newline at end of file
+e7b6e5963970afbf8266e6490ce6e9df4d4e718b
\ 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 76e254ac..5809b26 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 @@
-4fda076ca60e25c1d474e7d8f18f6dfd4f92d15e
\ No newline at end of file
+8c2470ab343dd081ee28eb7196da7b85a6b2996e
\ No newline at end of file
diff --git a/ios/testing/data/http_server_files/autofill_refill_test.html b/ios/testing/data/http_server_files/autofill_refill_test.html
new file mode 100644
index 0000000..3a4fc16
--- /dev/null
+++ b/ios/testing/data/http_server_files/autofill_refill_test.html
@@ -0,0 +1,61 @@
+
+<!DOCTYPE html>
+<!--
+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.
+-->
+<html>
+<head>
+    <meta name="viewport" content="width=device-width, initial-scale=1.5">
+    <title>Autofill Refill Test</title>
+    <style>
+        #address-fields { display: none; }
+    </style>
+    <script>
+        var FillFormPresident = function() {
+            var form = document.forms['profile_form'];
+            form['name'].value = 'George Washington';
+            form['address'].value = '1600 Pennsylvania Ave NW';
+            form['city'].value = 'Washington';
+            form['state'].value = 'DC';
+            form['zip'].value = '20500';
+        }
+    </script>
+</head>
+<body>
+    <h3>Refill Profile Autofill</h3>
+    <form name="profile_form" id="profile_form" action="destination.html" method="post">
+        Name: <input type="text" name="name" id="form_name" /><br />
+        <div id="address-fields">
+            Address: <input type="text" name="address" id="form_address" /><br />
+            City: <input type="text" name="city" id="form_city" /><br />
+            State: <select name="state" id="form_state">
+                <option value="CA">CA</option>
+                <option value="MA">MA</option>
+                <option value="NY">NY</option>
+                <option value="MD">MD</option>
+                <option value="OR">OR</option>
+                <option value="OH">OH</option>
+                <option value="IL">IL</option>
+                <option value="DC">DC</option>
+            </select> <br />
+	    Zip: <input name="zip" id="form_zip" /><br />
+        </div>
+        <input type="reset" value="Reset" />
+        <input id="submit_profile" type="submit" value="Submit Profile" />
+    </form>
+    <button type="button" onclick="FillFormPresident()" id="fill_profile_president">Fill Form (President)</button>
+    <br />
+    <script>
+        document.addEventListener('DOMContentLoaded', function() {
+            const zipInput = document.getElementById('form_name');
+            const addressFields = document.getElementById('address-fields');
+            zipInput.addEventListener('input', function() {
+                addressFields.style.display = this.value.trim() !== '' ? 'block' : 'none';
+            });
+        });
+    </script>
+</body>
+</html>
+
diff --git a/ios/testing/http_server_bundle_data.filelist b/ios/testing/http_server_bundle_data.filelist
index f4212d24..7fcee69 100644
--- a/ios/testing/http_server_bundle_data.filelist
+++ b/ios/testing/http_server_bundle_data.filelist
@@ -5,6 +5,7 @@
 #       If it requires updating, you should get a presubmit error with
 #       instructions on how to regenerate. Otherwise, do not edit.
 data/http_server_files/OWNERS
+data/http_server_files/autofill_refill_test.html
 data/http_server_files/autofill_smoke_test.html
 data/http_server_files/browsing_prevent_default_test_page.html
 data/http_server_files/bundle.pkpasses
@@ -19,8 +20,8 @@
 data/http_server_files/destination.html
 data/http_server_files/download_test_page.html
 data/http_server_files/email_signup_form.html
-data/http_server_files/fullscreen.html
 data/http_server_files/full_address_form.html
+data/http_server_files/fullscreen.html
 data/http_server_files/generic.pkpass
 data/http_server_files/green.pdf
 data/http_server_files/history.html
@@ -47,8 +48,8 @@
 data/http_server_files/redchair.usdz
 data/http_server_files/redirect_refresh.html
 data/http_server_files/sample.ics
-data/http_server_files/sample.order
 data/http_server_files/sample.mobileconfig
+data/http_server_files/sample.order
 data/http_server_files/semi_bundle.pkpasses
 data/http_server_files/simple_login_form.html
 data/http_server_files/simple_login_form_empty.html
diff --git a/ios/third_party/material_components_ios/src b/ios/third_party/material_components_ios/src
index f25aad8..b29adef 160000
--- a/ios/third_party/material_components_ios/src
+++ b/ios/third_party/material_components_ios/src
@@ -1 +1 @@
-Subproject commit f25aad87ce54240ed58abe7ae84b9316e80a8492
+Subproject commit b29adefbd8be6d39542d679b8f9189fc466283b7
diff --git a/ios_internal b/ios_internal
index 6753a19..8fbb4c8 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 6753a19d2e211ea27988a2603073395d0469a202
+Subproject commit 8fbb4c8734fa75050d85b613666e0d87d27e061e
diff --git a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
index 424788d..ad421bb 100644
--- a/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaDrmBridge.java
@@ -1435,9 +1435,14 @@
             // SessionException may be thrown when an operation failed in a way that is likely to
             // succeed on a subsequent attempt. However, checking for transient errors is only
             // available on S and later. Try only once to repeat it if possible.
-            if (retryAllowed && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && e.isTransient()) {
-                return startProvisioningQorLater(false);
+            // On versions Q and R, since transient error detection is not available, retry no
+            // matter what.
+            if (retryAllowed) {
+                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || e.isTransient()) {
+                    return startProvisioningQorLater(false);
+                }
             }
+
             Log.e(TAG, "Failed to get provisioning request", e);
             displayMetrics();
             return false;
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc
index 2fe23aa..358bd89 100644
--- a/media/base/media_switches.cc
+++ b/media/base/media_switches.cc
@@ -944,6 +944,8 @@
              base::FEATURE_DISABLED_BY_DEFAULT);
 
 // Enables hardware secure decryption if supported by hardware and CDM.
+// NOTE: This feature is experimental and not officially supported. Users may
+// encounter issues; enabling is discouraged.
 // TODO(xhwang): Currently this is only used for development of new features.
 // Apply this to Android and ChromeOS as well where hardware secure decryption
 // is already available.
diff --git a/media/base/win/mf_mocks.cc b/media/base/win/mf_mocks.cc
index b03cb59..150d30d 100644
--- a/media/base/win/mf_mocks.cc
+++ b/media/base/win/mf_mocks.cc
@@ -18,4 +18,16 @@
 MockMFCdmSession::MockMFCdmSession() = default;
 MockMFCdmSession::~MockMFCdmSession() = default;
 
+MockMFExtendedDRMTypeSupport::MockMFExtendedDRMTypeSupport() = default;
+MockMFExtendedDRMTypeSupport::~MockMFExtendedDRMTypeSupport() = default;
+
+MockMFGetService::MockMFGetService() = default;
+MockMFGetService::~MockMFGetService() = default;
+
+MockMFPMPHost::MockMFPMPHost() = default;
+MockMFPMPHost::~MockMFPMPHost() = default;
+
+MockMFPMPHostApp::MockMFPMPHostApp() = default;
+MockMFPMPHostApp::~MockMFPMPHostApp() = default;
+
 }  // namespace media
diff --git a/media/base/win/mf_mocks.h b/media/base/win/mf_mocks.h
index 8f325e3..75e936c5 100644
--- a/media/base/win/mf_mocks.h
+++ b/media/base/win/mf_mocks.h
@@ -6,6 +6,7 @@
 #define MEDIA_BASE_WIN_MF_MOCKS_H_
 
 #include <mfcontentdecryptionmodule.h>
+#include <mfmediaengine.h>
 #include <wrl/client.h>
 #include <wrl/implements.h>
 
@@ -56,6 +57,9 @@
   MockMFCdm();
   ~MockMFCdm() override;
 
+  // IUnknown method
+  MOCK_STDCALL_METHOD2(QueryInterface, HRESULT(REFIID riid, void** ppv));
+
   // IMFContentDecryptionModule methods
   MOCK_STDCALL_METHOD2(SetContentEnabler,
                        HRESULT(IMFContentEnabler* content_enabler,
@@ -103,6 +107,68 @@
   MOCK_STDCALL_METHOD0(Remove, HRESULT());
 };
 
+class MockMFExtendedDRMTypeSupport
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          IMFExtendedDRMTypeSupport> {
+ public:
+  MockMFExtendedDRMTypeSupport();
+  ~MockMFExtendedDRMTypeSupport() override;
+
+  // IMFExtendedDRMTypeSupport methods
+  MOCK_STDCALL_METHOD3(IsTypeSupportedEx,
+                       HRESULT(BSTR type,
+                               BSTR keySystem,
+                               MF_MEDIA_ENGINE_CANPLAY* pAnswer));
+};
+
+class MockMFGetService
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          IMFGetService> {
+ public:
+  MockMFGetService();
+  ~MockMFGetService() override;
+
+  // IMFGetService methods
+  MOCK_STDCALL_METHOD3(GetService,
+                       HRESULT(REFGUID guidService,
+                               REFIID riid,
+                               LPVOID* ppvObject));
+};
+
+class MockMFPMPHost
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          IMFPMPHost> {
+ public:
+  MockMFPMPHost();
+  ~MockMFPMPHost() override;
+
+  // IMFPMPHost methods
+  MOCK_STDCALL_METHOD0(LockProcess, HRESULT());
+  MOCK_STDCALL_METHOD0(UnlockProcess, HRESULT());
+  MOCK_STDCALL_METHOD4(
+      CreateObjectByCLSID,
+      HRESULT(REFCLSID clsid, IStream* pStream, REFIID riid, void** ppv));
+};
+
+class MockMFPMPHostApp
+    : public Microsoft::WRL::RuntimeClass<
+          Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+          IMFPMPHostApp> {
+ public:
+  MockMFPMPHostApp();
+  ~MockMFPMPHostApp() override;
+
+  // IMFPMPHostApp methods
+  MOCK_STDCALL_METHOD0(LockProcess, HRESULT());
+  MOCK_STDCALL_METHOD0(UnlockProcess, HRESULT());
+  MOCK_STDCALL_METHOD4(
+      ActivateClassById,
+      HRESULT(LPCWSTR id, IStream* pStream, REFIID riid, void** ppv));
+};
+
 }  // namespace media
 
 #endif  // MEDIA_BASE_WIN_MF_MOCKS_H_
diff --git a/media/cdm/BUILD.gn b/media/cdm/BUILD.gn
index 29cb46b..f56907b 100644
--- a/media/cdm/BUILD.gn
+++ b/media/cdm/BUILD.gn
@@ -257,6 +257,7 @@
       "win/media_foundation_cdm_factory_unittest.cc",
       "win/media_foundation_cdm_session_unittest.cc",
       "win/media_foundation_cdm_unittest.cc",
+      "win/media_foundation_cdm_util_unittest.cc",
     ]
   }
 }
diff --git a/media/cdm/win/media_foundation_cdm_factory.cc b/media/cdm/win/media_foundation_cdm_factory.cc
index eead4cd..9bdd8a2 100644
--- a/media/cdm/win/media_foundation_cdm_factory.cc
+++ b/media/cdm/win/media_foundation_cdm_factory.cc
@@ -38,6 +38,23 @@
                                       base::UTF8ToWide(content_type).c_str());
 }
 
+bool IsTypeSupportedInternalEx(const std::string& key_system,
+                               const std::string& content_type) {
+  ComPtr<IMFExtendedDRMTypeSupport> mf_type_support;
+  HRESULT hr =
+      CoCreateInstance(CLSID_MFMediaEngineClassFactory, nullptr,
+                       CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&mf_type_support));
+  if (FAILED(hr)) {
+    DLOG(ERROR) << __func__
+                << ": Failed to create class factory for IsTypeSupportedEx. hr="
+                << hr;
+    return false;
+  }
+
+  return IsMediaFoundationContentTypeSupported(mf_type_support, key_system,
+                                               content_type);
+}
+
 crash_reporter::CrashKeyString<256> g_origin_crash_key("cdm-origin");
 
 }  // namespace
@@ -169,6 +186,16 @@
     const std::string& key_system,
     const std::string& content_type,
     IsTypeSupportedResultCB is_type_supported_result_cb) {
+  // Note that IsTypeSupported may take up to 10s, so run it on a separate
+  // thread to unblock the main thread.
+  if (MediaFoundationCdmModule::GetInstance()->IsOsCdm()) {
+    base::ThreadPool::PostTaskAndReplyWithResult(
+        FROM_HERE,
+        base::BindOnce(&IsTypeSupportedInternalEx, key_system, content_type),
+        std::move(is_type_supported_result_cb));
+    return;
+  }
+
   ComPtr<IMFContentDecryptionModuleFactory> cdm_factory;
   HRESULT hr = GetCdmFactory(key_system, cdm_factory);
   if (FAILED(hr)) {
@@ -177,8 +204,6 @@
     return;
   }
 
-  // Note that IsTypeSupported may take up to 10s, so run it on a separate
-  // thread to unblock the main thread.
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE,
       base::BindOnce(&IsTypeSupportedInternal, cdm_factory, key_system,
diff --git a/media/cdm/win/media_foundation_cdm_unittest.cc b/media/cdm/win/media_foundation_cdm_unittest.cc
index e8dd45c6..7d8c2f53 100644
--- a/media/cdm/win/media_foundation_cdm_unittest.cc
+++ b/media/cdm/win/media_foundation_cdm_unittest.cc
@@ -463,6 +463,9 @@
   Initialize();
   CreateSessionAndGenerateRequest();
 
+  COM_EXPECT_CALL(mf_cdm_, QueryInterface(IID_IMFAttributes, _))
+      .WillOnce(Return(E_FAIL));
+
   std::vector<uint8_t> response = StringToVector("response");
   COM_EXPECT_CALL(mf_cdm_session_, Update(NotNull(), response.size()))
       .WillOnce(DoAll([&] { mf_cdm_session_callbacks_->KeyStatusChanged(); },
diff --git a/media/cdm/win/media_foundation_cdm_util.cc b/media/cdm/win/media_foundation_cdm_util.cc
index b69905b7..4f65bfa 100644
--- a/media/cdm/win/media_foundation_cdm_util.cc
+++ b/media/cdm/win/media_foundation_cdm_util.cc
@@ -15,6 +15,7 @@
 #include "base/not_fatal_until.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/win/propvarutil.h"
+#include "base/win/scoped_bstr.h"
 #include "base/win/scoped_propvariant.h"
 #include "media/base/win/mf_helpers.h"
 #include "media/cdm/cdm_paths.h"
@@ -251,4 +252,48 @@
   return S_OK;
 }
 
+bool IsMediaFoundationContentTypeSupported(
+    Microsoft::WRL::ComPtr<IMFExtendedDRMTypeSupport> mf_type_support,
+    const std::string& key_system,
+    const std::string& content_type) {
+  DCHECK(!key_system.empty());
+  DCHECK(!content_type.empty());
+
+  if (key_system.empty() || content_type.empty()) {
+    DLOG(ERROR) << __func__ << ": key_system or content_type is empty";
+    return false;
+  }
+
+  // `IMFContentDecryptionModuleFactory::IsTypeSupported()` returns
+  // 'supported' for OS PlayReady backed implementation regardless of the
+  // value passed in for the `contentType` parameter. Use
+  // IMFExtendedDRMTypeSupport::IsTypeSupportedEx() instead.
+  MF_MEDIA_ENGINE_CANPLAY answer = MF_MEDIA_ENGINE_CANPLAY_NOT_SUPPORTED;
+  base::win::ScopedBstr key_system_bstr(base::UTF8ToWide(key_system).c_str());
+  base::win::ScopedBstr query(base::UTF8ToWide(content_type).c_str());
+
+  const int kMaxRetryCount = 5;
+  for (int retry = 0; retry < kMaxRetryCount; ++retry) {
+    // IsTypeSupportedEx returns "MAYBE" for HDCP queries while
+    // HDCP is being established. If the answer is "Maybe" then
+    // try again once per second for a total of 5 seconds.
+    HRESULT hr = mf_type_support->IsTypeSupportedEx(
+        query.Get(), key_system_bstr.Get(), &answer);
+
+    if (FAILED(hr)) {
+      DLOG(ERROR) << __func__ << ": type_query support failed. hr=" << hr;
+      return false;
+    } else if (answer != MF_MEDIA_ENGINE_CANPLAY_MAYBE) {
+      break;
+    }
+
+    DVLOG(2) << "IsTypeSupportedEx() returned MAYBE; wait for negotiation...";
+    base::PlatformThread::Sleep(base::Seconds(1));
+  }
+
+  DVLOG(2) << __func__ << ": answer=" << answer << ", " << key_system << ", "
+           << content_type;
+  return (answer == MF_MEDIA_ENGINE_CANPLAY_PROBABLY);
+}
+
 }  // namespace media
diff --git a/media/cdm/win/media_foundation_cdm_util.h b/media/cdm/win/media_foundation_cdm_util.h
index 779a8b7..cf6a90a3 100644
--- a/media/cdm/win/media_foundation_cdm_util.h
+++ b/media/cdm/win/media_foundation_cdm_util.h
@@ -27,6 +27,11 @@
     const base::FilePath& cdm_store_path_root,
     Microsoft::WRL::ComPtr<IMFContentDecryptionModule>& mf_cdm);
 
+MEDIA_EXPORT bool IsMediaFoundationContentTypeSupported(
+    Microsoft::WRL::ComPtr<IMFExtendedDRMTypeSupport> mf_type_support,
+    const std::string& key_system,
+    const std::string& content_type);
+
 }  // namespace media
 
 #endif  // MEDIA_CDM_WIN_MEDIA_FOUNDATION_CDM_UTIL_H_
diff --git a/media/cdm/win/media_foundation_cdm_util_unittest.cc b/media/cdm/win/media_foundation_cdm_util_unittest.cc
new file mode 100644
index 0000000..2083667
--- /dev/null
+++ b/media/cdm/win/media_foundation_cdm_util_unittest.cc
@@ -0,0 +1,307 @@
+// 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 "media/cdm/win/media_foundation_cdm_util.h"
+
+#include <Mferror.h>
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/task_environment.h"
+#include "base/win/scoped_bstr.h"
+#include "base/win/scoped_com_initializer.h"
+#include "media/base/win/mf_helpers.h"
+#include "media/base/win/mf_mocks.h"
+#include "media/cdm/win/media_foundation_cdm_module.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using Microsoft::WRL::ComPtr;
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+using ::testing::StrictMock;
+
+namespace media {
+
+namespace {
+
+const char kTestKeySystem[] = "com.example.test.key.system";
+const char kTestContentType[] = "video/mp4;codecs=\"avc1.4d401e\"";
+const CdmConfig kTestCdmConfig = {
+    kTestKeySystem,  // key_system
+    true,            // allow_distinctive_identifier
+    true,            // allow_persistent_state
+    true             // use_hw_secure_codecs
+};
+const base::UnguessableToken kTestOriginId = base::UnguessableToken::Create();
+const base::FilePath kTestStorePath(FILE_PATH_LITERAL("C:\\test\\store\\path"));
+
+}  // namespace
+
+class IsMediaFoundationContentTypeSupportedTest : public testing::Test {
+ public:
+  IsMediaFoundationContentTypeSupportedTest()
+      : mf_type_support_(MakeComPtr<MockMFExtendedDRMTypeSupport>()) {}
+
+  ~IsMediaFoundationContentTypeSupportedTest() override = default;
+
+ protected:
+  ComPtr<MockMFExtendedDRMTypeSupport> mf_type_support_;
+};
+
+TEST_F(IsMediaFoundationContentTypeSupportedTest, Probably) {
+  COM_EXPECT_CALL(mf_type_support_, IsTypeSupportedEx(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<2>(MF_MEDIA_ENGINE_CANPLAY_PROBABLY),
+                      Return(S_OK)));
+
+  bool result = IsMediaFoundationContentTypeSupported(
+      mf_type_support_, kTestKeySystem, kTestContentType);
+
+  EXPECT_TRUE(result);
+}
+
+TEST_F(IsMediaFoundationContentTypeSupportedTest, NotSupported) {
+  COM_EXPECT_CALL(mf_type_support_, IsTypeSupportedEx(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<2>(MF_MEDIA_ENGINE_CANPLAY_NOT_SUPPORTED),
+                      Return(S_OK)));
+
+  bool result = IsMediaFoundationContentTypeSupported(
+      mf_type_support_, kTestKeySystem, kTestContentType);
+
+  EXPECT_FALSE(result);
+}
+
+TEST_F(IsMediaFoundationContentTypeSupportedTest, Maybe) {
+  // Set up the mock to return MAYBE for each call,
+  // simulating ongoing HDCP negotiation
+  COM_EXPECT_CALL(mf_type_support_, IsTypeSupportedEx(_, _, _))
+      .Times(5)
+      .WillRepeatedly(
+          DoAll(SetArgPointee<2>(MF_MEDIA_ENGINE_CANPLAY_MAYBE), Return(S_OK)));
+
+  // The method should retry the maximum number of times and then return false
+  bool result = IsMediaFoundationContentTypeSupported(
+      mf_type_support_, kTestKeySystem, kTestContentType);
+
+  // After only receiving MAYBE responses, the method should return false
+  EXPECT_FALSE(result);
+}
+
+TEST_F(IsMediaFoundationContentTypeSupportedTest, MaybeAndThenProbably) {
+  // Set up expectations in sequence
+  {
+    InSequence seq;
+    // First call returns MAYBE
+    COM_EXPECT_CALL(mf_type_support_, IsTypeSupportedEx(_, _, _))
+        .WillOnce(DoAll(SetArgPointee<2>(MF_MEDIA_ENGINE_CANPLAY_MAYBE),
+                        Return(S_OK)));
+
+    // Second call returns PROBABLY
+    COM_EXPECT_CALL(mf_type_support_, IsTypeSupportedEx(_, _, _))
+        .WillOnce(DoAll(SetArgPointee<2>(MF_MEDIA_ENGINE_CANPLAY_PROBABLY),
+                        Return(S_OK)));
+  }
+
+  bool result = IsMediaFoundationContentTypeSupported(
+      mf_type_support_, kTestKeySystem, kTestContentType);
+
+  EXPECT_TRUE(result);
+}
+
+TEST_F(IsMediaFoundationContentTypeSupportedTest, Failure) {
+  COM_EXPECT_CALL(mf_type_support_, IsTypeSupportedEx(_, _, _))
+      .WillOnce(Return(E_FAIL));
+
+  bool result = IsMediaFoundationContentTypeSupported(
+      mf_type_support_, kTestKeySystem, kTestContentType);
+
+  EXPECT_FALSE(result);
+}
+
+class CreateMediaFoundationCdmTest : public testing::Test {
+ public:
+  CreateMediaFoundationCdmTest()
+      : mf_cdm_factory_(MakeComPtr<MockMFCdmFactory>()),
+        mf_cdm_access_(MakeComPtr<MockMFCdmAccess>()),
+        mf_cdm_(MakeComPtr<MockMFCdm>()),
+        mf_get_service_(MakeComPtr<MockMFGetService>()),
+        mf_pmp_host_(MakeComPtr<MockMFPMPHost>()),
+        mf_pmp_host_app_(MakeComPtr<MockMFPMPHostApp>()) {}
+
+  ~CreateMediaFoundationCdmTest() override = default;
+
+  void SetUpMockCdmFactoryExpectations(bool support_key_system) {
+    COM_EXPECT_CALL(mf_cdm_factory_, IsTypeSupported(_, nullptr))
+        .WillOnce(Return(support_key_system ? TRUE : FALSE));
+  }
+
+  void SetUpMockCdmAccessExpectations(bool success) {
+    HRESULT hr = success ? S_OK : E_FAIL;
+    COM_EXPECT_CALL(mf_cdm_factory_, CreateContentDecryptionModuleAccess(
+                                         NotNull(), NotNull(), _, _))
+        .WillOnce(DoAll(SetComPointee<3>(mf_cdm_access_.Get()), Return(hr)));
+
+    if (success) {
+      COM_EXPECT_CALL(mf_cdm_access_, CreateContentDecryptionModule(_, _))
+          .WillOnce(DoAll(SetComPointee<1>(mf_cdm_.Get()), Return(S_OK)));
+    }
+  }
+
+  void SetUpOsCdmExpectations(bool with_pmp_host) {
+    MediaFoundationCdmModule::GetInstance()->SetIsOsCdmForTesting(true);
+    {
+      // Setup expectations to simulate OS CDM
+      InSequence seq;
+
+      COM_ON_CALL(mf_cdm_, QueryInterface(IID_IMFGetService, _))
+          .WillByDefault(SetComPointeeAndReturnOk<1>(mf_get_service_.Get()));
+
+      if (with_pmp_host) {
+        COM_EXPECT_CALL(mf_get_service_, GetService(_, IID_IMFPMPHost, _))
+            .WillOnce(
+                DoAll(SetComPointee<2>(mf_pmp_host_.Get()), Return(S_OK)));
+
+        COM_EXPECT_CALL(mf_cdm_, SetPMPHostApp(_)).WillOnce(Return(S_OK));
+      } else {
+        COM_EXPECT_CALL(mf_get_service_, GetService(_, IID_IMFPMPHost, _))
+            .WillOnce(Return(E_NOINTERFACE));
+
+        COM_EXPECT_CALL(mf_get_service_, GetService(_, IID_IMFPMPHostApp, _))
+            .WillOnce(
+                DoAll(SetComPointee<2>(mf_pmp_host_app_.Get()), Return(S_OK)));
+
+        COM_EXPECT_CALL(mf_cdm_, SetPMPHostApp(_)).WillOnce(Return(S_OK));
+      }
+    }
+  }
+
+ protected:
+  ComPtr<MockMFCdmFactory> mf_cdm_factory_;
+  ComPtr<MockMFCdmAccess> mf_cdm_access_;
+  ComPtr<MockMFCdm> mf_cdm_;
+  ComPtr<MockMFGetService> mf_get_service_;
+  ComPtr<MockMFPMPHost> mf_pmp_host_;
+  ComPtr<MockMFPMPHostApp> mf_pmp_host_app_;
+
+  base::win::ScopedCOMInitializer com_initializer_;
+};
+
+TEST_F(CreateMediaFoundationCdmTest, Success) {
+  SetUpMockCdmFactoryExpectations(true);
+  SetUpMockCdmAccessExpectations(true);
+  MediaFoundationCdmModule::GetInstance()->SetIsOsCdmForTesting(false);
+
+  ComPtr<IMFContentDecryptionModule> result_cdm;
+  HRESULT hr =
+      CreateMediaFoundationCdm(mf_cdm_factory_, kTestCdmConfig, kTestOriginId,
+                               std::nullopt, kTestStorePath, result_cdm);
+
+  EXPECT_EQ(hr, S_OK);
+  EXPECT_EQ(result_cdm.Get(), mf_cdm_.Get());
+}
+
+TEST_F(CreateMediaFoundationCdmTest, KeySystemNotSupported) {
+  // Set up mock to indicate key system not supported
+  SetUpMockCdmFactoryExpectations(false);
+
+  ComPtr<IMFContentDecryptionModule> result_cdm;
+  HRESULT hr =
+      CreateMediaFoundationCdm(mf_cdm_factory_, kTestCdmConfig, kTestOriginId,
+                               std::nullopt, kTestStorePath, result_cdm);
+
+  EXPECT_EQ(hr, (HRESULT)MF_NOT_SUPPORTED_ERR);
+  EXPECT_EQ(result_cdm.Get(), nullptr);
+}
+
+TEST_F(CreateMediaFoundationCdmTest, CreateAccessFailed) {
+  // Set up mock to indicate key system supported but CDM access creation fails
+  SetUpMockCdmFactoryExpectations(true);
+  COM_EXPECT_CALL(mf_cdm_factory_,
+                  CreateContentDecryptionModuleAccess(_, _, _, _))
+      .WillOnce(Return(E_FAIL));
+
+  ComPtr<IMFContentDecryptionModule> result_cdm;
+  HRESULT hr =
+      CreateMediaFoundationCdm(mf_cdm_factory_, kTestCdmConfig, kTestOriginId,
+                               std::nullopt, kTestStorePath, result_cdm);
+
+  EXPECT_EQ(hr, E_FAIL);
+  EXPECT_EQ(result_cdm.Get(), nullptr);
+}
+
+TEST_F(CreateMediaFoundationCdmTest, CreateCdmFailed) {
+  // Set up mock for successful key system support and CDM access creation
+  SetUpMockCdmFactoryExpectations(true);
+
+  COM_EXPECT_CALL(mf_cdm_factory_,
+                  CreateContentDecryptionModuleAccess(_, _, _, _))
+      .WillOnce(DoAll(SetComPointee<3>(mf_cdm_access_.Get()), Return(S_OK)));
+
+  COM_EXPECT_CALL(mf_cdm_access_, CreateContentDecryptionModule(_, _))
+      .WillOnce(Return(E_FAIL));
+
+  ComPtr<IMFContentDecryptionModule> result_cdm;
+  HRESULT hr =
+      CreateMediaFoundationCdm(mf_cdm_factory_, kTestCdmConfig, kTestOriginId,
+                               std::nullopt, kTestStorePath, result_cdm);
+
+  EXPECT_EQ(hr, E_FAIL);
+  EXPECT_EQ(result_cdm.Get(), nullptr);
+}
+
+TEST_F(CreateMediaFoundationCdmTest, OsCdmWithPmpHost) {
+  // Set up mock for successful CDM creation
+  SetUpMockCdmFactoryExpectations(true);
+  SetUpMockCdmAccessExpectations(true);
+
+  // Set up OS CDM with PMPHost expectations
+  SetUpOsCdmExpectations(true);
+
+  ComPtr<IMFContentDecryptionModule> result_cdm;
+  HRESULT hr =
+      CreateMediaFoundationCdm(mf_cdm_factory_, kTestCdmConfig, kTestOriginId,
+                               std::nullopt, kTestStorePath, result_cdm);
+
+  EXPECT_EQ(hr, S_OK);
+  EXPECT_EQ(result_cdm.Get(), mf_cdm_.Get());
+}
+
+TEST_F(CreateMediaFoundationCdmTest, OsCdmWithPmpHostApp) {
+  // Set up mock for successful CDM creation
+  SetUpMockCdmFactoryExpectations(true);
+  SetUpMockCdmAccessExpectations(true);
+
+  // Set up OS CDM with PMPHostApp expectations
+  SetUpOsCdmExpectations(false);
+
+  ComPtr<IMFContentDecryptionModule> result_cdm;
+  HRESULT hr =
+      CreateMediaFoundationCdm(mf_cdm_factory_, kTestCdmConfig, kTestOriginId,
+                               std::nullopt, kTestStorePath, result_cdm);
+
+  EXPECT_EQ(hr, S_OK);
+  EXPECT_EQ(result_cdm.Get(), mf_cdm_.Get());
+}
+
+TEST_F(CreateMediaFoundationCdmTest, WithClientToken) {
+  // Set up mock for successful CDM creation
+  SetUpMockCdmFactoryExpectations(true);
+  SetUpMockCdmAccessExpectations(true);
+  MediaFoundationCdmModule::GetInstance()->SetIsOsCdmForTesting(false);
+
+  std::vector<uint8_t> client_token = {1, 2, 3, 4, 5};
+
+  ComPtr<IMFContentDecryptionModule> result_cdm;
+  HRESULT hr =
+      CreateMediaFoundationCdm(mf_cdm_factory_, kTestCdmConfig, kTestOriginId,
+                               client_token, kTestStorePath, result_cdm);
+
+  EXPECT_EQ(hr, S_OK);
+  EXPECT_EQ(result_cdm.Get(), mf_cdm_.Get());
+}
+
+}  // namespace media
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index 2c6c0e3..ff199ef 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -673,9 +673,9 @@
   // below. Only the first buffer should have discard padding.
   // Note: Some packets marked for total discard have their `start_padding` set
   // to kInfiniteDuration. Ignore these packets.
-  if (first_valid_frame_timestamp_ == kNoTimestamp &&
+  if (!initial_start_padding_.has_value() &&
       start_padding != kInfiniteDuration) {
-    first_valid_frame_timestamp_ = buffer->timestamp() + start_padding;
+    initial_start_padding_ = start_padding;
   }
 
   last_packet_timestamp_ = buffer->timestamp();
@@ -684,14 +684,10 @@
   // Check if `buffer` contains only padding.
   const bool is_padding = buffer->duration() == start_padding + end_padding;
 
-  // Don't adjust for `first_valid_frame_timestamp_` if we don't have a valid
-  // timestamp yet. Otherwise, we end up with an infinite `new_duration`.
-  const base::TimeDelta base_timestamp =
-      first_valid_frame_timestamp_ == kNoTimestamp
-          ? base::TimeDelta()
-          : first_valid_frame_timestamp_;
+  const base::TimeDelta new_duration =
+      last_packet_timestamp_ -
+      initial_start_padding_.value_or(base::TimeDelta());
 
-  const base::TimeDelta new_duration = last_packet_timestamp_ - base_timestamp;
   if ((!is_padding && new_duration > duration_) || duration_ == kNoTimestamp) {
     duration_ = new_duration;
   }
diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h
index 64dc64c1..a7aa7028 100644
--- a/media/filters/ffmpeg_demuxer.h
+++ b/media/filters/ffmpeg_demuxer.h
@@ -26,6 +26,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -171,12 +172,12 @@
   scoped_refptr<base::SequencedTaskRunner> task_runner_;
   raw_ptr<AVStream> stream_;
   base::TimeDelta start_time_;
+  std::optional<base::TimeDelta> initial_start_padding_;
   std::unique_ptr<AudioDecoderConfig> audio_config_;
   std::unique_ptr<VideoDecoderConfig> video_config_;
   raw_ptr<MediaLog> media_log_;
   Type type_ = UNKNOWN;
   StreamLiveness liveness_ = StreamLiveness::kUnknown;
-  base::TimeDelta first_valid_frame_timestamp_ = kNoTimestamp;
   base::TimeDelta duration_;
   bool end_of_stream_;
   base::TimeDelta last_packet_timestamp_;
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index f2b41158..fba6aead 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -900,6 +900,38 @@
   verify_finite_duration(GetStream(DemuxerStream::AUDIO));
   verify_finite_duration(GetStream(DemuxerStream::VIDEO));
 }
+
+TEST_F(FFmpegDemuxerTest, Read_LargeStartTime_DurationUpdates) {
+  // This file is poorly muxed, but useful as a regression test. It has an
+  // initial duration of 10s, contains 20s of video data, starts at a timestamp
+  // of 10s, and (surprisingly) ends at ~20s.
+  // Reading from this stream should update the duration.
+  CreateDemuxer("mid-file-start-time.mp4");
+  InitializeDemuxer();
+
+  auto* stream =
+      reinterpret_cast<FFmpegDemuxerStream*>(GetStream(DemuxerStream::VIDEO));
+
+  auto inital_duration = stream->duration();
+  ASSERT_GT(inital_duration, base::Seconds(9));
+  ASSERT_LT(inital_duration, base::Seconds(11));
+
+  // The final duration of the file after reading all packets is just below 20s.
+  const base::TimeDelta target_duration = base::Seconds(19);
+
+  int remaining_reads = 300;
+  while (remaining_reads-- > 0 && stream->duration() < target_duration) {
+    base::RunLoop loop;
+    stream->Read(1, base::BindLambdaForTesting(
+                        [&](DemuxerStream::Status status,
+                            DemuxerStream::DecoderBufferVector buffers) {
+                          loop.QuitWhenIdle();
+                        }));
+    loop.Run();
+  }
+
+  EXPECT_GT(stream->duration(), target_duration);
+}
 #endif  // BUILDFLAG(USE_PROPRIETARY_CODECS)
 
 // Similar to the test above, but using sfx-opus.ogg, which has a much smaller
diff --git a/media/mojo/mojom/BUILD.gn b/media/mojo/mojom/BUILD.gn
index bbc8b7d..d9ef913 100644
--- a/media/mojo/mojom/BUILD.gn
+++ b/media/mojo/mojom/BUILD.gn
@@ -1004,6 +1004,7 @@
     ":speech_recognition_recognition_context",
     "//base",
     "//base/test:test_support",
+    "//gpu:test_support",
     "//media:test_support",
     "//media/mojo:test_support",
     "//mojo/public/cpp/test_support:test_utils",
diff --git a/media/mojo/mojom/speech_recognition_error_code.mojom b/media/mojo/mojom/speech_recognition_error_code.mojom
index f84f531..d9d189a0 100644
--- a/media/mojo/mojom/speech_recognition_error_code.mojom
+++ b/media/mojo/mojom/speech_recognition_error_code.mojom
@@ -20,5 +20,5 @@
   kBadGrammar,
   kLanguageNotSupported,
   kNoMatch,
-  [MinVersion=1] kRecognitionContextNotSupported,
+  [MinVersion=1] kPhrasesNotSupported,
 };
diff --git a/media/mojo/mojom/video_frame_mojom_traits_unittest.cc b/media/mojo/mojom/video_frame_mojom_traits_unittest.cc
index 4c1709c..e3270a77 100644
--- a/media/mojo/mojom/video_frame_mojom_traits_unittest.cc
+++ b/media/mojo/mojom/video_frame_mojom_traits_unittest.cc
@@ -18,6 +18,7 @@
 #include "base/numerics/safe_conversions.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
+#include "gpu/command_buffer/client/test_shared_image_interface.h"
 #include "gpu/command_buffer/common/mailbox.h"
 #include "gpu/command_buffer/common/mailbox_holder.h"
 #include "gpu/command_buffer/common/sync_token.h"
@@ -25,7 +26,6 @@
 #include "media/base/video_frame.h"
 #include "media/base/video_frame_layout.h"
 #include "media/mojo/mojom/traits_test_service.test-mojom.h"
-#include "media/video/fake_gpu_memory_buffer.h"
 #include "mojo/public/cpp/bindings/message.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote.h"
@@ -636,44 +636,36 @@
 }
 #endif  // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
 
-// BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) because
-// media::FakeGpuMemoryBuffer supports NativePixmapHandle backed
-// GpuMemoryBufferHandle only. !BUILDFLAG(IS_OZONE) so as to force
-// GpuMemoryBufferSupport to select gfx::ClientNativePixmapFactoryDmabuf for
-// gfx::ClientNativePixmapFactory.
-// TODO(crbug.com/40286368): Allow this test without !BUILDFLAG(IS_OZONE)
-#if (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) && !BUILDFLAG(IS_OZONE)
-TEST_F(VideoFrameStructTraitsTest, GpuMemoryBufferSharedImageVideoFrame) {
+TEST_F(VideoFrameStructTraitsTest, MappableSharedImageVideoFrame) {
+  auto test_sii = base::MakeRefCounted<gpu::TestSharedImageInterface>();
+  test_sii->UseTestGMBInSharedImageCreationWithBufferUsage();
   gfx::Size coded_size = gfx::Size(256, 256);
   gfx::Rect visible_rect(coded_size);
   auto timestamp = base::Milliseconds(1);
-  std::unique_ptr<gfx::GpuMemoryBuffer> gmb =
-      std::make_unique<FakeGpuMemoryBuffer>(
-          coded_size, gfx::BufferFormat::YUV_420_BIPLANAR);
-  gfx::BufferFormat expected_gmb_format = gmb->GetFormat();
-  gfx::Size expected_gmb_size = gmb->GetSize();
-  scoped_refptr<gpu::ClientSharedImage> shared_image =
-      gpu::ClientSharedImage::CreateForTesting();
-  auto frame = VideoFrame::WrapExternalGpuMemoryBuffer(
-      visible_rect, visible_rect.size(), std::move(gmb), shared_image,
-      gpu::SyncToken(), base::NullCallback(), timestamp);
+  auto si_format = viz::SinglePlaneFormat::kRGBA_8888;
+  const auto si_usage = gpu::SHARED_IMAGE_USAGE_CPU_WRITE_ONLY |
+                        gpu::SHARED_IMAGE_USAGE_DISPLAY_READ;
+  auto shared_image = test_sii->CreateSharedImage(
+      {si_format, coded_size, gfx::ColorSpace(),
+       gpu::SharedImageUsageSet(si_usage), "VideoFrameStructTraitsTest"},
+      gpu::kNullSurfaceHandle, gfx::BufferUsage::GPU_READ);
+  ASSERT_TRUE(shared_image);
+  auto frame = VideoFrame::WrapMappableSharedImage(
+      shared_image, test_sii->GenVerifiedSyncToken(), base::NullCallback(),
+      visible_rect, visible_rect.size(), timestamp);
+  ASSERT_TRUE(frame);
   ASSERT_TRUE(RoundTrip(&frame));
   ASSERT_TRUE(frame);
   ASSERT_EQ(frame->storage_type(), VideoFrame::STORAGE_GPU_MEMORY_BUFFER);
   EXPECT_TRUE(frame->HasMappableGpuBuffer());
   EXPECT_FALSE(frame->metadata().end_of_stream);
-  EXPECT_EQ(frame->format(), PIXEL_FORMAT_NV12);
+  EXPECT_EQ(frame->format(), PIXEL_FORMAT_ABGR);
   EXPECT_EQ(frame->coded_size(), coded_size);
   EXPECT_EQ(frame->visible_rect(), visible_rect);
   EXPECT_EQ(frame->natural_size(), visible_rect.size());
   EXPECT_EQ(frame->timestamp(), timestamp);
   ASSERT_TRUE(frame->HasSharedImage());
-  EXPECT_EQ(frame->mailbox_holder(0).mailbox, shared_image->mailbox());
-  EXPECT_EQ(frame->GetGpuMemoryBufferForTesting()->GetFormat(),
-            expected_gmb_format);
-  EXPECT_EQ(frame->GetGpuMemoryBufferForTesting()->GetSize(),
-            expected_gmb_size);
+  ASSERT_EQ(frame->shared_image()->mailbox(), shared_image->mailbox());
 }
-#endif  // (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) &&
-        // !BUILDFLAG(IS_OZONE)
+
 }  // namespace media
diff --git a/media/mojo/services/media_foundation_service.cc b/media/mojo/services/media_foundation_service.cc
index 43e289e..bff4dbb5 100644
--- a/media/mojo/services/media_foundation_service.cc
+++ b/media/mojo/services/media_foundation_service.cc
@@ -64,6 +64,9 @@
 const char kSwSecureRobustness[] = "SW_SECURE_DECODE";
 const char kHwSecureRobustness[] = "HW_SECURE_ALL";
 
+const char kPlayReadyKeySystemRecommendationHwSecure[] =
+    "com.microsoft.playready.recommendation.3000";
+
 // The followings define the supported codecs and encryption schemes that we try
 // to query.
 constexpr VideoCodec kAllVideoCodecs[] = {
@@ -124,6 +127,15 @@
   return supported;
 }
 
+bool IsTypeSupportedInternalEx(
+    ComPtr<IMFExtendedDRMTypeSupport> mf_type_support,
+    const std::string& key_system,
+    bool is_hw_secure,
+    const std::string& content_type) {
+  return IsMediaFoundationContentTypeSupported(mf_type_support, key_system,
+                                               content_type);
+}
+
 std::string GetFourCCString(VideoCodec codec) {
   switch (codec) {
     case VideoCodec::kH264:
@@ -234,10 +246,13 @@
     IsTypeSupportedCallback is_type_supported_cb) {
   base::flat_set<EncryptionScheme> supported_schemes;
   for (const auto scheme : kAllEncryptionSchemes) {
-    const FeatureMap extra_features = {
+    FeatureMap extra_features = {
         {kEncryptionSchemeQueryName, GetName(scheme)},
-        {kEncryptionIvQueryName, base::NumberToString(GetIvSize(scheme))},
-        {kRobustnessQueryName, robustness.c_str()}};
+        {kEncryptionIvQueryName, base::NumberToString(GetIvSize(scheme))}};
+
+    if (!robustness.empty()) {
+      extra_features.insert({kRobustnessQueryName, robustness});
+    }
 
     if (is_type_supported_cb.Run(
             is_hw_secure,
@@ -266,9 +281,12 @@
   //   C:\Users\<user>\AppData\Local\Packages\cr.sb.cdm<...>\AC\Temp
   // This folder is specifically for the CDM app container, so there's no need
   // to set ACL explicitly.
+  // Use a short name for the store path to help avoid hitting the MAX_PATH
+  // limitation. Note, this won't fix all scenarios since the path is still
+  // dependent on the username length.
   base::FilePath temp_dir;
   base::PathService::Get(base::DIR_TEMP, &temp_dir);
-  const char kDummyCdmStore[] = "DummyMediaFoundationCdmStore";
+  const char kDummyCdmStore[] = "DummyCdm";
   auto dummy_cdm_store_path_root = temp_dir.AppendASCII(kDummyCdmStore);
 
   // Create the dummy CDM.
@@ -305,6 +323,7 @@
     ComPtr<IMFContentDecryptionModuleFactory> cdm_factory,
     const std::string& key_system,
     bool is_hw_secure,
+    bool is_os_cdm,
     IsTypeSupportedCallback is_type_supported_cb) {
   DVLOG(2) << __func__ << ": key_system=" << key_system
            << ", is_hw_secure=" << is_hw_secure;
@@ -324,9 +343,16 @@
         CdmCapabilityQueryStatus::kCreateDummyMediaFoundationCdmFailed);
   }
 
-  // TODO(hmchen): make this generic for more key systems.
-  const std::string robustness =
-      is_hw_secure ? kHwSecureRobustness : kSwSecureRobustness;
+  std::string robustness;
+  FeatureMap extra_features = {};
+
+  if (!is_os_cdm) {
+    // TODO(hmchen): make this generic for more key systems.
+    robustness = is_hw_secure ? kHwSecureRobustness : kSwSecureRobustness;
+
+    // encryption-robustness is not a supported for PlayReady key systems.
+    extra_features.insert({{kRobustnessQueryName, robustness}});
+  }
 
   CdmCapability capability;
 
@@ -348,8 +374,6 @@
     }
 #endif
 
-    const FeatureMap extra_features = {{kRobustnessQueryName, robustness}};
-
     if (is_type_supported_cb.Run(
             is_hw_secure,
             GetTypeString(video_codec, /*audio_codec=*/std::nullopt,
@@ -397,7 +421,6 @@
   // supported video codecs> + <audio codec> to query the audio capability.
   for (const auto audio_codec : kAllAudioCodecs) {
     const auto& video_codec = capability.video_codecs.begin()->first;
-    const FeatureMap extra_features = {{kRobustnessQueryName, robustness}};
 
     if (is_type_supported_cb.Run(
             is_hw_secure,
@@ -440,8 +463,9 @@
 }  // namespace
 
 MediaFoundationService::MediaFoundationService(
+    bool is_os_cdm,
     mojo::PendingReceiver<mojom::MediaFoundationService> receiver)
-    : receiver_(this, std::move(receiver)) {
+    : receiver_(this, std::move(receiver)), is_os_cdm_(is_os_cdm) {
   DVLOG(1) << __func__;
   mojo_media_client_.Initialize();
 }
@@ -477,12 +501,45 @@
     return;
   }
 
+  IsTypeSupportedCallback is_type_supported_cb;
+
+  if (is_os_cdm_) {
+    // `IMFContentDecryptionModuleFactory::IsTypeSupported()` returns
+    // 'supported' for OS PlayReady backed implementation regardless of the
+    // value passed in for the `contentType` parameter. Use
+    // IMFExtendedDRMTypeSupport::IsTypeSupportedEx() instead.
+    ComPtr<IMFExtendedDRMTypeSupport> mf_type_support;
+    HRESULT hr =
+        CoCreateInstance(CLSID_MFMediaEngineClassFactory, nullptr,
+                         CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&mf_type_support));
+    if (FAILED(hr)) {
+      DLOG(ERROR) << __func__
+                  << ": Failed to create class factory for "
+                     "IMFExtendedDRMTypeSupport::IsTypeSupportedEx. hr="
+                  << hr;
+      std::move(callback).Run(
+          false, KeySystemCapability(
+                     // TODO(crbug.com/384962301): need better error codes here.
+                     base::unexpected(CdmCapabilityQueryStatus::kUnknown),
+                     base::unexpected(CdmCapabilityQueryStatus::kUnknown)));
+      return;
+    }
+
+    // Force the use of the hardware based PlayReady key system.
+    is_type_supported_cb =
+        base::BindRepeating(&IsTypeSupportedInternalEx, mf_type_support,
+                            kPlayReadyKeySystemRecommendationHwSecure);
+  } else {
+    is_type_supported_cb =
+        base::BindRepeating(&IsTypeSupportedInternal, cdm_factory, key_system);
+  }
+
   // Use empty software secure capability as it is not used.
   auto sw_cdm_capability_or_status =
       base::unexpected(CdmCapabilityQueryStatus::kNoSupportedVideoCodec);
-  auto hw_cdm_capability_or_status = GetCdmCapability(
-      cdm_factory, key_system, /*is_hw_secure=*/true,
-      base::BindRepeating(&IsTypeSupportedInternal, cdm_factory, key_system));
+  auto hw_cdm_capability_or_status =
+      GetCdmCapability(cdm_factory, key_system, /*is_hw_secure=*/true,
+                       is_os_cdm_, is_type_supported_cb);
   auto key_system_capability = KeySystemCapability(sw_cdm_capability_or_status,
                                                    hw_cdm_capability_or_status);
   if (!key_system_capability.sw_cdm_capability_or_status.has_value() &&
diff --git a/media/mojo/services/media_foundation_service.h b/media/mojo/services/media_foundation_service.h
index 6ee6eeb..1d8864ec 100644
--- a/media/mojo/services/media_foundation_service.h
+++ b/media/mojo/services/media_foundation_service.h
@@ -30,6 +30,7 @@
   // `ensure_sandboxed_cb` must be called after necessary initialization to
   // ensure the process is sandboxed.
   explicit MediaFoundationService(
+      bool is_os_cdm,
       mojo::PendingReceiver<mojom::MediaFoundationService> receiver);
   MediaFoundationService(const MediaFoundationService&) = delete;
   MediaFoundationService operator=(const MediaFoundationService&) = delete;
@@ -51,6 +52,11 @@
   // IMFContentDecryptionModule implementations typically require MTA to run.
   base::win::ScopedCOMInitializer com_initializer_{
       base::win::ScopedCOMInitializer::kMTA};
+
+  // An OS CDM is a CDM that is implemented by the OS, such as PlayReady.
+  // This type of CDM does not usually have a CDM path since the implementation
+  // is usually provided by the OS through a particular framework.
+  bool is_os_cdm_;
 };
 
 }  // namespace media
diff --git a/media/mojo/services/media_foundation_service_broker.cc b/media/mojo/services/media_foundation_service_broker.cc
index 98fa45f..74d6486 100644
--- a/media/mojo/services/media_foundation_service_broker.cc
+++ b/media/mojo/services/media_foundation_service_broker.cc
@@ -40,8 +40,8 @@
   bool success = MediaFoundationCdmModule::GetInstance()->Initialize(cdm_path);
   std::move(ensure_sandboxed_cb_).Run();
 
-  media_foundation_service_ =
-      std::make_unique<MediaFoundationService>(std::move(service_receiver));
+  media_foundation_service_ = std::make_unique<MediaFoundationService>(
+      /*is_os_cdm=*/cdm_path.empty(), std::move(service_receiver));
 
   DVLOG(1) << __func__ << ": success=" << success;
   if (!success) {
diff --git a/media/test/data/README.md b/media/test/data/README.md
index aba6795..8298cf8 100644
--- a/media/test/data/README.md
+++ b/media/test/data/README.md
@@ -76,6 +76,14 @@
 #### front-discard.mp4
 File recorded by a ChromeOS device, whose first packet is marked for complete discard.
 
+#### mid-file-start-time.mp4
+File generated by copying the last 20s of a 30s file, without adjusting timestamps.
+At the time when this file was generated, FFmpeg seems introduce some muxing issues.
+```
+ffmpeg -filter_complex "smptehdbars=size=1280x720" -t 30 -c:v libx264 -y bars.mp4
+ffmpeg -i bars.mp4 -ss 10 -copyts -c copy -movflags +faststart -y bars2.mp4
+```
+
 ### FLAC
 
 #### bear-flac.mp4
diff --git a/media/test/data/mid-file-start-time.mp4 b/media/test/data/mid-file-start-time.mp4
new file mode 100644
index 0000000..dfcf545
--- /dev/null
+++ b/media/test/data/mid-file-start-time.mp4
Binary files differ
diff --git a/media/test/media_bundle_data.filelist b/media/test/media_bundle_data.filelist
index 75f2ecd..7f76509e 100644
--- a/media/test/media_bundle_data.filelist
+++ b/media/test/media_bundle_data.filelist
@@ -393,6 +393,7 @@
 data/media_foundation_fallback.html
 data/media_source_player.html
 data/media_suspend_test.html
+data/mid-file-start-time.mp4
 data/midstream_config_change.mp3
 data/mono_cpe.adts
 data/mse_config_change.html
diff --git a/media/unit_tests_bundle_data.filelist b/media/unit_tests_bundle_data.filelist
index 20d5130..a4d5ba5 100644
--- a/media/unit_tests_bundle_data.filelist
+++ b/media/unit_tests_bundle_data.filelist
@@ -405,6 +405,7 @@
 //media/test/data/media_foundation_fallback.html
 //media/test/data/media_source_player.html
 //media/test/data/media_suspend_test.html
+//media/test/data/mid-file-start-time.mp4
 //media/test/data/midstream_config_change.mp3
 //media/test/data/mono_cpe.adts
 //media/test/data/mse_config_change.html
diff --git a/media/video/fake_gpu_memory_buffer.cc b/media/video/fake_gpu_memory_buffer.cc
index 0066457..d6bee7c 100644
--- a/media/video/fake_gpu_memory_buffer.cc
+++ b/media/video/fake_gpu_memory_buffer.cc
@@ -11,7 +11,6 @@
 
 #include "base/atomic_sequence_num.h"
 #include "build/build_config.h"
-#include "gpu/ipc/common/gpu_memory_buffer_impl.h"
 #include "media/base/format_utils.h"
 #include "media/base/video_frame.h"
 
@@ -38,38 +37,6 @@
 }
 #endif
 
-class FakeGpuMemoryBufferImpl : public gpu::GpuMemoryBufferImpl {
- public:
-  FakeGpuMemoryBufferImpl(const gfx::Size& size, gfx::BufferFormat format)
-      : gpu::GpuMemoryBufferImpl(
-            gfx::GpuMemoryBufferId(),
-            size,
-            format,
-            gpu::GpuMemoryBufferImpl::DestructionCallback()),
-        fake_gmb_(std::make_unique<media::FakeGpuMemoryBuffer>(size, format)) {}
-
-  // gfx::GpuMemoryBuffer implementation
-  bool Map() override { return fake_gmb_->Map(); }
-  void MapAsync(base::OnceCallback<void(bool)> result_cb) override {
-    fake_gmb_->MapAsync(std::move(result_cb));
-  }
-  bool AsyncMappingIsNonBlocking() const override {
-    return fake_gmb_->AsyncMappingIsNonBlocking();
-  }
-  void* memory(size_t plane) override { return fake_gmb_->memory(plane); }
-  void Unmap() override { fake_gmb_->Unmap(); }
-  int stride(size_t plane) const override { return fake_gmb_->stride(plane); }
-  gfx::GpuMemoryBufferType GetType() const override {
-    return fake_gmb_->GetType();
-  }
-  gfx::GpuMemoryBufferHandle CloneHandle() const override {
-    return fake_gmb_->CloneHandle();
-  }
-
- private:
-  std::unique_ptr<media::FakeGpuMemoryBuffer> fake_gmb_;
-};
-
 static base::AtomicSequenceNumber buffer_id_generator;
 
 }  // namespace
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 04e1ad5f..07a7c20 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -2069,6 +2069,8 @@
     "http/http_transaction_test_util.h",
     "http/mock_http_cache.cc",
     "http/mock_http_cache.h",
+    "http/no_vary_search_cache_storage_mock_file_operations.cc",
+    "http/no_vary_search_cache_storage_mock_file_operations.h",
     "http/no_vary_search_cache_test_utils.cc",
     "http/no_vary_search_cache_test_utils.h",
     "http/transport_security_state_test_util.cc",
diff --git a/net/cookies/cookie_monster_unittest.cc b/net/cookies/cookie_monster_unittest.cc
index 37f1707..924bd083 100644
--- a/net/cookies/cookie_monster_unittest.cc
+++ b/net/cookies/cookie_monster_unittest.cc
@@ -7512,14 +7512,12 @@
   net::SchemefulSite member4(GURL("https://member4.test"));
 
   access_delegate_->SetFirstPartySets({
-      {owner1,
-       net::FirstPartySetEntry(owner1, net::SiteType::kPrimary, std::nullopt)},
-      {member1, net::FirstPartySetEntry(owner1, net::SiteType::kAssociated, 0)},
-      {member2, net::FirstPartySetEntry(owner1, net::SiteType::kAssociated, 1)},
-      {owner2,
-       net::FirstPartySetEntry(owner2, net::SiteType::kPrimary, std::nullopt)},
-      {member3, net::FirstPartySetEntry(owner2, net::SiteType::kAssociated, 0)},
-      {member4, net::FirstPartySetEntry(owner2, net::SiteType::kAssociated, 1)},
+      {owner1, net::FirstPartySetEntry(owner1, net::SiteType::kPrimary)},
+      {member1, net::FirstPartySetEntry(owner1, net::SiteType::kAssociated)},
+      {member2, net::FirstPartySetEntry(owner1, net::SiteType::kAssociated)},
+      {owner2, net::FirstPartySetEntry(owner2, net::SiteType::kPrimary)},
+      {member3, net::FirstPartySetEntry(owner2, net::SiteType::kAssociated)},
+      {member4, net::FirstPartySetEntry(owner2, net::SiteType::kAssociated)},
   });
 
   ASSERT_TRUE(SetCookie(cm(), GURL("https://owner1.test"), kValidCookieLine));
diff --git a/net/first_party_sets/first_party_set_entry.cc b/net/first_party_sets/first_party_set_entry.cc
index 213b369..e35a56d 100644
--- a/net/first_party_sets/first_party_set_entry.cc
+++ b/net/first_party_sets/first_party_set_entry.cc
@@ -28,37 +28,11 @@
 
 }  // namespace
 
-FirstPartySetEntry::SiteIndex::SiteIndex() = default;
-
-FirstPartySetEntry::SiteIndex::SiteIndex(uint32_t value) : value_(value) {}
-
-bool FirstPartySetEntry::SiteIndex::operator==(const SiteIndex& other) const =
-    default;
-
 FirstPartySetEntry::FirstPartySetEntry() = default;
 
-FirstPartySetEntry::FirstPartySetEntry(
-    SchemefulSite primary,
-    SiteType site_type,
-    std::optional<FirstPartySetEntry::SiteIndex> site_index)
-    : primary_(std::move(primary)), site_type_(site_type) {
-  switch (site_type_) {
-    case SiteType::kPrimary:
-    case SiteType::kService:
-      CHECK(!site_index.has_value());
-      break;
-    case SiteType::kAssociated:
-      break;
-  }
-}
-
 FirstPartySetEntry::FirstPartySetEntry(SchemefulSite primary,
-                                       SiteType site_type,
-                                       uint32_t site_index)
-    : FirstPartySetEntry(
-          std::move(primary),
-          site_type,
-          std::make_optional(FirstPartySetEntry::SiteIndex(site_index))) {}
+                                       SiteType site_type)
+    : primary_(std::move(primary)), site_type_(site_type) {}
 
 FirstPartySetEntry::FirstPartySetEntry(const FirstPartySetEntry&) = default;
 FirstPartySetEntry& FirstPartySetEntry::operator=(const FirstPartySetEntry&) =
@@ -95,12 +69,6 @@
                        ", site_type: ", SiteTypeToString(site_type_), "}"});
 }
 
-std::ostream& operator<<(std::ostream& os,
-                         const FirstPartySetEntry::SiteIndex& index) {
-  os << index.value();
-  return os;
-}
-
 std::ostream& operator<<(std::ostream& os, const FirstPartySetEntry& entry) {
   os << "{" << entry.primary() << ", " << static_cast<int>(entry.site_type())
      << "}";
diff --git a/net/first_party_sets/first_party_set_entry.h b/net/first_party_sets/first_party_set_entry.h
index 17eee21..222c045 100644
--- a/net/first_party_sets/first_party_set_entry.h
+++ b/net/first_party_sets/first_party_set_entry.h
@@ -30,28 +30,10 @@
 // First-Party Set.
 class NET_EXPORT FirstPartySetEntry {
  public:
-  class NET_EXPORT SiteIndex {
-   public:
-    SiteIndex();
-    explicit SiteIndex(uint32_t value);
-
-    bool operator==(const SiteIndex& other) const;
-
-    uint32_t value() const { return value_; }
-
-   private:
-    uint32_t value_;
-  };
-
   FirstPartySetEntry();
   // `primary` is the primary site in the First-Party Set associated with this
   // entry.
-  FirstPartySetEntry(SchemefulSite primary,
-                     SiteType site_type,
-                     std::optional<SiteIndex> site_index);
-  FirstPartySetEntry(SchemefulSite primary,
-                     SiteType site_type,
-                     uint32_t site_index);
+  FirstPartySetEntry(SchemefulSite primary, SiteType site_type);
 
   FirstPartySetEntry(const FirstPartySetEntry&);
   FirstPartySetEntry& operator=(const FirstPartySetEntry&);
@@ -78,9 +60,6 @@
   SiteType site_type_;
 };
 
-NET_EXPORT std::ostream& operator<<(
-    std::ostream& os,
-    const FirstPartySetEntry::SiteIndex& site_index);
 NET_EXPORT std::ostream& operator<<(std::ostream& os,
                                     const FirstPartySetEntry& fpse);
 
diff --git a/net/first_party_sets/first_party_set_entry_override_unittest.cc b/net/first_party_sets/first_party_set_entry_override_unittest.cc
index 0b5b1cd..9b19d211 100644
--- a/net/first_party_sets/first_party_set_entry_override_unittest.cc
+++ b/net/first_party_sets/first_party_set_entry_override_unittest.cc
@@ -19,13 +19,13 @@
   EXPECT_FALSE(
       FirstPartySetEntryOverride(
           FirstPartySetEntry(SchemefulSite(GURL("https://example.test")),
-                             SiteType::kPrimary, std::nullopt))
+                             SiteType::kPrimary))
           .IsDeletion());
 }
 
 TEST(FirstPartySetEntryOverrideTest, GetEntry) {
   FirstPartySetEntry entry(SchemefulSite(GURL("https://example.test")),
-                           SiteType::kPrimary, std::nullopt);
+                           SiteType::kPrimary);
   EXPECT_EQ(FirstPartySetEntryOverride(entry).GetEntry(), entry);
 }
 
diff --git a/net/first_party_sets/first_party_sets_context_config_unittest.cc b/net/first_party_sets/first_party_sets_context_config_unittest.cc
index fdf9a8bf..96f2e7b 100644
--- a/net/first_party_sets/first_party_sets_context_config_unittest.cc
+++ b/net/first_party_sets/first_party_sets_context_config_unittest.cc
@@ -34,7 +34,7 @@
 
 TEST(FirstPartySetsContextConfigTest, FindOverride_irrelevant) {
   SchemefulSite example(GURL("https://example.test"));
-  FirstPartySetEntry entry(example, SiteType::kPrimary, std::nullopt);
+  FirstPartySetEntry entry(example, SiteType::kPrimary);
   SchemefulSite foo(GURL("https://foo.test"));
 
   EXPECT_EQ(FirstPartySetsContextConfig::Create(
@@ -56,7 +56,7 @@
 
 TEST(FirstPartySetsContextConfigTest, FindOverride_modification) {
   SchemefulSite example(GURL("https://example.test"));
-  FirstPartySetEntry entry(example, SiteType::kPrimary, std::nullopt);
+  FirstPartySetEntry entry(example, SiteType::kPrimary);
 
   EXPECT_THAT(FirstPartySetsContextConfig::Create(
                   {{example, FirstPartySetEntryOverride(entry)}})
@@ -126,9 +126,8 @@
   const SchemefulSite foo(GURL("https://foo.test"));
   const SchemefulSite foo_alias(GURL("https://foo.test2"));
 
-  const FirstPartySetEntry primary_entry(example, SiteType::kPrimary,
-                                         std::nullopt);
-  const FirstPartySetEntry associated_entry(example, SiteType::kAssociated, 0);
+  const FirstPartySetEntry primary_entry(example, SiteType::kPrimary);
+  const FirstPartySetEntry associated_entry(example, SiteType::kAssociated);
 
   const FirstPartySetsContextConfig config =
       FirstPartySetsContextConfig::Create(
@@ -153,11 +152,9 @@
   const SchemefulSite bar(GURL("https://bar.test"));
   const SchemefulSite bar_alias(GURL("https://bar.test2"));
 
-  const FirstPartySetEntry primary_entry(example, SiteType::kPrimary,
-                                         std::nullopt);
-  const FirstPartySetEntry associated_entry(example, SiteType::kAssociated, 0);
-  const FirstPartySetEntry service_entry(example, SiteType::kService,
-                                         std::nullopt);
+  const FirstPartySetEntry primary_entry(example, SiteType::kPrimary);
+  const FirstPartySetEntry associated_entry(example, SiteType::kAssociated);
+  const FirstPartySetEntry service_entry(example, SiteType::kService);
 
   const FirstPartySetsContextConfig config =
       FirstPartySetsContextConfig::Create(
diff --git a/net/first_party_sets/global_first_party_sets.cc b/net/first_party_sets/global_first_party_sets.cc
index 617d10e..658dffa 100644
--- a/net/first_party_sets/global_first_party_sets.cc
+++ b/net/first_party_sets/global_first_party_sets.cc
@@ -328,8 +328,7 @@
                 member, FirstPartySetEntry(entry->second.primary(),
                                            member == entry->second.primary()
                                                ? SiteType::kPrimary
-                                               : SiteType::kAssociated,
-                                           std::nullopt));
+                                               : SiteType::kAssociated));
           }
           if (member == set_entry.primary())
             return true;
@@ -436,8 +435,7 @@
         bool inserted =
             normalized
                 .emplace(child_site_and_entry.first,
-                         FirstPartySetEntry(rep_primary, SiteType::kAssociated,
-                                            std::nullopt))
+                         FirstPartySetEntry(rep_primary, SiteType::kAssociated))
                 .second;
         CHECK(inserted);
       }
diff --git a/net/first_party_sets/global_first_party_sets_unittest.cc b/net/first_party_sets/global_first_party_sets_unittest.cc
index 3537bb3..50788d4 100644
--- a/net/first_party_sets/global_first_party_sets_unittest.cc
+++ b/net/first_party_sets/global_first_party_sets_unittest.cc
@@ -73,10 +73,8 @@
   GlobalFirstPartySets sets(
       base::Version(), /*entries=*/
       {
-          {kPrimary,
-           FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-          {kAssociated1,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+          {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+          {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
       },
       /*aliases=*/{});
 
@@ -90,13 +88,13 @@
   const SchemefulSite example(GURL("https://example.test"));
   const SchemefulSite example_cctld(GURL("https://example.cctld"));
   const SchemefulSite member1(GURL("https://member1.test"));
-  const FirstPartySetEntry entry(example, SiteType::kPrimary, std::nullopt);
-  const FirstPartySetEntry member1_entry(example, SiteType::kAssociated, 1);
+  const FirstPartySetEntry entry(example, SiteType::kPrimary);
+  const FirstPartySetEntry member1_entry(example, SiteType::kAssociated);
 
   const SchemefulSite foo(GURL("https://foo.test"));
   const SchemefulSite member2(GURL("https://member2.test"));
-  const FirstPartySetEntry foo_entry(foo, SiteType::kPrimary, std::nullopt);
-  const FirstPartySetEntry member2_entry(foo, SiteType::kAssociated, 1);
+  const FirstPartySetEntry foo_entry(foo, SiteType::kPrimary);
+  const FirstPartySetEntry member2_entry(foo, SiteType::kAssociated);
 
   GlobalFirstPartySets sets(version,
                             /*entries=*/
@@ -115,8 +113,7 @@
   GlobalFirstPartySets global_sets(
       kVersion, /*entries=*/
       {
-          {kPrimary,
-           FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
+          {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
       },
       /*aliases=*/
       {
@@ -126,10 +123,8 @@
   EXPECT_THAT(
       CollectEffectiveSetEntries(global_sets, FirstPartySetsContextConfig()),
       UnorderedElementsAre(
-          Pair(kPrimaryCctld,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
-          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary,
-                                            std::nullopt))));
+          Pair(kPrimaryCctld, FirstPartySetEntry(kPrimary, SiteType::kPrimary)),
+          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary))));
 }
 
 TEST_F(GlobalFirstPartySetsTest, FindEntry_Nonexistent) {
@@ -143,8 +138,8 @@
 TEST_F(GlobalFirstPartySetsTest, FindEntry_Exists) {
   SchemefulSite example(GURL("https://example.test"));
   SchemefulSite decoy_site(GURL("https://decoy.test"));
-  FirstPartySetEntry entry(example, SiteType::kPrimary, std::nullopt);
-  FirstPartySetEntry decoy_entry(example, SiteType::kAssociated, 1);
+  FirstPartySetEntry entry(example, SiteType::kPrimary);
+  FirstPartySetEntry decoy_entry(example, SiteType::kAssociated);
 
   EXPECT_THAT(GlobalFirstPartySets(kVersion,
                                    {
@@ -160,8 +155,8 @@
   SchemefulSite https_example(GURL("https://example.test"));
   SchemefulSite associated(GURL("https://associated.test"));
   SchemefulSite wss_example(GURL("wss://example.test"));
-  FirstPartySetEntry entry(https_example, SiteType::kPrimary, std::nullopt);
-  FirstPartySetEntry assoc_entry(https_example, SiteType::kAssociated, 0);
+  FirstPartySetEntry entry(https_example, SiteType::kPrimary);
+  FirstPartySetEntry assoc_entry(https_example, SiteType::kAssociated);
 
   EXPECT_THAT(GlobalFirstPartySets(kVersion,
                                    {
@@ -176,9 +171,9 @@
 TEST_F(GlobalFirstPartySetsTest, FindEntry_ExistsViaOverride) {
   SchemefulSite example(GURL("https://example.test"));
   SchemefulSite associated(GURL("https://associated.test"));
-  FirstPartySetEntry public_entry(example, SiteType::kPrimary, std::nullopt);
-  FirstPartySetEntry assoc_entry(example, SiteType::kAssociated, 0);
-  FirstPartySetEntry override_entry(example, SiteType::kAssociated, 1);
+  FirstPartySetEntry public_entry(example, SiteType::kPrimary);
+  FirstPartySetEntry assoc_entry(example, SiteType::kAssociated);
+  FirstPartySetEntry override_entry(example, SiteType::kAssociated);
 
   FirstPartySetsContextConfig config =
       FirstPartySetsContextConfig::Create(
@@ -198,8 +193,8 @@
 TEST_F(GlobalFirstPartySetsTest, FindEntry_RemovedViaOverride) {
   SchemefulSite example(GURL("https://example.test"));
   SchemefulSite associated(GURL("https://associated.test"));
-  FirstPartySetEntry public_entry(example, SiteType::kPrimary, std::nullopt);
-  FirstPartySetEntry assoc_entry(example, SiteType::kAssociated, 0);
+  FirstPartySetEntry public_entry(example, SiteType::kPrimary);
+  FirstPartySetEntry assoc_entry(example, SiteType::kAssociated);
 
   FirstPartySetsContextConfig config =
       FirstPartySetsContextConfig::Create(
@@ -219,7 +214,7 @@
 TEST_F(GlobalFirstPartySetsTest, FindEntry_ExistsViaAlias) {
   SchemefulSite example(GURL("https://example.test"));
   SchemefulSite example_cctld(GURL("https://example.cctld"));
-  FirstPartySetEntry entry(example, SiteType::kPrimary, std::nullopt);
+  FirstPartySetEntry entry(example, SiteType::kPrimary);
 
   EXPECT_THAT(GlobalFirstPartySets(kVersion,
                                    {
@@ -233,8 +228,8 @@
 TEST_F(GlobalFirstPartySetsTest, FindEntry_ExistsViaOverrideWithDecoyAlias) {
   SchemefulSite example(GURL("https://example.test"));
   SchemefulSite example_cctld(GURL("https://example.cctld"));
-  FirstPartySetEntry public_entry(example, SiteType::kPrimary, std::nullopt);
-  FirstPartySetEntry override_entry(example, SiteType::kAssociated, 1);
+  FirstPartySetEntry public_entry(example, SiteType::kPrimary);
+  FirstPartySetEntry override_entry(example, SiteType::kAssociated);
 
   FirstPartySetsContextConfig config =
       FirstPartySetsContextConfig::Create(
@@ -253,7 +248,7 @@
 TEST_F(GlobalFirstPartySetsTest, FindEntry_RemovedViaOverrideWithDecoyAlias) {
   SchemefulSite example(GURL("https://example.test"));
   SchemefulSite example_cctld(GURL("https://example.cctld"));
-  FirstPartySetEntry public_entry(example, SiteType::kPrimary, std::nullopt);
+  FirstPartySetEntry public_entry(example, SiteType::kPrimary);
 
   FirstPartySetsContextConfig config =
       FirstPartySetsContextConfig::Create(
@@ -272,8 +267,8 @@
 TEST_F(GlobalFirstPartySetsTest, FindEntry_AliasesIgnoredForConfig) {
   SchemefulSite example(GURL("https://example.test"));
   SchemefulSite example_cctld(GURL("https://example.cctld"));
-  FirstPartySetEntry public_entry(example, SiteType::kPrimary, std::nullopt);
-  FirstPartySetEntry override_entry(example, SiteType::kAssociated, 1);
+  FirstPartySetEntry public_entry(example, SiteType::kPrimary);
+  FirstPartySetEntry override_entry(example, SiteType::kAssociated);
 
   FirstPartySetsContextConfig config =
       FirstPartySetsContextConfig::Create(
@@ -300,10 +295,9 @@
       GlobalFirstPartySets(
           kVersion,
           {
-              {kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
+              {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
               {kAssociated4,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
           },
           {})
           .empty());
@@ -315,10 +309,9 @@
       LocalSetDeclaration::Create(
           /*set_entries=*/
           {
-              {kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
+              {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
               {kAssociated4,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
           },
           /*aliases=*/{})
           .value());
@@ -329,10 +322,8 @@
   GlobalFirstPartySets sets(
       base::Version(), /*entries=*/
       {
-          {kPrimary,
-           FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-          {kAssociated1,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+          {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+          {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
       },
       /*aliases=*/{});
   ASSERT_TRUE(sets.empty());
@@ -340,10 +331,9 @@
       LocalSetDeclaration::Create(
           /*set_entries=*/
           {
-              {kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
+              {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
               {kAssociated4,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
           },
           /*aliases=*/{})
           .value());
@@ -355,10 +345,9 @@
       sets.FindEntries({kPrimary, kAssociated1, kAssociated4},
                        FirstPartySetsContextConfig()),
       UnorderedElementsAre(
-          Pair(kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
+          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)),
           Pair(kAssociated4,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0))));
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated))));
 }
 
 TEST_F(GlobalFirstPartySetsTest,
@@ -368,12 +357,11 @@
       LocalSetDeclaration::Create(
           /*set_entries=*/
           {
-              {kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
+              {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
               {kAssociated4,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
               {kAssociated5,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)},
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
           },
           /*aliases=*/{})
           .value());
@@ -384,17 +372,14 @@
       /*replacement_sets=*/
       {
           {
-              {kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-              {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)},
+              {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+              {kAssociated1,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
               {kAssociated1Cctld,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                  std::nullopt)},
-              {kAssociated4, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)},
-              {kService,
-               FirstPartySetEntry(kPrimary, SiteType::kService, std::nullopt)},
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
+              {kAssociated4,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
+              {kService, FirstPartySetEntry(kPrimary, SiteType::kService)},
           },
       },
       /*addition_sets=*/{}, /*aliases=*/
@@ -408,16 +393,13 @@
       CollectEffectiveSetEntries(global_sets, config),
       UnorderedElementsAre(
           Pair(kAssociated1Cctld,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                  std::nullopt)),
-          Pair(kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)),
-          Pair(kAssociated4, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)),
-          Pair(kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
-          Pair(kService, FirstPartySetEntry(kPrimary, SiteType::kService,
-                                            std::nullopt))));
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kAssociated1,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kAssociated4,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)),
+          Pair(kService, FirstPartySetEntry(kPrimary, SiteType::kService))));
 }
 
 class PopulatedGlobalFirstPartySetsTest : public GlobalFirstPartySetsTest {
@@ -426,21 +408,15 @@
       : global_sets_(
             kVersion,
             {
-                {kPrimary, FirstPartySetEntry(kPrimary,
-                                              SiteType::kPrimary,
-                                              std::nullopt)},
+                {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
                 {kAssociated1,
-                 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+                 FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
                 {kAssociated2,
-                 FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)},
-                {kService, FirstPartySetEntry(kPrimary,
-                                              SiteType::kService,
-                                              std::nullopt)},
-                {kPrimary2, FirstPartySetEntry(kPrimary2,
-                                               SiteType::kPrimary,
-                                               std::nullopt)},
+                 FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
+                {kService, FirstPartySetEntry(kPrimary, SiteType::kService)},
+                {kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary)},
                 {kAssociated3,
-                 FirstPartySetEntry(kPrimary2, SiteType::kAssociated, 0)},
+                 FirstPartySetEntry(kPrimary2, SiteType::kAssociated)},
             },
             {
                 {kAssociated1Cctld, kAssociated1},
@@ -460,10 +436,9 @@
       LocalSetDeclaration::Create(
           /*set_entries=*/
           {
-              {kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
+              {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
               {kAssociated4,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
           },
           /*aliases=*/{})
           .value());
@@ -480,10 +455,9 @@
           },
           FirstPartySetsContextConfig()),
       UnorderedElementsAre(
-          Pair(kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
+          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)),
           Pair(kAssociated4,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0))));
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated))));
 }
 
 TEST_F(PopulatedGlobalFirstPartySetsTest,
@@ -494,10 +468,8 @@
       LocalSetDeclaration::Create(
           /*set_entries=*/
           {
-              {kPrimary3,
-               FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)},
-              {kPrimary,
-               FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0)},
+              {kPrimary3, FirstPartySetEntry(kPrimary3, SiteType::kPrimary)},
+              {kPrimary, FirstPartySetEntry(kPrimary3, SiteType::kAssociated)},
           },
           /*aliases=*/{})
           .value());
@@ -515,10 +487,9 @@
           },
           FirstPartySetsContextConfig()),
       UnorderedElementsAre(
-          Pair(kPrimary3,
-               FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)),
+          Pair(kPrimary3, FirstPartySetEntry(kPrimary3, SiteType::kPrimary)),
           Pair(kPrimary,
-               FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0))));
+               FirstPartySetEntry(kPrimary3, SiteType::kAssociated))));
 }
 
 TEST_F(PopulatedGlobalFirstPartySetsTest,
@@ -531,10 +502,9 @@
           /*set_entries=*/
           {
               {kAssociated1,
-               FirstPartySetEntry(kAssociated1, SiteType::kPrimary,
-                                  std::nullopt)},
+               FirstPartySetEntry(kAssociated1, SiteType::kPrimary)},
               {kAssociated4,
-               FirstPartySetEntry(kAssociated1, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(kAssociated1, SiteType::kAssociated)},
           },
           /*aliases=*/{})
           .value());
@@ -552,17 +522,14 @@
           },
           FirstPartySetsContextConfig()),
       UnorderedElementsAre(
-          Pair(kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
+          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)),
           Pair(kAssociated2,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)),
-          Pair(kService,
-               FirstPartySetEntry(kPrimary, SiteType::kService, std::nullopt)),
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kService, FirstPartySetEntry(kPrimary, SiteType::kService)),
           Pair(kAssociated1,
-               FirstPartySetEntry(kAssociated1, SiteType::kPrimary,
-                                  std::nullopt)),
+               FirstPartySetEntry(kAssociated1, SiteType::kPrimary)),
           Pair(kAssociated4,
-               FirstPartySetEntry(kAssociated1, SiteType::kAssociated, 0))));
+               FirstPartySetEntry(kAssociated1, SiteType::kAssociated))));
 }
 
 TEST_F(PopulatedGlobalFirstPartySetsTest,
@@ -573,10 +540,9 @@
       LocalSetDeclaration::Create(
           /*set_entries=*/
           {
-              {kPrimary3,
-               FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)},
+              {kPrimary3, FirstPartySetEntry(kPrimary3, SiteType::kPrimary)},
               {kAssociated1,
-               FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(kPrimary3, SiteType::kAssociated)},
           },
           /*aliases=*/{})
           .value());
@@ -594,16 +560,13 @@
           },
           FirstPartySetsContextConfig()),
       UnorderedElementsAre(
-          Pair(kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
+          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)),
           Pair(kAssociated2,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)),
-          Pair(kService,
-               FirstPartySetEntry(kPrimary, SiteType::kService, std::nullopt)),
-          Pair(kPrimary3,
-               FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)),
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kService, FirstPartySetEntry(kPrimary, SiteType::kService)),
+          Pair(kPrimary3, FirstPartySetEntry(kPrimary3, SiteType::kPrimary)),
           Pair(kAssociated1,
-               FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0))));
+               FirstPartySetEntry(kPrimary3, SiteType::kAssociated))));
 }
 
 TEST_F(PopulatedGlobalFirstPartySetsTest,
@@ -614,10 +577,9 @@
       LocalSetDeclaration::Create(
           /*set_entries=*/
           {
-              {kPrimary3,
-               FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)},
+              {kPrimary3, FirstPartySetEntry(kPrimary3, SiteType::kPrimary)},
               {kAssociated3,
-               FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(kPrimary3, SiteType::kAssociated)},
           },
           /*aliases=*/{})
           .value());
@@ -636,10 +598,9 @@
       LocalSetDeclaration::Create(
           /*set_entries=*/
           {
-              {kPrimary3,
-               FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)},
+              {kPrimary3, FirstPartySetEntry(kPrimary3, SiteType::kPrimary)},
               {kAssociated1,
-               FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(kPrimary3, SiteType::kAssociated)},
           },
           /*aliases=*/
           {
@@ -647,19 +608,18 @@
           })
           .value());
 
-  EXPECT_THAT(
-      global_sets().FindEntries(
-          {
-              kAssociated1,
-              kAssociated1Cctld,
-              kAssociated1Cctld2,
-          },
-          FirstPartySetsContextConfig()),
-      UnorderedElementsAre(
-          Pair(kAssociated1,
-               FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0)),
-          Pair(kAssociated1Cctld2,
-               FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0))));
+  EXPECT_THAT(global_sets().FindEntries(
+                  {
+                      kAssociated1,
+                      kAssociated1Cctld,
+                      kAssociated1Cctld2,
+                  },
+                  FirstPartySetsContextConfig()),
+              UnorderedElementsAre(
+                  Pair(kAssociated1,
+                       FirstPartySetEntry(kPrimary3, SiteType::kAssociated)),
+                  Pair(kAssociated1Cctld2,
+                       FirstPartySetEntry(kPrimary3, SiteType::kAssociated))));
 }
 
 TEST_F(PopulatedGlobalFirstPartySetsTest, ForEachPublicSetEntry_FullIteration) {
@@ -688,19 +648,16 @@
       CollectEffectiveSetEntries(global_sets(), FirstPartySetsContextConfig()),
       UnorderedElementsAre(
           Pair(kAssociated1Cctld,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)),
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
           Pair(kAssociated1,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)),
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
           Pair(kAssociated2,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)),
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
           Pair(kAssociated3,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated, 0)),
-          Pair(kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
-          Pair(kPrimary2,
-               FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)),
-          Pair(kService, FirstPartySetEntry(kPrimary, SiteType::kService,
-                                            std::nullopt))));
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)),
+          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)),
+          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary)),
+          Pair(kService, FirstPartySetEntry(kPrimary, SiteType::kService))));
 }
 
 TEST_F(PopulatedGlobalFirstPartySetsTest,
@@ -711,10 +668,9 @@
       LocalSetDeclaration::Create(
           /*set_entries=*/
           {
-              {kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
+              {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
               {kAssociated4,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
           },
           /*aliases=*/{})
           .value());
@@ -723,13 +679,11 @@
       CollectEffectiveSetEntries(global_sets(), FirstPartySetsContextConfig()),
       UnorderedElementsAre(
           Pair(kAssociated3,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated, 0)),
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)),
           Pair(kAssociated4,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)),
-          Pair(kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
-          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary,
-                                             std::nullopt))));
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)),
+          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary))));
 }
 
 TEST_F(PopulatedGlobalFirstPartySetsTest,
@@ -740,17 +694,14 @@
       /*replacement_sets=*/
       {
           {
-              {kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-              {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)},
+              {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+              {kAssociated1,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
               {kAssociated1Cctld,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                  std::nullopt)},
-              {kAssociated4, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)},
-              {kService,
-               FirstPartySetEntry(kPrimary, SiteType::kService, std::nullopt)},
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
+              {kAssociated4,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
+              {kService, FirstPartySetEntry(kPrimary, SiteType::kService)},
           },
       },
       /*addition_sets=*/{},
@@ -763,20 +714,16 @@
       CollectEffectiveSetEntries(global_sets(), config),
       UnorderedElementsAre(
           Pair(kAssociated1Cctld,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                  std::nullopt)),
-          Pair(kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)),
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kAssociated1,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
           Pair(kAssociated3,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated, 0)),
-          Pair(kAssociated4, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)),
-          Pair(kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
-          Pair(kPrimary2,
-               FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)),
-          Pair(kService, FirstPartySetEntry(kPrimary, SiteType::kService,
-                                            std::nullopt))));
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)),
+          Pair(kAssociated4,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)),
+          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary)),
+          Pair(kService, FirstPartySetEntry(kPrimary, SiteType::kService))));
 }
 
 TEST_F(
@@ -788,12 +735,11 @@
       LocalSetDeclaration::Create(
           /*set_entries=*/
           {
-              {kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
+              {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
               {kAssociated4,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
               {kAssociated5,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)},
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
           },
           /*aliases=*/{})
           .value());
@@ -804,17 +750,14 @@
       /*replacement_sets=*/
       {
           {
-              {kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-              {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)},
+              {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+              {kAssociated1,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
               {kAssociated1Cctld,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                  std::nullopt)},
-              {kAssociated4, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)},
-              {kService,
-               FirstPartySetEntry(kPrimary, SiteType::kService, std::nullopt)},
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
+              {kAssociated4,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
+              {kService, FirstPartySetEntry(kPrimary, SiteType::kService)},
           },
       },
       /*addition_sets=*/{}, /*aliases=*/
@@ -828,20 +771,16 @@
       CollectEffectiveSetEntries(global_sets(), config),
       UnorderedElementsAre(
           Pair(kAssociated1Cctld,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                  std::nullopt)),
-          Pair(kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)),
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kAssociated1,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
           Pair(kAssociated3,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated, 0)),
-          Pair(kAssociated4, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)),
-          Pair(kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
-          Pair(kPrimary2,
-               FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)),
-          Pair(kService, FirstPartySetEntry(kPrimary, SiteType::kService,
-                                            std::nullopt))));
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)),
+          Pair(kAssociated4,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)),
+          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary)),
+          Pair(kService, FirstPartySetEntry(kPrimary, SiteType::kService))));
 }
 
 TEST_F(
@@ -851,10 +790,9 @@
       LocalSetDeclaration::Create(
           /*set_entries=*/
           {
-              {kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
+              {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
               {kAssociated1,
-               FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
           },
           /*aliases=*/
           {
@@ -866,11 +804,9 @@
       /*replacement_sets=*/
       {
           {
-              {kPrimary2,
-               FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)},
+              {kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary)},
               {kAssociated1,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
-                                  std::nullopt)},
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)},
           },
       },
       /*addition_sets=*/{}, /*aliases=*/{}));
@@ -879,16 +815,14 @@
       CollectEffectiveSetEntries(global_sets(), config),
       UnorderedElementsAre(
           Pair(kAssociated1,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
-                                  std::nullopt)),
-          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary,
-                                             std::nullopt))));
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)),
+          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary))));
 }
 
 TEST_F(PopulatedGlobalFirstPartySetsTest, ComputeMetadata) {
   SchemefulSite nonmember(GURL("https://nonmember.test"));
-  FirstPartySetEntry primary_entry(kPrimary, SiteType::kPrimary, std::nullopt);
-  FirstPartySetEntry associated_entry(kPrimary, SiteType::kAssociated, 0);
+  FirstPartySetEntry primary_entry(kPrimary, SiteType::kPrimary);
+  FirstPartySetEntry associated_entry(kPrimary, SiteType::kAssociated);
 
   // Works as usual for sites that are in First-Party sets.
   EXPECT_EQ(global_sets().ComputeMetadata(kAssociated1, &kAssociated1,
@@ -914,18 +848,18 @@
 }
 
 TEST_F(GlobalFirstPartySetsTest, ComputeConfig_Empty) {
-  EXPECT_EQ(GlobalFirstPartySets(
-                kVersion,
-                /*entries=*/
-                {
-                    {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary,
-                                                  std::nullopt)},
-                    {kAssociated1,
-                     FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
-                },
-                /*aliases=*/{})
-                .ComputeConfig(SetsMutation({}, {}, {})),
-            FirstPartySetsContextConfig());
+  EXPECT_EQ(
+      GlobalFirstPartySets(
+          kVersion,
+          /*entries=*/
+          {
+              {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+              {kAssociated1,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
+          },
+          /*aliases=*/{})
+          .ComputeConfig(SetsMutation({}, {}, {})),
+      FirstPartySetsContextConfig());
 }
 
 TEST_F(GlobalFirstPartySetsTest,
@@ -934,21 +868,17 @@
       kVersion,
       /*entries=*/
       {
-          {kPrimary,
-           FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-          {kAssociated1,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+          {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+          {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
       },
       /*aliases=*/{});
   FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
       /*replacement_sets=*/
       {
           {
-              {kPrimary2,
-               FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)},
+              {kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary)},
               {kAssociated2,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
-                                  std::nullopt)},
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)},
           },
       },
       /*addition_sets=*/{}, /*aliases=*/{}));
@@ -956,10 +886,8 @@
       sets.FindEntries({kAssociated2, kPrimary2}, config),
       UnorderedElementsAre(
           Pair(kAssociated2,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
-                                  std::nullopt)),
-          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary,
-                                             std::nullopt))));
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)),
+          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary))));
 }
 
 // The common associated site between the policy and existing set is removed
@@ -971,23 +899,18 @@
       kVersion,
       /*entries=*/
       {
-          {kPrimary,
-           FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-          {kAssociated1,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
-          {kAssociated2,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)},
+          {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+          {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
+          {kAssociated2, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
       },
       /*aliases=*/{});
   FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
       /*replacement_sets=*/
       {
           {
-              {kPrimary2,
-               FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)},
+              {kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary)},
               {kAssociated2,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
-                                  std::nullopt)},
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)},
           },
       },
       /*addition_sets=*/{}, /*aliases=*/{}));
@@ -995,10 +918,8 @@
       sets.FindEntries({kPrimary2, kAssociated2}, config),
       UnorderedElementsAre(
           Pair(kAssociated2,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
-                                  std::nullopt)),
-          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary,
-                                             std::nullopt))));
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)),
+          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary))));
 }
 
 // The common primary between the policy and existing set is removed and its
@@ -1010,22 +931,18 @@
       kVersion,
       /*entries=*/
       {
-          {kPrimary,
-           FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-          {kAssociated1,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
-          {kAssociated2,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)},
+          {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+          {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
+          {kAssociated2, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
       },
       /*aliases=*/{});
   FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
       /*replacement_sets=*/
       {
           {
-              {kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-              {kAssociated3, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)},
+              {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+              {kAssociated3,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
           },
       },
       /*addition_sets=*/{}, /*aliases=*/{}));
@@ -1033,10 +950,9 @@
       sets.FindEntries({kAssociated3, kPrimary, kAssociated1, kAssociated2},
                        config),
       UnorderedElementsAre(
-          Pair(kAssociated3, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)),
-          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary,
-                                            std::nullopt))));
+          Pair(kAssociated3,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary))));
 }
 
 // The common associated site between the policy and existing set is removed and
@@ -1048,21 +964,17 @@
       kVersion,
       /*entries=*/
       {
-          {kPrimary,
-           FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-          {kAssociated1,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+          {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+          {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
       },
       /*aliases=*/{});
   FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
       /*replacement_sets=*/
       {
           {
-              {kPrimary3,
-               FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)},
+              {kPrimary3, FirstPartySetEntry(kPrimary3, SiteType::kPrimary)},
               {kAssociated1,
-               FirstPartySetEntry(kPrimary3, SiteType::kAssociated,
-                                  std::nullopt)},
+               FirstPartySetEntry(kPrimary3, SiteType::kAssociated)},
           },
       },
       /*addition_sets=*/{}, /*aliases=*/{}));
@@ -1070,10 +982,8 @@
       sets.FindEntries({kAssociated1, kPrimary3, kPrimary}, config),
       UnorderedElementsAre(
           Pair(kAssociated1,
-               FirstPartySetEntry(kPrimary3, SiteType::kAssociated,
-                                  std::nullopt)),
-          Pair(kPrimary3, FirstPartySetEntry(kPrimary3, SiteType::kPrimary,
-                                             std::nullopt))));
+               FirstPartySetEntry(kPrimary3, SiteType::kAssociated)),
+          Pair(kPrimary3, FirstPartySetEntry(kPrimary3, SiteType::kPrimary))));
 }
 
 // The policy set and the existing set have nothing in common so the policy set
@@ -1084,10 +994,8 @@
       kVersion,
       /*entries=*/
       {
-          {kPrimary,
-           FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-          {kAssociated1,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+          {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+          {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
       },
       /*aliases=*/{});
   FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
@@ -1095,11 +1003,9 @@
       /*addition_sets=*/
       {
           {
-              {kPrimary2,
-               FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)},
+              {kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary)},
               {kAssociated2,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
-                                  std::nullopt)},
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)},
           },
       },
       /*aliases=*/{}));
@@ -1107,10 +1013,8 @@
       sets.FindEntries({kAssociated2, kPrimary2}, config),
       UnorderedElementsAre(
           Pair(kAssociated2,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
-                                  std::nullopt)),
-          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary,
-                                             std::nullopt))));
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)),
+          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary))));
 }
 
 // The primary of a policy set is also an associated site in an existing set.
@@ -1123,10 +1027,8 @@
       kVersion,
       /*entries=*/
       {
-          {kPrimary,
-           FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-          {kAssociated1,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+          {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+          {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
       },
       /*aliases=*/{});
   FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
@@ -1135,32 +1037,25 @@
       {
           {
               {kAssociated1,
-               FirstPartySetEntry(kAssociated1, SiteType::kPrimary,
-                                  std::nullopt)},
+               FirstPartySetEntry(kAssociated1, SiteType::kPrimary)},
               {kAssociated2,
-               FirstPartySetEntry(kAssociated1, SiteType::kAssociated,
-                                  std::nullopt)},
+               FirstPartySetEntry(kAssociated1, SiteType::kAssociated)},
               {kAssociated3,
-               FirstPartySetEntry(kAssociated1, SiteType::kAssociated,
-                                  std::nullopt)},
+               FirstPartySetEntry(kAssociated1, SiteType::kAssociated)},
           },
       },
       /*aliases=*/{}));
-  EXPECT_THAT(
-      sets.FindEntries({kPrimary, kAssociated2, kAssociated3, kAssociated1},
-                       config),
-      UnorderedElementsAre(
-          Pair(kPrimary, FirstPartySetEntry(kAssociated1, SiteType::kAssociated,
-                                            std::nullopt)),
-          Pair(kAssociated2,
-               FirstPartySetEntry(kAssociated1, SiteType::kAssociated,
-                                  std::nullopt)),
-          Pair(kAssociated3,
-               FirstPartySetEntry(kAssociated1, SiteType::kAssociated,
-                                  std::nullopt)),
-          Pair(kAssociated1,
-               FirstPartySetEntry(kAssociated1, SiteType::kPrimary,
-                                  std::nullopt))));
+  EXPECT_THAT(sets.FindEntries(
+                  {kPrimary, kAssociated2, kAssociated3, kAssociated1}, config),
+              UnorderedElementsAre(
+                  Pair(kPrimary,
+                       FirstPartySetEntry(kAssociated1, SiteType::kAssociated)),
+                  Pair(kAssociated2,
+                       FirstPartySetEntry(kAssociated1, SiteType::kAssociated)),
+                  Pair(kAssociated3,
+                       FirstPartySetEntry(kAssociated1, SiteType::kAssociated)),
+                  Pair(kAssociated1,
+                       FirstPartySetEntry(kAssociated1, SiteType::kPrimary))));
 }
 
 // The primary of a policy set is also a primary of an existing set.
@@ -1173,36 +1068,30 @@
       kVersion,
       /*entries=*/
       {
-          {kPrimary,
-           FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-          {kAssociated1,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
-          {kAssociated3,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)},
+          {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+          {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
+          {kAssociated3, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
       },
       /*aliases=*/{});
   FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
       /*replacement_sets=*/{},
       /*addition_sets=*/
       {{
-          {kPrimary,
-           FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-          {kAssociated2,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, std::nullopt)},
+          {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+          {kAssociated2, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
       }},
       /*aliases=*/{}));
   EXPECT_THAT(
       sets.FindEntries({kAssociated1, kAssociated2, kAssociated3, kPrimary},
                        config),
       UnorderedElementsAre(
-          Pair(kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)),
-          Pair(kAssociated2, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)),
-          Pair(kAssociated3, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)),
-          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary,
-                                            std::nullopt))));
+          Pair(kAssociated1,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kAssociated2,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kAssociated3,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary))));
 }
 
 // Existing set overlaps with both replacement and addition set.
@@ -1213,32 +1102,26 @@
       kVersion,
       /*entries=*/
       {
-          {kPrimary,
-           FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-          {kAssociated1,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
-          {kAssociated2,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 1)},
+          {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+          {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
+          {kAssociated2, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
       },
       /*aliases=*/{});
   FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
       /*replacement_sets=*/
       {
           {
-              {kPrimary2,
-               FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)},
+              {kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary)},
               {kAssociated1,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
-                                  std::nullopt)},
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)},
           },
       },
       /*addition_sets=*/
       {
           {
-              {kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-              {kAssociated3, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)},
+              {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+              {kAssociated3,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
           },
       },
       /*aliases=*/{}));
@@ -1248,16 +1131,13 @@
           config),
       UnorderedElementsAre(
           Pair(kAssociated1,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
-                                  std::nullopt)),
-          Pair(kAssociated2, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)),
-          Pair(kAssociated3, FirstPartySetEntry(kPrimary, SiteType::kAssociated,
-                                                std::nullopt)),
-          Pair(kPrimary,
-               FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)),
-          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary,
-                                             std::nullopt))));
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)),
+          Pair(kAssociated2,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kAssociated3,
+               FirstPartySetEntry(kPrimary, SiteType::kAssociated)),
+          Pair(kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)),
+          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary))));
 }
 
 TEST_F(GlobalFirstPartySetsTest, TransitiveOverlap_TwoCommonPrimaries) {
@@ -1277,32 +1157,26 @@
       kVersion,
       /*entries=*/
       {
-          {primary1,
-           FirstPartySetEntry(primary1, SiteType::kPrimary, std::nullopt)},
-          {primary2, FirstPartySetEntry(primary1, SiteType::kAssociated, 0)},
+          {primary1, FirstPartySetEntry(primary1, SiteType::kPrimary)},
+          {primary2, FirstPartySetEntry(primary1, SiteType::kAssociated)},
       },
       /*aliases=*/{});
   FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
       /*replacement_sets=*/{},
       /*addition_sets=*/
       {
-          {{primary0,
-            FirstPartySetEntry(primary0, SiteType::kPrimary, std::nullopt)},
+          {{primary0, FirstPartySetEntry(primary0, SiteType::kPrimary)},
            {associated_site0,
-            FirstPartySetEntry(primary0, SiteType::kAssociated, std::nullopt)}},
-          {{primary1,
-            FirstPartySetEntry(primary1, SiteType::kPrimary, std::nullopt)},
+            FirstPartySetEntry(primary0, SiteType::kAssociated)}},
+          {{primary1, FirstPartySetEntry(primary1, SiteType::kPrimary)},
            {associated_site1,
-            FirstPartySetEntry(primary1, SiteType::kAssociated, std::nullopt)}},
-          {{primary2,
-            FirstPartySetEntry(primary2, SiteType::kPrimary, std::nullopt)},
+            FirstPartySetEntry(primary1, SiteType::kAssociated)}},
+          {{primary2, FirstPartySetEntry(primary2, SiteType::kPrimary)},
            {associated_site2,
-            FirstPartySetEntry(primary2, SiteType::kAssociated, std::nullopt)}},
-          {{primary42,
-            FirstPartySetEntry(primary42, SiteType::kPrimary, std::nullopt)},
+            FirstPartySetEntry(primary2, SiteType::kAssociated)}},
+          {{primary42, FirstPartySetEntry(primary42, SiteType::kPrimary)},
            {associated_site42,
-            FirstPartySetEntry(primary42, SiteType::kAssociated,
-                               std::nullopt)}},
+            FirstPartySetEntry(primary42, SiteType::kAssociated)}},
       },
       /*aliases=*/{}));
   EXPECT_THAT(
@@ -1320,25 +1194,17 @@
           config),
       UnorderedElementsAre(
           Pair(associated_site0,
-               FirstPartySetEntry(primary0, SiteType::kAssociated,
-                                  std::nullopt)),
+               FirstPartySetEntry(primary0, SiteType::kAssociated)),
           Pair(associated_site1,
-               FirstPartySetEntry(primary1, SiteType::kAssociated,
-                                  std::nullopt)),
+               FirstPartySetEntry(primary1, SiteType::kAssociated)),
           Pair(associated_site2,
-               FirstPartySetEntry(primary1, SiteType::kAssociated,
-                                  std::nullopt)),
+               FirstPartySetEntry(primary1, SiteType::kAssociated)),
           Pair(associated_site42,
-               FirstPartySetEntry(primary42, SiteType::kAssociated,
-                                  std::nullopt)),
-          Pair(primary0,
-               FirstPartySetEntry(primary0, SiteType::kPrimary, std::nullopt)),
-          Pair(primary1,
-               FirstPartySetEntry(primary1, SiteType::kPrimary, std::nullopt)),
-          Pair(primary2, FirstPartySetEntry(primary1, SiteType::kAssociated,
-                                            std::nullopt)),
-          Pair(primary42, FirstPartySetEntry(primary42, SiteType::kPrimary,
-                                             std::nullopt))));
+               FirstPartySetEntry(primary42, SiteType::kAssociated)),
+          Pair(primary0, FirstPartySetEntry(primary0, SiteType::kPrimary)),
+          Pair(primary1, FirstPartySetEntry(primary1, SiteType::kPrimary)),
+          Pair(primary2, FirstPartySetEntry(primary1, SiteType::kAssociated)),
+          Pair(primary42, FirstPartySetEntry(primary42, SiteType::kPrimary))));
 }
 
 TEST_F(GlobalFirstPartySetsTest, TransitiveOverlap_TwoCommonAssociatedSites) {
@@ -1358,32 +1224,26 @@
       kVersion,
       /*entries=*/
       {
-          {primary2,
-           FirstPartySetEntry(primary2, SiteType::kPrimary, std::nullopt)},
-          {primary1, FirstPartySetEntry(primary2, SiteType::kAssociated, 0)},
+          {primary2, FirstPartySetEntry(primary2, SiteType::kPrimary)},
+          {primary1, FirstPartySetEntry(primary2, SiteType::kAssociated)},
       },
       /*aliases=*/{});
   FirstPartySetsContextConfig config = sets.ComputeConfig(SetsMutation(
       /*replacement_sets=*/{},
       /*addition_sets=*/
       {
-          {{primary0,
-            FirstPartySetEntry(primary0, SiteType::kPrimary, std::nullopt)},
+          {{primary0, FirstPartySetEntry(primary0, SiteType::kPrimary)},
            {associated_site0,
-            FirstPartySetEntry(primary0, SiteType::kAssociated, std::nullopt)}},
-          {{primary2,
-            FirstPartySetEntry(primary2, SiteType::kPrimary, std::nullopt)},
+            FirstPartySetEntry(primary0, SiteType::kAssociated)}},
+          {{primary2, FirstPartySetEntry(primary2, SiteType::kPrimary)},
            {associated_site2,
-            FirstPartySetEntry(primary2, SiteType::kAssociated, std::nullopt)}},
-          {{primary1,
-            FirstPartySetEntry(primary1, SiteType::kPrimary, std::nullopt)},
+            FirstPartySetEntry(primary2, SiteType::kAssociated)}},
+          {{primary1, FirstPartySetEntry(primary1, SiteType::kPrimary)},
            {associated_site1,
-            FirstPartySetEntry(primary1, SiteType::kAssociated, std::nullopt)}},
-          {{primary42,
-            FirstPartySetEntry(primary42, SiteType::kPrimary, std::nullopt)},
+            FirstPartySetEntry(primary1, SiteType::kAssociated)}},
+          {{primary42, FirstPartySetEntry(primary42, SiteType::kPrimary)},
            {associated_site42,
-            FirstPartySetEntry(primary42, SiteType::kAssociated,
-                               std::nullopt)}},
+            FirstPartySetEntry(primary42, SiteType::kAssociated)}},
       },
       /*aliases=*/{}));
   EXPECT_THAT(
@@ -1401,35 +1261,25 @@
           config),
       UnorderedElementsAre(
           Pair(associated_site0,
-               FirstPartySetEntry(primary0, SiteType::kAssociated,
-                                  std::nullopt)),
+               FirstPartySetEntry(primary0, SiteType::kAssociated)),
           Pair(associated_site1,
-               FirstPartySetEntry(primary2, SiteType::kAssociated,
-                                  std::nullopt)),
+               FirstPartySetEntry(primary2, SiteType::kAssociated)),
           Pair(associated_site2,
-               FirstPartySetEntry(primary2, SiteType::kAssociated,
-                                  std::nullopt)),
+               FirstPartySetEntry(primary2, SiteType::kAssociated)),
           Pair(associated_site42,
-               FirstPartySetEntry(primary42, SiteType::kAssociated,
-                                  std::nullopt)),
-          Pair(primary0,
-               FirstPartySetEntry(primary0, SiteType::kPrimary, std::nullopt)),
-          Pair(primary1, FirstPartySetEntry(primary2, SiteType::kAssociated,
-                                            std::nullopt)),
-          Pair(primary2,
-               FirstPartySetEntry(primary2, SiteType::kPrimary, std::nullopt)),
-          Pair(primary42, FirstPartySetEntry(primary42, SiteType::kPrimary,
-                                             std::nullopt))));
+               FirstPartySetEntry(primary42, SiteType::kAssociated)),
+          Pair(primary0, FirstPartySetEntry(primary0, SiteType::kPrimary)),
+          Pair(primary1, FirstPartySetEntry(primary2, SiteType::kAssociated)),
+          Pair(primary2, FirstPartySetEntry(primary2, SiteType::kPrimary)),
+          Pair(primary42, FirstPartySetEntry(primary42, SiteType::kPrimary))));
 }
 
 TEST_F(GlobalFirstPartySetsTest, InvalidPublicSetsVersion_ComputeConfig) {
   const GlobalFirstPartySets sets(
       base::Version(), /*entries=*/
       {
-          {kPrimary,
-           FirstPartySetEntry(kPrimary, SiteType::kPrimary, std::nullopt)},
-          {kAssociated1,
-           FirstPartySetEntry(kPrimary, SiteType::kAssociated, 0)},
+          {kPrimary, FirstPartySetEntry(kPrimary, SiteType::kPrimary)},
+          {kAssociated1, FirstPartySetEntry(kPrimary, SiteType::kAssociated)},
       },
       /*aliases=*/{});
   ASSERT_TRUE(sets.empty());
@@ -1438,11 +1288,9 @@
       /*replacement_sets=*/
       {
           {
-              {kPrimary2,
-               FirstPartySetEntry(kPrimary2, SiteType::kPrimary, std::nullopt)},
+              {kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary)},
               {kAssociated2,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
-                                  std::nullopt)},
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)},
           },
       },
       /*addition_sets=*/{}, /*aliases=*/{}));
@@ -1461,10 +1309,8 @@
           config),
       UnorderedElementsAre(
           Pair(kAssociated2,
-               FirstPartySetEntry(kPrimary2, SiteType::kAssociated,
-                                  std::nullopt)),
-          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary,
-                                             std::nullopt))));
+               FirstPartySetEntry(kPrimary2, SiteType::kAssociated)),
+          Pair(kPrimary2, FirstPartySetEntry(kPrimary2, SiteType::kPrimary))));
 }
 
 class GlobalFirstPartySetsWithConfigTest
@@ -1475,17 +1321,15 @@
             FirstPartySetsContextConfig::Create(
                 {
                     // New entry:
-                    {kPrimary3, net::FirstPartySetEntryOverride(
-                                    FirstPartySetEntry(kPrimary3,
-                                                       SiteType::kPrimary,
-                                                       std::nullopt))},
+                    {kPrimary3,
+                     net::FirstPartySetEntryOverride(
+                         FirstPartySetEntry(kPrimary3, SiteType::kPrimary))},
                     // Removed entry:
                     {kAssociated1, net::FirstPartySetEntryOverride()},
                     // Remapped entry:
-                    {kAssociated3, net::FirstPartySetEntryOverride(
-                                       FirstPartySetEntry(kPrimary3,
-                                                          SiteType::kAssociated,
-                                                          0))},
+                    {kAssociated3,
+                     net::FirstPartySetEntryOverride(
+                         FirstPartySetEntry(kPrimary3, SiteType::kAssociated))},
                     // Removed alias:
                     {kAssociated1Cctld, net::FirstPartySetEntryOverride()},
                 })
@@ -1499,17 +1343,16 @@
 
 TEST_F(GlobalFirstPartySetsWithConfigTest, ComputeMetadata) {
   // kAssociated1 has been removed from its set.
-  EXPECT_EQ(global_sets().ComputeMetadata(kAssociated1, &kPrimary, config()),
-            FirstPartySetMetadata(
-                std::nullopt, FirstPartySetEntry(kPrimary, SiteType::kPrimary,
-                                                 std::nullopt)));
+  EXPECT_EQ(
+      global_sets().ComputeMetadata(kAssociated1, &kPrimary, config()),
+      FirstPartySetMetadata(std::nullopt,
+                            FirstPartySetEntry(kPrimary, SiteType::kPrimary)));
 
   // kAssociated3 and kPrimary3 are sites in a new set.
-  EXPECT_EQ(
-      global_sets().ComputeMetadata(kAssociated3, &kPrimary3, config()),
-      FirstPartySetMetadata(
-          FirstPartySetEntry(kPrimary3, SiteType::kAssociated, 0),
-          FirstPartySetEntry(kPrimary3, SiteType::kPrimary, std::nullopt)));
+  EXPECT_EQ(global_sets().ComputeMetadata(kAssociated3, &kPrimary3, config()),
+            FirstPartySetMetadata(
+                FirstPartySetEntry(kPrimary3, SiteType::kAssociated),
+                FirstPartySetEntry(kPrimary3, SiteType::kPrimary)));
 }
 
 }  // namespace net
diff --git a/net/first_party_sets/local_set_declaration_unittest.cc b/net/first_party_sets/local_set_declaration_unittest.cc
index 1ba5292..3e51ae9 100644
--- a/net/first_party_sets/local_set_declaration_unittest.cc
+++ b/net/first_party_sets/local_set_declaration_unittest.cc
@@ -32,8 +32,8 @@
   SchemefulSite associated(GURL("https://associated.test"));
 
   base::flat_map<SchemefulSite, FirstPartySetEntry> entries({
-      {primary, FirstPartySetEntry(primary, SiteType::kPrimary, std::nullopt)},
-      {associated, FirstPartySetEntry(primary, SiteType::kAssociated, 0)},
+      {primary, FirstPartySetEntry(primary, SiteType::kPrimary)},
+      {associated, FirstPartySetEntry(primary, SiteType::kAssociated)},
   });
 
   LocalSetDeclaration local_set_declaration =
@@ -45,10 +45,9 @@
           /*replacement_sets=*/
           {
               {
-                  {primary, FirstPartySetEntry(primary, SiteType::kPrimary,
-                                               std::nullopt)},
+                  {primary, FirstPartySetEntry(primary, SiteType::kPrimary)},
                   {associated,
-                   FirstPartySetEntry(primary, SiteType::kAssociated, 0)},
+                   FirstPartySetEntry(primary, SiteType::kAssociated)},
               },
           },
           /*addition_sets=*/{}, /*aliases=*/{}));
@@ -62,8 +61,8 @@
   SchemefulSite associated_cctld(GURL("https://associated.cctld"));
 
   base::flat_map<SchemefulSite, FirstPartySetEntry> entries({
-      {primary, FirstPartySetEntry(primary, SiteType::kPrimary, std::nullopt)},
-      {associated, FirstPartySetEntry(primary, SiteType::kAssociated, 0)},
+      {primary, FirstPartySetEntry(primary, SiteType::kPrimary)},
+      {associated, FirstPartySetEntry(primary, SiteType::kAssociated)},
   });
 
   base::flat_map<SchemefulSite, SchemefulSite> aliases(
@@ -80,15 +79,13 @@
           /*replacement_sets=*/
           {
               {
-                  {primary, FirstPartySetEntry(primary, SiteType::kPrimary,
-                                               std::nullopt)},
+                  {primary, FirstPartySetEntry(primary, SiteType::kPrimary)},
                   {primary_cctld,
-                   FirstPartySetEntry(primary, SiteType::kPrimary,
-                                      std::nullopt)},
+                   FirstPartySetEntry(primary, SiteType::kPrimary)},
                   {associated,
-                   FirstPartySetEntry(primary, SiteType::kAssociated, 0)},
+                   FirstPartySetEntry(primary, SiteType::kAssociated)},
                   {associated_cctld,
-                   FirstPartySetEntry(primary, SiteType::kAssociated, 0)},
+                   FirstPartySetEntry(primary, SiteType::kAssociated)},
               },
           },
           /*addition_sets=*/{}, /*aliases=*/
@@ -112,19 +109,17 @@
   // All aliases must refer to a canonical site that has an entry in the set.
   EXPECT_FALSE(LocalSetDeclaration::Create(
       {
-          {primary,
-           FirstPartySetEntry(primary, SiteType::kPrimary, std::nullopt)},
-          {associated, FirstPartySetEntry(primary, SiteType::kAssociated, 0)},
+          {primary, FirstPartySetEntry(primary, SiteType::kPrimary)},
+          {associated, FirstPartySetEntry(primary, SiteType::kAssociated)},
       },
       {{associated2_cctld, associated2}}));
 
   // An alias must not have an explicit entry, even one that matches the
   // canonical's entry.
-  FirstPartySetEntry associated_entry(primary, SiteType::kAssociated, 0);
+  FirstPartySetEntry associated_entry(primary, SiteType::kAssociated);
   EXPECT_FALSE(LocalSetDeclaration::Create(
       {
-          {primary,
-           FirstPartySetEntry(primary, SiteType::kPrimary, std::nullopt)},
+          {primary, FirstPartySetEntry(primary, SiteType::kPrimary)},
           {associated, associated_entry},
           {associated_cctld, associated_entry},
       },
@@ -133,20 +128,17 @@
   // No singleton sets.
   EXPECT_FALSE(LocalSetDeclaration::Create(
       {
-          {primary,
-           FirstPartySetEntry(primary, SiteType::kPrimary, std::nullopt)},
+          {primary, FirstPartySetEntry(primary, SiteType::kPrimary)},
       },
       {}));
 
   // Multiple sets aren't supported.
   EXPECT_FALSE(LocalSetDeclaration::Create(
       {
-          {primary,
-           FirstPartySetEntry(primary, SiteType::kPrimary, std::nullopt)},
-          {primary2,
-           FirstPartySetEntry(primary2, SiteType::kPrimary, std::nullopt)},
-          {associated, FirstPartySetEntry(primary, SiteType::kAssociated, 0)},
-          {associated2, FirstPartySetEntry(primary2, SiteType::kAssociated, 0)},
+          {primary, FirstPartySetEntry(primary, SiteType::kPrimary)},
+          {primary2, FirstPartySetEntry(primary2, SiteType::kPrimary)},
+          {associated, FirstPartySetEntry(primary, SiteType::kAssociated)},
+          {associated2, FirstPartySetEntry(primary2, SiteType::kAssociated)},
       },
       {}));
 }
diff --git a/net/first_party_sets/sets_mutation_unittest.cc b/net/first_party_sets/sets_mutation_unittest.cc
index abcac89e..b84bd0d 100644
--- a/net/first_party_sets/sets_mutation_unittest.cc
+++ b/net/first_party_sets/sets_mutation_unittest.cc
@@ -31,22 +31,20 @@
       /*replacement_sets=*/
       {
           {
-              {primary1,
-               FirstPartySetEntry(primary1, SiteType::kPrimary, std::nullopt)},
+              {primary1, FirstPartySetEntry(primary1, SiteType::kPrimary)},
               {associated1,
-               FirstPartySetEntry(primary1, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(primary1, SiteType::kAssociated)},
               {primary1_cctld,
-               FirstPartySetEntry(primary1, SiteType::kPrimary, std::nullopt)},
+               FirstPartySetEntry(primary1, SiteType::kPrimary)},
               {associated1_cctld,
-               FirstPartySetEntry(primary1, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(primary1, SiteType::kAssociated)},
           },
           {
-              {primary2,
-               FirstPartySetEntry(primary2, SiteType::kPrimary, std::nullopt)},
+              {primary2, FirstPartySetEntry(primary2, SiteType::kPrimary)},
               {associated2,
-               FirstPartySetEntry(primary2, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(primary2, SiteType::kAssociated)},
               {associated2_cctld,
-               FirstPartySetEntry(primary2, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(primary2, SiteType::kAssociated)},
           },
       },
       /*addition_sets=*/{}, /*aliases=*/
@@ -61,22 +59,20 @@
       /*addition_sets=*/
       {
           {
-              {primary1,
-               FirstPartySetEntry(primary1, SiteType::kPrimary, std::nullopt)},
+              {primary1, FirstPartySetEntry(primary1, SiteType::kPrimary)},
               {associated1,
-               FirstPartySetEntry(primary1, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(primary1, SiteType::kAssociated)},
               {primary1_cctld,
-               FirstPartySetEntry(primary1, SiteType::kPrimary, std::nullopt)},
+               FirstPartySetEntry(primary1, SiteType::kPrimary)},
               {associated1_cctld,
-               FirstPartySetEntry(primary1, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(primary1, SiteType::kAssociated)},
           },
           {
-              {primary2,
-               FirstPartySetEntry(primary2, SiteType::kPrimary, std::nullopt)},
+              {primary2, FirstPartySetEntry(primary2, SiteType::kPrimary)},
               {associated2,
-               FirstPartySetEntry(primary2, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(primary2, SiteType::kAssociated)},
               {associated2_cctld,
-               FirstPartySetEntry(primary2, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(primary2, SiteType::kAssociated)},
           },
       },
       /*aliases=*/
@@ -90,25 +86,23 @@
       /*replacement_sets=*/
       {
           {
-              {primary1,
-               FirstPartySetEntry(primary1, SiteType::kPrimary, std::nullopt)},
+              {primary1, FirstPartySetEntry(primary1, SiteType::kPrimary)},
               {associated1,
-               FirstPartySetEntry(primary1, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(primary1, SiteType::kAssociated)},
               {primary1_cctld,
-               FirstPartySetEntry(primary1, SiteType::kPrimary, std::nullopt)},
+               FirstPartySetEntry(primary1, SiteType::kPrimary)},
               {associated1_cctld,
-               FirstPartySetEntry(primary1, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(primary1, SiteType::kAssociated)},
           },
       },
       /*addition_sets=*/
       {
           {
-              {primary2,
-               FirstPartySetEntry(primary2, SiteType::kPrimary, std::nullopt)},
+              {primary2, FirstPartySetEntry(primary2, SiteType::kPrimary)},
               {associated2,
-               FirstPartySetEntry(primary2, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(primary2, SiteType::kAssociated)},
               {associated2_cctld,
-               FirstPartySetEntry(primary2, SiteType::kAssociated, 0)},
+               FirstPartySetEntry(primary2, SiteType::kAssociated)},
           },
       },
       /*aliases=*/
@@ -132,18 +126,18 @@
             /*replacement_sets=*/
             {
                 {
-                    {primary1, FirstPartySetEntry(primary1, SiteType::kPrimary,
-                                                  std::nullopt)},
+                    {primary1,
+                     FirstPartySetEntry(primary1, SiteType::kPrimary)},
                     {associated1,
-                     FirstPartySetEntry(primary1, SiteType::kAssociated, 0)},
+                     FirstPartySetEntry(primary1, SiteType::kAssociated)},
                 },
                 {
-                    {primary2, FirstPartySetEntry(primary2, SiteType::kPrimary,
-                                                  std::nullopt)},
+                    {primary2,
+                     FirstPartySetEntry(primary2, SiteType::kPrimary)},
                     {associated1,
-                     FirstPartySetEntry(primary2, SiteType::kAssociated, 0)},
+                     FirstPartySetEntry(primary2, SiteType::kAssociated)},
                     {associated2,
-                     FirstPartySetEntry(primary2, SiteType::kAssociated, 0)},
+                     FirstPartySetEntry(primary2, SiteType::kAssociated)},
                 },
             },
             /*addition_sets=*/{}, /*aliases=*/{});
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index 7027689a..fda98b13 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -54,6 +54,7 @@
 #include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
 #include "net/http/http_util.h"
+#include "net/http/no_vary_search_cache_storage_file_operations.h"
 #include "net/log/net_log_with_source.h"
 #include "net/quic/quic_server_info.h"
 #include "url/origin.h"
@@ -396,20 +397,26 @@
 
 //-----------------------------------------------------------------------------
 
-HttpCache::HttpCache(std::unique_ptr<HttpTransactionFactory> network_layer,
-                     std::unique_ptr<BackendFactory> backend_factory)
+HttpCache::HttpCache(
+    std::unique_ptr<HttpTransactionFactory> network_layer,
+    std::unique_ptr<BackendFactory> backend_factory,
+    std::unique_ptr<NoVarySearchCacheStorageFileOperations> file_operations)
     : net_log_(nullptr),
       backend_factory_(std::move(backend_factory)),
 
       network_layer_(std::move(network_layer)),
       clock_(base::DefaultClock::GetInstance()),
       keys_marked_no_store_(
-          features::kAvoidEntryCreationForNoStoreCacheSize.Get()) {
+          features::kAvoidEntryCreationForNoStoreCacheSize.Get()),
+      file_operations_(std::move(file_operations)) {
   g_init_cache = true;
   if (base::FeatureList::IsEnabled(features::kHttpCacheNoVarySearch)) {
     size_t max_entries = features::kHttpCacheNoVarySearchCacheMaxEntries.Get();
     if (max_entries) {
-      no_vary_search_cache_.emplace(static_cast<size_t>(max_entries));
+      // TODO(https://crbug.com/382394774): Make
+      // kHttpCacheNoVarySearchCacheMaxEntries be a size_t param.
+      no_vary_search_cache_ =
+          std::make_unique<NoVarySearchCache>(static_cast<size_t>(max_entries));
     }
   }
   HttpNetworkSession* session = network_layer_->GetSession();
@@ -552,8 +559,8 @@
       filter_type, origins, domains, delete_begin, delete_end);
 
   if (cleared) {
-    // TODO(https://crbug.com/399562754): Re-write the on-disk store to erase
-    // the removed entries.
+    // This will safely do nothing if we are not using on-disk storage.
+    no_vary_search_cache_storage_.TakeSnapshot();
   }
 }
 
@@ -1438,6 +1445,18 @@
          keys_marked_no_store_.end();
 }
 
+void HttpCache::MaybeLoadNoVarySearchCacheFromDisk() {
+  if (file_operations_ && no_vary_search_cache_) {
+    // This use of base::Unretained() is safe because destroying this object
+    // destroys the `no_vary_search_cache_storage_` object after which the
+    // callback will not be called.
+    no_vary_search_cache_storage_.Load(
+        std::move(file_operations_), no_vary_search_cache_->max_size(),
+        base::BindOnce(&HttpCache::OnNoVarySearchCacheLoadComplete,
+                       base::Unretained(this)));
+  }
+}
+
 void HttpCache::OnProcessQueuedTransactions(scoped_refptr<ActiveEntry> entry) {
   entry->set_will_process_queued_transactions(false);
 
@@ -1639,6 +1658,7 @@
       disk_cache_ = std::move(pending_op->backend);
       UMA_HISTOGRAM_MEMORY_KB("HttpCache.MaxFileSizeOnInit",
                               disk_cache_->MaxFileSize() / 1024);
+      MaybeLoadNoVarySearchCacheFromDisk();
     }
   }
 
@@ -1666,4 +1686,18 @@
   }
 }
 
+void HttpCache::OnNoVarySearchCacheLoadComplete(
+    NoVarySearchCacheStorage::LoadResult result) {
+  if (!result.has_value()) {
+    // Failure. Nothing to do here.
+    return;
+  }
+  base::UmaHistogramCounts100(
+      "HttpCache.NoVarySearch.EntriesAddedDuringLoading",
+      no_vary_search_cache_->size());
+  auto provisional_no_vary_search_cache = std::move(no_vary_search_cache_);
+  no_vary_search_cache_ = std::move(result.value());
+  no_vary_search_cache_->MergeFrom(*provisional_no_vary_search_cache);
+}
+
 }  // namespace net
diff --git a/net/http/http_cache.h b/net/http/http_cache.h
index b94b3de8..465513a 100644
--- a/net/http/http_cache.h
+++ b/net/http/http_cache.h
@@ -46,6 +46,7 @@
 #include "net/disk_cache/disk_cache.h"
 #include "net/http/http_transaction_factory.h"
 #include "net/http/no_vary_search_cache.h"
+#include "net/http/no_vary_search_cache_storage.h"
 
 class GURL;
 
@@ -63,6 +64,7 @@
 class HttpResponseInfo;
 class NetLog;
 class NetworkIsolationKey;
+class NoVarySearchCacheStorageFileOperations;
 struct HttpRequestInfo;
 
 class NET_EXPORT HttpCache : public HttpTransactionFactory {
@@ -177,8 +179,10 @@
 
   // Initialize the cache from its component parts. |network_layer| and
   // |backend_factory| will be destroyed when the HttpCache is.
-  HttpCache(std::unique_ptr<HttpTransactionFactory> network_layer,
-            std::unique_ptr<BackendFactory> backend_factory);
+  HttpCache(
+      std::unique_ptr<HttpTransactionFactory> network_layer,
+      std::unique_ptr<BackendFactory> backend_factory,
+      std::unique_ptr<NoVarySearchCacheStorageFileOperations> file_operations);
 
   HttpCache(const HttpCache&) = delete;
   HttpCache& operator=(const HttpCache&) = delete;
@@ -231,8 +235,8 @@
 
   // Delete entries matching the criteria `filter_type`, `origins`, `domains`,
   // `delete_begin` and `delete_end` from the NoVarySearchCache and refresh the
-  // on-disk cache to reflect the removals if necessary. See
-  // no_vary_search_cache.h for the definition of the parameter.
+  // on-disk cache snapshot to reflect the removals if necessary. See
+  // no_vary_search_cache.h for the definition of the parameters.
   void ClearNoVarySearchCache(UrlFilterType filter_type,
                               const base::flat_set<url::Origin>& origins,
                               const base::flat_set<std::string>& domains,
@@ -722,6 +726,10 @@
   // to avoid attempting creating cache entries uselessly.
   bool DidKeyLeadToNoStoreResponse(const std::string& key);
 
+  // Calls NoVarySearchCacheStorage::Load() if `file_operations_` is set. Since
+  // this gives away `file_operations_`, it will only call it once.
+  void MaybeLoadNoVarySearchCacheFromDisk();
+
   // Events (called via PostTask) ---------------------------------------------
 
   void OnProcessQueuedTransactions(scoped_refptr<ActiveEntry> entry);
@@ -753,6 +761,10 @@
   // Processes the backend creation notification.
   void OnBackendCreated(int result, PendingOp* pending_op);
 
+  // Starts using the loaded cache if loading was successful.
+  void OnNoVarySearchCacheLoadComplete(
+      NoVarySearchCacheStorage::LoadResult result);
+
   // Constants ----------------------------------------------------------------
 
   // Used when generating and accessing keys if cache is split.
@@ -801,7 +813,18 @@
   // Set if the kHttpCacheNoVarySearch feature is enabled. Translates the URL in
   // the request into the URL of a previous response that is equivalent
   // according to the rules of the No-Vary-Search header in the response.
-  std::optional<NoVarySearchCache> no_vary_search_cache_;
+  std::unique_ptr<NoVarySearchCache> no_vary_search_cache_;
+
+  // Implements persistence for `no_vary_search_cache_`. Only used when the
+  // cache is stored on disk. Holds a raw_ptr to `no_vary_search_cache_` so must
+  // be destroyed before it.
+  NoVarySearchCacheStorage no_vary_search_cache_storage_;
+
+  // Implementation of file operations for No-Vary-Search cache persistence.
+  // Only non-null if the backend is on-disk, and only until a backend has been
+  // created. After that ownership is transferred to
+  // `no_vary_search_cache_storage_`.
+  std::unique_ptr<NoVarySearchCacheStorageFileOperations> file_operations_;
 
   THREAD_CHECKER(thread_checker_);
 
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index 594c8772..74cb85e 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -32,10 +32,12 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/bind.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_clock.h"
 #include "base/test/test_future.h"
+#include "base/test/test_waitable_event.h"
 #include "base/time/time.h"
 #include "base/trace_event/memory_allocator_dump.h"
 #include "base/trace_event/memory_dump_request_args.h"
@@ -72,6 +74,7 @@
 #include "net/http/http_transaction_test_util.h"
 #include "net/http/http_util.h"
 #include "net/http/mock_http_cache.h"
+#include "net/http/no_vary_search_cache_storage_mock_file_operations.h"
 #include "net/log/net_log_event_type.h"
 #include "net/log/net_log_source.h"
 #include "net/log/net_log_with_source.h"
@@ -90,17 +93,26 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/origin.h"
 
+using base::test::RunClosure;
 using net::test::IsError;
 using net::test::IsOk;
+using testing::_;
 using testing::AllOf;
 using testing::ByRef;
 using testing::Contains;
+using testing::DoAll;
 using testing::ElementsAre;
 using testing::Eq;
 using testing::Field;
 using testing::Gt;
+using testing::InSequence;
+using testing::Invoke;
 using testing::IsEmpty;
+using testing::MockFunction;
 using testing::NotNull;
+using testing::Return;
+using testing::StrictMock;
+using testing::Truly;
 
 using base::Time;
 
@@ -14112,13 +14124,14 @@
             HINT_HIGH_PRIORITY);
 }
 
-class HttpCacheNoVarySearchTest : public HttpCacheTest,
-                                  public ::testing::WithParamInterface<bool> {
+class HttpCacheNoVarySearchTestBase
+    : public HttpCacheTest,
+      public ::testing::WithParamInterface<bool> {
  protected:
   static constexpr int kMaxAgeOneDay = 24 * 60 * 60;  // seconds
   static constexpr std::string_view kBaseURL = "https://example.com/search?";
 
-  HttpCacheNoVarySearchTest() {
+  HttpCacheNoVarySearchTestBase() {
     using base::test::FeatureRef;
     std::vector<FeatureRef> enabled_features = {
         features::kHttpCacheNoVarySearch};
@@ -14130,11 +14143,30 @@
       disabled_features.push_back(split_cache_feature);
     }
     scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
-    http_cache_.emplace();
+  }
+
+  ~HttpCacheNoVarySearchTestBase() {
+    // Destroy the NoVarySearchCacheStorage object.
+    http_cache_.reset();
+
+    // Make sure the NoVarySearchCacheStorage::Journaller object that lives on
+    // the thread pool has been destroyed along with any mock objects it owns.
+    // Despite what the presubmit claims, this use of RunUntilIdle() is correct.
+    RunUntilIdle();
+  }
+
+  void SetUp() override { ConstructCache(http_cache_); }
+
+  // This can be overloaded by subclasses to construct the cache with different
+  // arguments.
+  virtual void ConstructCache(std::optional<MockHttpCache>& http_cache) {
+    http_cache.emplace();
   }
 
   HttpCache* cache() { return http_cache_->http_cache(); }
 
+  MockDiskCache* mock_disk_cache() { return http_cache_->disk_cache(); }
+
   // Callers can safely modify the return value, except for the `url` field.
   MockTransaction& CreateMockTransaction(std::string_view query,
                                          std::string_view no_vary_search,
@@ -14199,6 +14231,8 @@
   std::optional<MockHttpCache> http_cache_;
 };
 
+using HttpCacheNoVarySearchTest = HttpCacheNoVarySearchTestBase;
+
 TEST_P(HttpCacheNoVarySearchTest, SimpleSuccess) {
   FetchIntoCache("q=fred&a=1", "params=(\"a\")");
 
@@ -14350,4 +14384,273 @@
                            return info.param ? "NotSplitCache" : "SplitCache";
                          });
 
+// A GoogleMock action to quit a base::RunLoop. This is not defined using the
+// ACTION_P macro because to be thread-safe QuitClosure() needs to be called
+// when the action is created, not when it is executed.
+auto QuitRunLoop(base::RunLoop& run_loop) {
+  return RunClosure(run_loop.QuitClosure());
+}
+
+// Tests for NoVarySearchCacheStorage integration. These necessarily depend on
+// the implementation of NoVarySearchCacheStorage, and would need to be updated
+// if a different storage backend was used.
+class HttpCacheNoVarySearchMockFileOperationsTest
+    : public HttpCacheNoVarySearchTestBase {
+ public:
+  using StrictMockFileOperations = StrictMock<MockFileOperations>;
+  using StrictMockWriter = StrictMock<MockWriter>;
+  using Checkpoint = StrictMock<MockFunction<void()>>;
+
+  void ConstructCache(std::optional<MockHttpCache>& http_cache) override {
+    auto file_operations = std::make_unique<StrictMockFileOperations>();
+    file_operations_ = file_operations.get();
+    auto writer = std::make_unique<StrictMockWriter>();
+    writer_ = writer.get();
+
+    auto maybe_block = [&] {
+      if (delay_load_.load()) {
+        // This thread synchronously goes to sleep until signalled.
+        load_can_proceed_.Wait();
+      }
+    };
+
+    {
+      InSequence s;
+
+      load_expectations_ +=
+          EXPECT_CALL(*file_operations, Load)
+              .WillOnce(DoAll(
+                  Invoke(maybe_block),
+                  Return(base::unexpected(base::File::FILE_ERROR_NOT_FOUND))));
+      load_expectations_ += EXPECT_CALL(*file_operations, AtomicSave)
+                                .WillOnce(Return(base::ok()));
+      load_expectations_ += EXPECT_CALL(*file_operations, CreateWriter)
+                                .WillOnce(Return(std::move(writer)));
+      load_expectations_ +=
+          EXPECT_CALL(*writer_, Write)
+              .WillOnce(DoAll(QuitRunLoop(load_run_loop_), Return(true)));
+    }
+    http_cache.emplace(std::make_unique<MockBackendFactory>(),
+                       std::move(file_operations));
+  }
+
+  void InitializeBackend() {
+    initialized_backend_ = true;
+    // It's not safe to set new expectations after this point.
+    file_operations_ = nullptr;
+    writer_ = nullptr;
+
+    TestGetBackendCompletionCallback cb;
+    HttpCache::GetBackendResult result = cache()->GetBackend(cb.callback());
+    EXPECT_THAT(cb.GetResult(result).first, IsOk());
+  }
+
+  void WaitForLoad() { load_run_loop_.Run(); }
+
+  // Returns a GoogleMock matcher which tests if `substring` appears within a
+  // uint8_t span. For example SpanHasSubstring("foo") will match against a span
+  // that contains the bytes 'f' 'o' 'o' contiguously in that order.
+  static auto SpanHasSubstring(const std::string& substring) {
+    return Truly([substring](base::span<const uint8_t> span) {
+      return !std::ranges::search(span, substring).empty();
+    });
+  }
+
+  void ResumeLoad() {
+    CHECK(delay_load_.load());
+    load_can_proceed_.Signal();
+  }
+
+  // Set whether loading the snapshot should block the background thread.
+  void set_delay_load(bool delay_load) { delay_load_.store(delay_load); }
+
+  StrictMockFileOperations& operations() {
+    CHECK(!initialized_backend_)
+        << "Set expectations before initializing backend";
+    return *file_operations_;
+  }
+
+  StrictMockWriter& writer() {
+    CHECK(!initialized_backend_)
+        << "Set expectations before initializing backend";
+    return *writer_;
+  }
+
+  const testing::ExpectationSet& load_expectations() const {
+    CHECK(!initialized_backend_)
+        << "Set expectations before initializing backend";
+    return load_expectations_;
+  }
+
+ private:
+  base::RunLoop load_run_loop_;
+  raw_ptr<StrictMockFileOperations> file_operations_ = nullptr;
+
+  // This pointer will dangle if a new snapshot is created
+  raw_ptr<StrictMockWriter> writer_ = nullptr;
+
+  // ExpectationSet represents a set of expectation. See
+  // https://google.github.io/googletest/reference/mocking.html#ExpectationSet
+  // for documentation.
+  testing::ExpectationSet load_expectations_;
+
+  // This is used for blocking load when `delay_load_` is true.
+  base::TestWaitableEvent load_can_proceed_;
+
+  bool initialized_backend_ = false;
+  std::atomic<bool> delay_load_ = false;
+};
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         HttpCacheNoVarySearchMockFileOperationsTest,
+                         ::testing::Bool(),
+                         [](const auto& info) {
+                           return info.param ? "NotSplitCache" : "SplitCache";
+                         });
+
+TEST_P(HttpCacheNoVarySearchMockFileOperationsTest, CacheStorageIsCreated) {
+  InitializeBackend();
+
+  WaitForLoad();
+}
+
+TEST_P(HttpCacheNoVarySearchMockFileOperationsTest, InsertsAreJournalled) {
+  base::RunLoop run_loop;
+  EXPECT_CALL(writer(), Write(SpanHasSubstring("q=fred&a=1")))
+      .After(load_expectations())
+      .WillOnce(DoAll(QuitRunLoop(run_loop), Return(true)));
+
+  InitializeBackend();
+
+  WaitForLoad();
+
+  FetchIntoCache("q=fred&a=1", "params=(\"a\")");
+  run_loop.Run();
+}
+
+TEST_P(HttpCacheNoVarySearchMockFileOperationsTest, EraseIsJournalled) {
+  base::RunLoop insert_run_loop;
+  base::RunLoop erase_run_loop;
+  // The same matcher works for the insert and erase operations, because they
+  // both have to contain the original query. This line verifies that two
+  // matching writes have been performed, but to avoid making the test too
+  // sensitive to the format of the journal, it does not verify that the journal
+  // entry types match.
+  EXPECT_CALL(writer(), Write(SpanHasSubstring("q=fred&a=1")))
+      .After(load_expectations())
+      .WillOnce(DoAll(QuitRunLoop(insert_run_loop), Return(true)))
+      .WillOnce(DoAll(QuitRunLoop(erase_run_loop), Return(true)));
+
+  InitializeBackend();
+
+  WaitForLoad();
+
+  FetchIntoCache("q=fred&a=1", "params=(\"a\")");
+
+  // Ensure the insert is really complete. Without this, the call to
+  // set_fail_requests() sometimes causes the previous transaction to fail.
+  insert_run_loop.Run();
+
+  // Cause the next request to fail. This will then cause the entry to be
+  // erased from the NoVarySearchCache.
+  mock_disk_cache()->set_fail_requests(true);
+
+  MockTransaction& from_cache =
+      CreateMockTransaction("q=fred&a=2", "params=(\"a\")");
+  RunTransactionTest(cache(), from_cache);
+  erase_run_loop.Run();
+}
+
+TEST_P(HttpCacheNoVarySearchMockFileOperationsTest,
+       ClearNoVarySearchCacheRewritesSnapshot) {
+  base::RunLoop journal_run_loop;
+  base::RunLoop snapshot_run_loop;
+  {
+    InSequence s;
+
+    // Verifies that the initial FetchIntoCache() transaction is journalled and
+    // permits the main thread to continue.
+    EXPECT_CALL(writer(), Write(SpanHasSubstring("q=fred&a=1")))
+        .After(load_expectations())
+        .WillOnce(DoAll(QuitRunLoop(journal_run_loop), Return(true)));
+
+    // Verifies that the snapshot is written and that it does not contain the
+    // entry that the call to ClearNoVarySearchCache() should have removed.
+    EXPECT_CALL(operations(),
+                AtomicSave(_, Not(Contains(SpanHasSubstring("q=fred&a=1")))))
+        .WillOnce(Return(base::ok()));
+
+    auto writer = std::make_unique<StrictMockWriter>();
+    auto& writer_ref = *writer;
+
+    // Verifies that a new journal file is created.
+    EXPECT_CALL(operations(), CreateWriter).WillOnce(Return(std::move(writer)));
+
+    // Verifies that the magic number (and nothing else) is written to the
+    // journal file, and permits the test to complete.
+    EXPECT_CALL(writer_ref, Write)
+        .WillOnce(DoAll(QuitRunLoop(snapshot_run_loop), Return(true)));
+  }
+
+  InitializeBackend();
+
+  WaitForLoad();
+
+  FetchIntoCache("q=fred&a=1", "params=(\"a\")");
+  journal_run_loop.Run();
+
+  cache()->ClearNoVarySearchCache(UrlFilterType::kFalseIfMatches, {}, {},
+                                  base::Time(), base::Time::Max());
+
+  snapshot_run_loop.Run();
+}
+
+TEST_P(HttpCacheNoVarySearchMockFileOperationsTest,
+       InsertDuringLoadIsJournalled) {
+  // To make the test robust, ensure that background file operations don't
+  // complete until we have finished our first request.
+  set_delay_load(true);
+
+  base::RunLoop run_loop;
+  Checkpoint checkpoint;
+  {
+    InSequence s;
+
+    // Verifies that the journal write does not happen before the call to
+    // ResumeLoad().
+    EXPECT_CALL(checkpoint, Call());
+
+    // Verifies that the transaction that happened before the load completed is
+    // journalled, and permits the test to continue.
+    EXPECT_CALL(writer(), Write(SpanHasSubstring("q=fred&a=1")))
+        .After(load_expectations())
+        .WillOnce(DoAll(QuitRunLoop(run_loop), Return(true)));
+  }
+
+  InitializeBackend();
+
+  FetchIntoCache("q=fred&a=1", "params=(\"a\")");
+
+  checkpoint.Call();
+
+  // Let background file operations resume.
+  ResumeLoad();
+
+  // Wait for the journal to be written.
+  run_loop.Run();
+
+  // The entry should still be there.
+  MockTransaction& from_cache =
+      CreateMockTransaction("q=fred&a=2", "params=(\"a\")");
+  MockHttpRequest cache_request(from_cache);
+
+  HttpResponseInfo info;
+
+  RunTransactionTestWithRequest(cache(), from_cache, cache_request, &info);
+  EXPECT_TRUE(info.was_cached);
+  EXPECT_FALSE(info.network_accessed);
+  EXPECT_EQ(info.cache_entry_status, HttpResponseInfo::ENTRY_USED);
+  EXPECT_EQ(info.headers->response_code(), 200);
+}
+
 }  // namespace net
diff --git a/net/http/http_cache_writers_unittest.cc b/net/http/http_cache_writers_unittest.cc
index 9ba736e5..a7c4cad 100644
--- a/net/http/http_cache_writers_unittest.cc
+++ b/net/http/http_cache_writers_unittest.cc
@@ -20,6 +20,7 @@
 #include "net/http/http_transaction.h"
 #include "net/http/http_transaction_test_util.h"
 #include "net/http/mock_http_cache.h"
+#include "net/http/no_vary_search_cache_storage_file_operations.h"
 #include "net/http/partial_data.h"
 #include "net/test/gtest_util.h"
 #include "net/test/test_with_task_environment.h"
@@ -56,7 +57,9 @@
  public:
   TestHttpCache(std::unique_ptr<HttpTransactionFactory> network_layer,
                 std::unique_ptr<BackendFactory> backend_factory)
-      : HttpCache(std::move(network_layer), std::move(backend_factory)) {}
+      : HttpCache(std::move(network_layer),
+                  std::move(backend_factory),
+                  /*file_operations=*/nullptr) {}
 
   void WritersDoneWritingToEntry(scoped_refptr<ActiveEntry> entry,
                                  bool success,
diff --git a/net/http/http_stream_factory_job_controller_unittest.cc b/net/http/http_stream_factory_job_controller_unittest.cc
index 9e47159a..53db343 100644
--- a/net/http/http_stream_factory_job_controller_unittest.cc
+++ b/net/http/http_stream_factory_job_controller_unittest.cc
@@ -6955,7 +6955,8 @@
 };
 
 TEST_F(HttpStreamFactoryJobControllerPoolTest, Preconnect) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
   endpoint_request
       ->add_endpoint(ServiceEndpointBuilder().add_v4("127.0.0.1").endpoint())
       .CompleteStartSynchronously(OK);
diff --git a/net/http/http_stream_pool_attempt_manager.cc b/net/http/http_stream_pool_attempt_manager.cc
index aa05b77..c7fdd5d 100644
--- a/net/http/http_stream_pool_attempt_manager.cc
+++ b/net/http/http_stream_pool_attempt_manager.cc
@@ -848,6 +848,12 @@
   return MultiplexedSessionCreationInitiator::kUnknown;
 }
 
+void HttpStreamPool::AttemptManager::SetOnCompleteCallbackForTesting(
+    base::OnceClosure callback) {
+  CHECK(on_complete_callback_for_testing_.is_null());
+  on_complete_callback_for_testing_ = std::move(callback);
+}
+
 void HttpStreamPool::AttemptManager::StartInternal(Job* job) {
   RestrictAllowedProtocols(job->allowed_alpns());
   UpdateTcpBasedAttemptState();
@@ -904,6 +910,9 @@
 }
 
 void HttpStreamPool::AttemptManager::ProcessServiceEndpointChanges() {
+  CHECK(!is_failing_);
+  CHECK(service_endpoint_request_);
+
   // The order of the following checks is important, see the following comments.
   // TODO(crbug.com/383606724): Figure out a better design and algorithms to
   // handle attempts and existing sessions.
@@ -1414,6 +1423,8 @@
   CHECK(!final_error_to_notify_jobs_.has_value());
   final_error_to_notify_jobs_ = error;
   is_failing_ = true;
+  service_endpoint_request_.reset();
+
   net_log_.AddEvent(
       NetLogEventType::HTTP_STREAM_POOL_ATTEMPT_MANAGER_NOTIFY_FAILURE, [&] {
         base::Value::Dict dict = GetStatesAsNetLogParams();
@@ -2060,6 +2071,11 @@
   CHECK(ip_based_pooling_disabling_jobs_.empty());
   CHECK(alternative_service_disabling_jobs_.empty());
 
+  if (on_complete_callback_for_testing_) {
+    base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE, std::move(on_complete_callback_for_testing_));
+  }
+
   group_->OnAttemptManagerComplete();
   // `this` is deleted.
 }
diff --git a/net/http/http_stream_pool_attempt_manager.h b/net/http/http_stream_pool_attempt_manager.h
index 4347bf18..30b19941 100644
--- a/net/http/http_stream_pool_attempt_manager.h
+++ b/net/http/http_stream_pool_attempt_manager.h
@@ -200,6 +200,8 @@
     return ip_endpoint_states_;
   }
 
+  void SetOnCompleteCallbackForTesting(base::OnceClosure callback);
+
  private:
   FRIEND_TEST_ALL_PREFIXES(HttpStreamPoolAttemptManagerTest,
                            GetIPEndPointToAttempt);
@@ -613,6 +615,8 @@
   bool should_block_tcp_based_attempt_ = false;
   base::OneShotTimer tcp_based_attempt_delay_timer_;
 
+  base::OnceClosure on_complete_callback_for_testing_;
+
   base::WeakPtrFactory<AttemptManager> weak_ptr_factory_{this};
 };
 
diff --git a/net/http/http_stream_pool_attempt_manager_unittest.cc b/net/http/http_stream_pool_attempt_manager_unittest.cc
index f276dc2..4ac4add 100644
--- a/net/http/http_stream_pool_attempt_manager_unittest.cc
+++ b/net/http/http_stream_pool_attempt_manager_unittest.cc
@@ -619,7 +619,8 @@
 };
 
 TEST_F(HttpStreamPoolAttemptManagerTest, ResolveEndpointFailedSync) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
   endpoint_request->CompleteStartSynchronously(ERR_FAILED);
   StreamRequester requester;
   requester.RequestStream(pool());
@@ -633,7 +634,8 @@
 
 TEST_F(HttpStreamPoolAttemptManagerTest,
        ResolveEndpointFailedMultipleRequests) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester1;
   requester1.RequestStream(pool());
@@ -649,7 +651,8 @@
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, LoadState) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester;
   HttpStreamRequest* request = requester.RequestStream(pool());
@@ -665,7 +668,8 @@
 TEST_F(HttpStreamPoolAttemptManagerTest, ResolveErrorInfo) {
   ResolveErrorInfo resolve_error_info(ERR_NAME_NOT_RESOLVED);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
   endpoint_request->set_resolve_error_info(resolve_error_info);
 
   StreamRequester requester;
@@ -681,7 +685,8 @@
 
 TEST_F(HttpStreamPoolAttemptManagerTest, DnsAliases) {
   const std::set<std::string> kAliases = {"alias1", "alias2"};
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
   endpoint_request
       ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
       .set_aliases(kAliases)
@@ -704,7 +709,8 @@
   constexpr base::TimeDelta kTcpDelay = base::Milliseconds(20);
   constexpr base::TimeDelta kTlsDelay = base::Milliseconds(90);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester;
   requester.set_destination("https://a.test").RequestStream(pool());
@@ -770,7 +776,8 @@
        ConnectTimingDnsResolutionNotFinished) {
   constexpr base::TimeDelta kDnsUpdateDelay = base::Milliseconds(30);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester;
   requester.set_destination("http://a.test").RequestStream(pool());
@@ -804,7 +811,8 @@
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, PlainHttpWaitForHttpsRecord) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester;
   requester.set_destination("http://a.test").RequestStream(pool());
@@ -825,7 +833,8 @@
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, SetPriority) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester1;
   HttpStreamRequest* request1 =
@@ -833,7 +842,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(requester1.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   ASSERT_EQ(endpoint_request->priority(), RequestPriority::LOW);
   ASSERT_EQ(manager->GetPriority(), RequestPriority::LOW);
 
@@ -843,7 +852,7 @@
       requester2.set_priority(RequestPriority::IDLE).RequestStream(pool());
   ASSERT_EQ(manager, pool()
                          .GetOrCreateGroupForTesting(requester2.GetStreamKey())
-                         .GetAttemptManagerForTesting());
+                         .attempt_manager());
   ASSERT_EQ(endpoint_request->priority(), RequestPriority::LOW);
   ASSERT_EQ(manager->GetPriority(), RequestPriority::LOW);
 
@@ -903,7 +912,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(requester.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   ASSERT_EQ(manager->GetPriority(), RequestPriority::HIGHEST);
 
   // Complete the stream attempt and wait for request completion. The
@@ -915,7 +924,8 @@
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, TcpFailSync) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester;
   requester.RequestStream(pool());
@@ -934,7 +944,8 @@
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, TcpFailAsync) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester;
   requester.RequestStream(pool());
@@ -953,7 +964,8 @@
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, TlsOkAsync) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   auto data = std::make_unique<SequencedSocketData>();
   socket_factory()->AddSocketDataProvider(data.get());
@@ -990,7 +1002,8 @@
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, TlsCryptoReadyDelayed) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   auto data = std::make_unique<SequencedSocketData>();
   socket_factory()->AddSocketDataProvider(data.get());
@@ -1018,7 +1031,8 @@
   constexpr size_t kMaxPerGroup = 1;
   pool().set_max_stream_sockets_per_group_for_testing(kMaxPerGroup);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   const scoped_refptr<X509Certificate> kCert =
       ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem");
@@ -1063,7 +1077,8 @@
   constexpr size_t kMaxPerGroup = 1;
   pool().set_max_stream_sockets_per_group_for_testing(kMaxPerGroup);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   const url::SchemeHostPort kDestination(GURL("https://a.test"));
 
@@ -1099,7 +1114,8 @@
 // following attempt failures are ignored and the existing requests get the
 // same fatal error.
 TEST_F(HttpStreamPoolAttemptManagerTest, TcpFailAfterNeedsClientAuth) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   const url::SchemeHostPort kDestination(GURL("https://a.test"));
 
@@ -1132,7 +1148,8 @@
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, RequestCanceledBeforeAttemptSuccess) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester;
   requester.RequestStream(pool());
@@ -1155,7 +1172,8 @@
 // Tests that canceling a limit ignoring request doesn't result in hitting a
 // CHECK. Ensures that a group is destroyed after the attempt failed.
 TEST_F(HttpStreamPoolAttemptManagerTest, LimitIgnoringRequestCanceled) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester;
   requester.set_load_flags(LOAD_IGNORE_LIMITS).RequestStream(pool());
@@ -1249,7 +1267,8 @@
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, OneIPEndPointFailed) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester;
   requester.RequestStream(pool());
@@ -1271,7 +1290,8 @@
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, IPEndPointTimedout) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester;
   requester.RequestStream(pool());
@@ -1379,9 +1399,8 @@
     requester.RequestStream(pool());
     ASSERT_FALSE(requester.result().has_value());
 
-    AttemptManager* manager = pool()
-                                  .GetOrCreateGroupForTesting(stream_key)
-                                  .GetAttemptManagerForTesting();
+    AttemptManager* manager =
+        pool().GetOrCreateGroupForTesting(stream_key).attempt_manager();
     // TODO(crbug.com/383606724): Don't modify internal representations. This
     // makes the test fragile and overly depend on internal representation. Best
     // practice is to test the public-facing API, when possible.
@@ -1401,7 +1420,8 @@
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, IPEndPointsSlow) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester;
   HttpStreamRequest* request = requester.RequestStream(pool());
@@ -1428,7 +1448,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(requester.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   ASSERT_EQ(manager->TcpBasedAttemptCount(), 1u);
   ASSERT_FALSE(request->completed());
 
@@ -1467,7 +1487,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(requester1.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   auto get_ip_endpoint_state = [&]() -> std::optional<IPEndPointState> {
     auto it = manager->ip_endpoint_states_for_testing().find(ip_endpoint);
     if (it == manager->ip_endpoint_states_for_testing().end()) {
@@ -1628,7 +1648,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(requester1.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   auto get_ip_endpoint_state =
       [&](const IPEndPoint& ip_endpoint) -> std::optional<IPEndPointState> {
     auto it = manager->ip_endpoint_states_for_testing().find(ip_endpoint);
@@ -1727,7 +1747,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(requester1.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   auto get_ip_endpoint_state =
       [&](const IPEndPoint& ip_endpoint) -> std::optional<IPEndPointState> {
     auto it = manager->ip_endpoint_states_for_testing().find(ip_endpoint);
@@ -1779,7 +1799,8 @@
 
 TEST_F(HttpStreamPoolAttemptManagerTest,
        PauseSlowTimerAfterTcpHandshakeForTls) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester;
   requester.set_destination("https://a.test").RequestStream(pool());
@@ -1810,7 +1831,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(requester.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   ASSERT_EQ(manager->TcpBasedAttemptCount(), 1u);
   ASSERT_FALSE(requester.result().has_value());
 
@@ -1949,7 +1970,8 @@
   constexpr size_t kMaxPerGroup = 4;
   pool().set_max_stream_sockets_per_group_for_testing(kMaxPerGroup);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   // Create streams up to the per-group limit for a destination.
   std::vector<std::unique_ptr<StreamRequester>> requesters;
@@ -1972,7 +1994,7 @@
 
   Group& group =
       pool().GetOrCreateGroupForTesting(requesters[0]->GetStreamKey());
-  AttemptManager* manager = group.GetAttemptManagerForTesting();
+  AttemptManager* manager = group.attempt_manager();
   ASSERT_EQ(pool().TotalActiveStreamCount(), kMaxPerGroup);
   ASSERT_EQ(group.ActiveStreamSocketCount(), kMaxPerGroup);
   ASSERT_EQ(manager->TcpBasedAttemptCount(), kMaxPerGroup);
@@ -2068,7 +2090,8 @@
   ASSERT_EQ(pool().TotalActiveStreamCount(), kMaxPerGroup);
   ASSERT_EQ(group_a.ActiveStreamSocketCount(), kMaxPerGroup);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   // Create a HttpStream in group B. It should not be blocked because both
   // per-group and per-pool limits are not reached yet.
@@ -2104,7 +2127,7 @@
             LOAD_STATE_WAITING_FOR_STALLED_SOCKET_POOL);
 
   RunUntilIdle();
-  AttemptManager* manager_b = group_b.GetAttemptManagerForTesting();
+  AttemptManager* manager_b = group_b.attempt_manager();
   ASSERT_FALSE(request2->completed());
   ASSERT_TRUE(pool().ReachedMaxStreamLimit());
   ASSERT_TRUE(pool().IsPoolStalled());
@@ -2143,11 +2166,12 @@
       {"d.test", "192.0.2.4", RequestPriority::HIGHEST},
   };
 
-  std::vector<FakeServiceEndpointRequest*> endpoint_requests;
+  std::vector<base::WeakPtr<FakeServiceEndpointRequest>> endpoint_requests;
   std::vector<std::unique_ptr<StreamRequester>> requesters;
   std::vector<std::unique_ptr<SequencedSocketData>> socket_datas;
   for (const auto& [host, ip_address, priority] : items) {
-    FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+    base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+        resolver()->AddFakeRequest();
     endpoint_request->add_endpoint(
         ServiceEndpointBuilder().add_v4(ip_address).endpoint());
     endpoint_requests.emplace_back(endpoint_request);
@@ -2401,8 +2425,7 @@
   }
 
   Group& group = pool().GetOrCreateGroupForTesting(stream_key);
-  ASSERT_GT(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(),
-            kMaxPerGroup);
+  ASSERT_GT(group.attempt_manager()->TcpBasedAttemptCount(), kMaxPerGroup);
 
   // Complete requests that ignore limits.
   while (!limit_ignoring_requesters.empty()) {
@@ -2523,7 +2546,7 @@
   resolver()->AddFakeRequest();
   HttpStreamRequest* request = requester.RequestStream(pool());
   RunUntilIdle();
-  AttemptManager* manager = group.GetAttemptManagerForTesting();
+  AttemptManager* manager = group.attempt_manager();
   ASSERT_FALSE(request->completed());
   ASSERT_EQ(manager->PendingJobCount(), 1u);
 
@@ -2573,7 +2596,8 @@
 
   // Request a stream in group B. The request should close an idle stream in
   // group A.
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
   StreamRequester requester;
   HttpStreamRequest* request = requester.RequestStream(pool());
   auto data = std::make_unique<SequencedSocketData>();
@@ -2591,7 +2615,8 @@
 
 TEST_F(HttpStreamPoolAttemptManagerTest,
        ProcessPendingRequestDnsResolutionOngoing) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   auto data = std::make_unique<SequencedSocketData>();
   socket_factory()->AddSocketDataProvider(data.get());
@@ -2614,8 +2639,10 @@
 // when an IP address change event happens.
 TEST_F(HttpStreamPoolAttemptManagerTest,
        CancelAttemptAndRequestsOnIPAddressChange) {
-  FakeServiceEndpointRequest* endpoint_request1 = resolver()->AddFakeRequest();
-  FakeServiceEndpointRequest* endpoint_request2 = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request1 =
+      resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request2 =
+      resolver()->AddFakeRequest();
 
   auto data1 = std::make_unique<SequencedSocketData>();
   data1->set_connect_data(MockConnect(ASYNC, ERR_IO_PENDING));
@@ -2641,11 +2668,11 @@
   AttemptManager* manager1 =
       pool()
           .GetOrCreateGroupForTesting(requester1.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   AttemptManager* manager2 =
       pool()
           .GetOrCreateGroupForTesting(requester2.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   ASSERT_EQ(manager1->JobCount(), 1u);
   ASSERT_EQ(manager1->TcpBasedAttemptCount(), 1u);
   ASSERT_EQ(manager2->JobCount(), 1u);
@@ -2668,7 +2695,8 @@
   constexpr size_t kMaxPerGroup = 1;
   pool().set_max_stream_sockets_per_group_for_testing(kMaxPerGroup);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   const url::SchemeHostPort kDestination(GURL("https://a.test"));
 
@@ -2762,7 +2790,7 @@
     ASSERT_FALSE(raw_requester->result().has_value());
   }
   AttemptManager* manager =
-      pool().GetGroupForTesting(stream_key)->GetAttemptManagerForTesting();
+      pool().GetGroupForTesting(stream_key)->attempt_manager();
   ASSERT_EQ(manager->JobCount(), 2u);
   ASSERT_EQ(manager->NotifiedJobCount(), 0u);
   ASSERT_EQ(manager->TcpBasedAttemptCount(), 2u);
@@ -2785,8 +2813,7 @@
   // Ensure that the job and attempt manager are still alive since there are
   // in-flight attempts.
   requesters.clear();
-  manager =
-      pool().GetGroupForTesting(stream_key)->GetAttemptManagerForTesting();
+  manager = pool().GetGroupForTesting(stream_key)->attempt_manager();
   ASSERT_TRUE(manager);
   ASSERT_EQ(manager->JobCount(), 0u);
   ASSERT_EQ(manager->NotifiedJobCount(), 0u);
@@ -2863,7 +2890,8 @@
   const HttpStreamKey stream_key =
       StreamKeyBuilder().set_destination("https://a.test").Build();
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   std::vector<std::unique_ptr<SequencedSocketData>> socket_datas;
   std::vector<std::unique_ptr<SSLSocketDataProvider>> ssls;
@@ -2896,7 +2924,7 @@
   }
   Group& group =
       pool().GetOrCreateGroupForTesting(requesters[0]->GetStreamKey());
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(), 0u);
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(), 0u);
   ASSERT_EQ(group.IdleStreamSocketCount(), 0u);
   ASSERT_EQ(group.ActiveStreamSocketCount(), 1u);
   ASSERT_EQ(pool().TotalConnectingStreamCount(), 0u);
@@ -2905,7 +2933,8 @@
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, SpdyCreateSessionFail) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   const MockWrite writes[] = {MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 0)};
   const MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 1)};
@@ -3161,7 +3190,8 @@
   ASSERT_FALSE(pool().IsPoolStalled());
 
   // Request a stream in group C. It should be blocked.
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   const MockWrite writes[] = {MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 1)};
   const MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
@@ -3180,7 +3210,7 @@
   RunUntilIdle();
   Group& group_c =
       pool().GetOrCreateGroupForTesting(requester_c.GetStreamKey());
-  ASSERT_EQ(group_c.GetAttemptManagerForTesting()->PendingJobCount(), 1u);
+  ASSERT_EQ(group_c.attempt_manager()->PendingJobCount(), 1u);
   ASSERT_TRUE(pool().ReachedMaxStreamLimit());
   ASSERT_TRUE(pool().IsPoolStalled());
 
@@ -3214,7 +3244,8 @@
   RunUntilIdle();
   EXPECT_THAT(requester_a.result(), Optional(IsOk()));
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester_b;
   requester_b.set_destination("https://example.test").RequestStream(pool());
@@ -3250,7 +3281,8 @@
   ssl->next_proto = NextProto::kProtoHTTP2;
   socket_factory()->AddSSLSocketDataProvider(ssl.get());
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   // Create the second request to example.test. It will finds the matching
   // SPDY session, but the task to use the session runs asynchronously, so it
@@ -3290,7 +3322,8 @@
   RunUntilIdle();
   EXPECT_THAT(requester_a.result(), Optional(IsOk()));
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   Preconnector preconnector_b("https://example.test");
   preconnector_b.Preconnect(pool());
@@ -3316,7 +3349,8 @@
   requester_a.WaitForResult();
   EXPECT_THAT(requester_a.result(), Optional(IsOk()));
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester_b;
   requester_b.set_destination("https://example.test").RequestStream(pool());
@@ -3347,7 +3381,8 @@
   RunUntilIdle();
   EXPECT_THAT(requester_a.result(), Optional(IsOk()));
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
   endpoint_request
       ->add_endpoint(
           ServiceEndpointBuilder().add_ip_endpoint(kCommonEndPoint).endpoint())
@@ -3373,7 +3408,8 @@
   RunUntilIdle();
   EXPECT_THAT(requester_a.result(), Optional(IsOk()));
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   const MockWrite writes[] = {MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 1)};
   const MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
@@ -3460,7 +3496,8 @@
   RunUntilIdle();
   EXPECT_THAT(requester_a.result(), Optional(IsOk()));
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   const MockWrite writes[] = {MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 1)};
   const MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
@@ -3494,7 +3531,8 @@
   RunUntilIdle();
   EXPECT_THAT(requester_a.result(), Optional(IsOk()));
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   const MockWrite writes[] = {MockWrite(SYNCHRONOUS, ERR_IO_PENDING, 1)};
   const MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
@@ -3531,7 +3569,8 @@
 
   const IPEndPoint kCommonEndPoint = MakeIPEndPoint("2001:db8::1", 443);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
   endpoint_request
       ->add_endpoint(
           ServiceEndpointBuilder().add_ip_endpoint(kCommonEndPoint).endpoint())
@@ -3581,7 +3620,8 @@
       stream_key.destination(), stream_key.network_anonymization_key(),
       /*supports_spdy=*/true);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester1;
   requester1.set_destination(kDestination).RequestStream(pool());
@@ -3602,7 +3642,7 @@
       .CallOnServiceEndpointRequestFinished(OK);
   // There should be only one in-flight attempt because attempts are throttled.
   Group& group = pool().GetOrCreateGroupForTesting(requester1.GetStreamKey());
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(), 1u);
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(), 1u);
 
   // This should not enter an infinite loop.
   pool().ProcessPendingRequestsInGroups();
@@ -3623,7 +3663,8 @@
       stream_key.destination(), stream_key.network_anonymization_key(),
       /*supports_spdy=*/true);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester1;
   requester1.set_destination(kDestination).RequestStream(pool());
@@ -3654,14 +3695,14 @@
       .CallOnServiceEndpointRequestFinished(OK);
   // There should be only one in-flight attempt because attempts are throttled.
   Group& group = pool().GetOrCreateGroupForTesting(requester1.GetStreamKey());
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(), 1u);
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(), 1u);
 
   FastForwardBy(AttemptManager::kSpdyThrottleDelay);
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(), 2u);
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(), 2u);
 
   connect_completer1.Complete(OK);
   RunUntilIdle();
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(), 0u);
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(), 0u);
 
   EXPECT_THAT(requester1.result(), Optional(IsOk()));
   EXPECT_THAT(requester2.result(), Optional(IsOk()));
@@ -3678,7 +3719,8 @@
       stream_key.destination(), stream_key.network_anonymization_key(),
       /*supports_spdy=*/true);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester1;
   requester1.set_destination(kDestination).RequestStream(pool());
@@ -3707,14 +3749,14 @@
       .CallOnServiceEndpointRequestFinished(OK);
   // There should be only one in-flight attempt because attempts are throttled.
   Group& group = pool().GetOrCreateGroupForTesting(requester1.GetStreamKey());
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(), 1u);
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(), 1u);
 
   FastForwardBy(AttemptManager::kSpdyThrottleDelay);
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(), 2u);
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(), 2u);
 
   connect_completer1.Complete(OK);
   RunUntilIdle();
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(), 1u);
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(), 1u);
 
   connect_completer2.Complete(OK);
   RunUntilIdle();
@@ -3738,11 +3780,12 @@
 
   int rv = preconnector.Preconnect(pool());
   EXPECT_THAT(rv, IsOk());
-  ASSERT_EQ(group.GetAttemptManagerForTesting(), nullptr);
+  ASSERT_EQ(group.attempt_manager(), nullptr);
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, PreconnectFail) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   Preconnector preconnector("http://a.test");
 
@@ -3757,7 +3800,7 @@
       ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
       .CallOnServiceEndpointRequestFinished(OK);
   Group& group = pool().GetOrCreateGroupForTesting(preconnector.GetStreamKey());
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(), 1u);
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(), 1u);
   ASSERT_FALSE(preconnector.result().has_value());
 
   RunUntilIdle();
@@ -3767,7 +3810,8 @@
 TEST_F(HttpStreamPoolAttemptManagerTest, PreconnectMultipleStreamsHttp1) {
   constexpr size_t kNumStreams = 2;
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   Preconnector preconnector("http://a.test");
 
@@ -3786,8 +3830,7 @@
       ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
       .CallOnServiceEndpointRequestFinished(OK);
   Group& group = pool().GetOrCreateGroupForTesting(preconnector.GetStreamKey());
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(),
-            kNumStreams);
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(), kNumStreams);
   ASSERT_FALSE(preconnector.result().has_value());
 
   RunUntilIdle();
@@ -3833,7 +3876,7 @@
   int rv =
       preconnector.set_num_streams(kNumPreconnectStreams).Preconnect(pool());
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(),
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(),
             kNumPreconnectStreams - 1u);
   ASSERT_FALSE(preconnector.result().has_value());
 
@@ -3845,7 +3888,8 @@
 TEST_F(HttpStreamPoolAttemptManagerTest, PreconnectMultipleStreamsHttp2) {
   constexpr size_t kNumStreams = 2;
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   Preconnector preconnector("https://a.test");
 
@@ -3869,7 +3913,7 @@
       ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
       .CallOnServiceEndpointRequestFinished(OK);
   Group& group = pool().GetOrCreateGroupForTesting(preconnector.GetStreamKey());
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(), 1u);
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(), 1u);
   ASSERT_FALSE(preconnector.result().has_value());
 
   RunUntilIdle();
@@ -3883,7 +3927,8 @@
 TEST_F(HttpStreamPoolAttemptManagerTest, PreconnectRequireHttp1) {
   constexpr size_t kNumStreams = 2;
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   Preconnector preconnector("https://a.test");
 
@@ -3911,7 +3956,7 @@
       ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
       .CallOnServiceEndpointRequestFinished(OK);
   Group& group = pool().GetOrCreateGroupForTesting(preconnector.GetStreamKey());
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(), 2u);
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(), 2u);
   ASSERT_FALSE(preconnector.result().has_value());
 
   RunUntilIdle();
@@ -3925,7 +3970,8 @@
 TEST_F(HttpStreamPoolAttemptManagerTest, PreconnectMultipleStreamsOkAndFail) {
   constexpr size_t kNumStreams = 2;
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   Preconnector preconnector("http://a.test");
 
@@ -3946,8 +3992,7 @@
       ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
       .CallOnServiceEndpointRequestFinished(OK);
   Group& group = pool().GetOrCreateGroupForTesting(preconnector.GetStreamKey());
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(),
-            kNumStreams);
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(), kNumStreams);
   ASSERT_FALSE(preconnector.result().has_value());
 
   preconnector.WaitForResult();
@@ -3963,7 +4008,8 @@
 TEST_F(HttpStreamPoolAttemptManagerTest, PreconnectMultipleStreamsFailAndOk) {
   constexpr size_t kNumStreams = 2;
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   Preconnector preconnector("http://a.test");
 
@@ -3984,8 +4030,7 @@
       ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
       .CallOnServiceEndpointRequestFinished(OK);
   Group& group = pool().GetOrCreateGroupForTesting(preconnector.GetStreamKey());
-  ASSERT_EQ(group.GetAttemptManagerForTesting()->TcpBasedAttemptCount(),
-            kNumStreams);
+  ASSERT_EQ(group.attempt_manager()->TcpBasedAttemptCount(), kNumStreams);
   ASSERT_FALSE(preconnector.result().has_value());
 
   RunUntilIdle();
@@ -3996,7 +4041,8 @@
 TEST_F(HttpStreamPoolAttemptManagerTest, PreconnectMultipleRequests) {
   constexpr std::string_view kDestination("http://a.test");
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   Preconnector preconnector1(kDestination);
 
@@ -4043,7 +4089,8 @@
 
   constexpr size_t kNumStreams = 2;
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   Preconnector preconnector("http://a.test");
 
@@ -4078,7 +4125,8 @@
       StreamSocketHandle::SocketReuseType::kUnused,
       LoadTimingInfo::ConnectTiming());
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   Preconnector preconnector_b("http://b.test");
 
@@ -4107,7 +4155,8 @@
   // Add two fake DNS resolutions (one for failing case, another is for success
   // case).
   for (size_t i = 0; i < 2; ++i) {
-    FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+    base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+        resolver()->AddFakeRequest();
     endpoint_request
         ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
         .CompleteStartSynchronously(OK);
@@ -4145,11 +4194,10 @@
   // Destroy the failed request. This should destroy the failing attempt manager
   // and should create a new one.
   requester1.ResetRequest();
-  WaitForAttemptManagerComplete(*pool().GetGroupForTesting(stream_key));
-  ASSERT_FALSE(pool()
-                   .GetGroupForTesting(stream_key)
-                   ->GetAttemptManagerForTesting()
-                   ->is_failing());
+  WaitForAttemptManagerComplete(
+      pool().GetGroupForTesting(stream_key)->attempt_manager());
+  ASSERT_FALSE(
+      pool().GetGroupForTesting(stream_key)->attempt_manager()->is_failing());
 
   // The paused request should succeed now.
   requester2.WaitForResult();
@@ -4168,7 +4216,8 @@
   // Add two fake DNS resolutions (one for failing case, another is for success
   // case).
   for (size_t i = 0; i < 2; ++i) {
-    FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+    base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+        resolver()->AddFakeRequest();
     endpoint_request
         ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
         .CompleteStartSynchronously(OK);
@@ -4220,7 +4269,8 @@
   // Add fake DNS resolutions since we will create at least three attempt
   // managers. +2 is for the first two failed attempts.
   for (size_t i = 0; i < kNumPausedJobs + 2; ++i) {
-    FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+    base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+        resolver()->AddFakeRequest();
     endpoint_request
         ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
         .CompleteStartSynchronously(OK);
@@ -4273,21 +4323,18 @@
 
   // Destroy the first request. This should resume paused requests.
   failing_requester1.ResetRequest();
-  WaitForAttemptManagerComplete(*pool().GetGroupForTesting(stream_key));
-  ASSERT_FALSE(pool()
-                   .GetGroupForTesting(stream_key)
-                   ->GetAttemptManagerForTesting()
-                   ->is_failing());
+  WaitForAttemptManagerComplete(
+      pool().GetGroupForTesting(stream_key)->attempt_manager());
+  ASSERT_FALSE(
+      pool().GetGroupForTesting(stream_key)->attempt_manager()->is_failing());
 
   // Complete and destroy the second request. The group should enter failing
   // mode again.
   failing_requester2.WaitForResult();
   EXPECT_THAT(failing_requester2.result(),
               Optional(IsError(ERR_CONNECTION_RESET)));
-  ASSERT_TRUE(pool()
-                  .GetGroupForTesting(stream_key)
-                  ->GetAttemptManagerForTesting()
-                  ->is_failing());
+  ASSERT_TRUE(
+      pool().GetGroupForTesting(stream_key)->attempt_manager()->is_failing());
   failing_requester2.ResetRequest();
 
   // Complete subsequent requests. These requests could be associated with
@@ -4390,8 +4437,7 @@
   EXPECT_EQ(requester.negotiated_protocol(), NextProto::kProtoHTTP2);
   // The Group should not create an AttemptManager since the second request used
   // the existing SPDY session.
-  ASSERT_FALSE(
-      pool().GetGroupForTesting(stream_key)->GetAttemptManagerForTesting());
+  ASSERT_FALSE(pool().GetGroupForTesting(stream_key)->attempt_manager());
 
   // Close the SPDY session so that the Group can complete. The Group should not
   // be destroyed yet since the second request isn't destroyed yet.
@@ -4535,10 +4581,9 @@
   HttpStreamKey stream_key = requester1.GetStreamKey();
   requester1.ResetRequest();
   requester2.ResetRequest();
-  WaitForAttemptManagerComplete(*pool().GetGroupForTesting(stream_key));
-  ASSERT_FALSE(pool()
-                   .GetOrCreateGroupForTesting(stream_key)
-                   .GetAttemptManagerForTesting());
+  WaitForAttemptManagerComplete(
+      pool().GetGroupForTesting(stream_key)->attempt_manager());
+  ASSERT_FALSE(pool().GetOrCreateGroupForTesting(stream_key).attempt_manager());
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, PreconnectPriority) {
@@ -4555,7 +4600,7 @@
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
   EXPECT_EQ(pool()
                 .GetOrCreateGroupForTesting(preconnector.GetStreamKey())
-                .GetAttemptManagerForTesting()
+                .attempt_manager()
                 ->GetPriority(),
             RequestPriority::IDLE);
 }
@@ -4600,7 +4645,7 @@
   requester_b.ReleaseStream().reset();
   EXPECT_FALSE(pool()
                    .GetOrCreateGroupForTesting(requester_a.GetStreamKey())
-                   .GetAttemptManagerForTesting()
+                   .attempt_manager()
                    ->IsStalledByPoolLimit());
 }
 
@@ -4627,7 +4672,7 @@
 
   EXPECT_FALSE(pool()
                    .GetOrCreateGroupForTesting(requester.GetStreamKey())
-                   .GetAttemptManagerForTesting()
+                   .attempt_manager()
                    ->IsStalledByPoolLimit());
 }
 
@@ -4655,7 +4700,7 @@
 
   EXPECT_FALSE(pool()
                    .GetOrCreateGroupForTesting(requester.GetStreamKey())
-                   .GetAttemptManagerForTesting()
+                   .attempt_manager()
                    ->IsStalledByPoolLimit());
 }
 
@@ -4741,7 +4786,8 @@
   // is updated to true after the QUIC attempt succeeds.
   quic_session_pool()->set_has_quic_ever_worked_on_current_network(false);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   AddQuicData();
 
@@ -4769,7 +4815,7 @@
   EXPECT_THAT(requester.result(), Optional(IsOk()));
   EXPECT_THAT(pool()
                   .GetOrCreateGroupForTesting(requester.GetStreamKey())
-                  .GetAttemptManagerForTesting()
+                  .attempt_manager()
                   ->GetQuicAttemptResultForTesting(),
               Optional(IsOk()));
   EXPECT_TRUE(quic_session_pool()->has_quic_ever_worked_on_current_network());
@@ -4800,7 +4846,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(requester.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   ASSERT_EQ(manager->TcpBasedAttemptCount(), 0u);
 
   requester.WaitForResult();
@@ -4808,7 +4854,8 @@
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, QuicOkDnsAlpn) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   AddQuicData();
 
@@ -4839,7 +4886,7 @@
   EXPECT_THAT(requester2.result(), Optional(IsOk()));
   EXPECT_THAT(pool()
                   .GetOrCreateGroupForTesting(requester1.GetStreamKey())
-                  .GetAttemptManagerForTesting()
+                  .attempt_manager()
                   ->GetQuicAttemptResultForTesting(),
               Optional(IsOk()));
 }
@@ -4849,7 +4896,8 @@
 TEST_F(HttpStreamPoolAttemptManagerTest, DontStartQuicAfterFailure) {
   AddQuicData();
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   // Request a stream to create an AttemptManager.
   StreamRequester requester;
@@ -4858,29 +4906,27 @@
       .RequestStream(pool());
   ASSERT_FALSE(requester.result().has_value());
 
-  // Simulate a network change event to fail the AttemptManager.
+  // Simulate a network change event to fail the AttemptManager. The
+  // AttemptManager will reset ServiceEndpointRequest.
   NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
   FastForwardUntilNoTasksRemain();
+  ASSERT_FALSE(endpoint_request);
 
-  // Complete the service endpoint resolution. QuicAttempt should not start.
-  endpoint_request
-      ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
-      .CallOnServiceEndpointRequestFinished(OK);
   requester.WaitForResult();
   EXPECT_THAT(requester.result(), Optional(IsError(ERR_NETWORK_CHANGED)));
   ASSERT_TRUE(pool()
                   .GetGroupForTesting(requester.GetStreamKey())
-                  ->GetAttemptManagerForTesting()
+                  ->attempt_manager()
                   ->is_failing());
   ASSERT_FALSE(pool()
                    .GetGroupForTesting(requester.GetStreamKey())
-                   ->GetAttemptManagerForTesting()
+                   ->attempt_manager()
                    ->GetQuicAttemptResultForTesting());
 
   // Ensure that the attempt manager completes after the request is destroyed.
   requester.ResetRequest();
   WaitForAttemptManagerComplete(
-      *pool().GetGroupForTesting(requester.GetStreamKey()));
+      pool().GetGroupForTesting(requester.GetStreamKey())->attempt_manager());
 }
 
 // Tests that QUIC is not attempted when marked broken.
@@ -4941,7 +4987,7 @@
   FastForwardBy(base::Milliseconds(1));
   EXPECT_THAT(pool()
                   .GetOrCreateGroupForTesting(requester.GetStreamKey())
-                  .GetAttemptManagerForTesting()
+                  .attempt_manager()
                   ->GetQuicAttemptResultForTesting(),
               Optional(IsError(ERR_CONNECTION_REFUSED)));
   ASSERT_FALSE(requester.result().has_value());
@@ -4995,7 +5041,7 @@
   requester.WaitForResult();
   EXPECT_THAT(pool()
                   .GetOrCreateGroupForTesting(requester.GetStreamKey())
-                  .GetAttemptManagerForTesting()
+                  .attempt_manager()
                   ->GetQuicAttemptResultForTesting(),
               Optional(IsError(ERR_CONNECTION_REFUSED)));
   EXPECT_THAT(requester.result(), Optional(IsError(ERR_CONNECTION_REFUSED)));
@@ -5135,7 +5181,8 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(net::features::kAsyncQuicSession);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   AddQuicData();
 
@@ -5171,13 +5218,14 @@
 
   EXPECT_THAT(pool()
                   .GetOrCreateGroupForTesting(requester1.GetStreamKey())
-                  .GetAttemptManagerForTesting()
+                  .attempt_manager()
                   ->GetQuicAttemptResultForTesting(),
               Optional(IsOk()));
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, AlternativeSerivcesDisabled) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
   endpoint_request
       ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
       .CompleteStartSynchronously(OK);
@@ -5196,7 +5244,7 @@
   EXPECT_THAT(requester.result(), Optional(IsOk()));
   ASSERT_FALSE(pool()
                    .GetOrCreateGroupForTesting(requester.GetStreamKey())
-                   .GetAttemptManagerForTesting()
+                   .attempt_manager()
                    ->GetQuicAttemptResultForTesting()
                    .has_value());
 }
@@ -5234,7 +5282,7 @@
   EXPECT_THAT(requester2.result(), Optional(IsOk()));
   EXPECT_THAT(pool()
                   .GetOrCreateGroupForTesting(requester1.GetStreamKey())
-                  .GetAttemptManagerForTesting()
+                  .attempt_manager()
                   ->GetQuicAttemptResultForTesting(),
               Optional(IsOk()));
 }
@@ -5279,7 +5327,8 @@
   // Set that QUIC is working on the current network.
   quic_session_pool()->set_has_quic_ever_worked_on_current_network(true);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
   endpoint_request
       ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
       .CompleteStartSynchronously(OK);
@@ -5298,7 +5347,7 @@
   EXPECT_THAT(requester.result(), Optional(IsOk()));
   EXPECT_THAT(pool()
                   .GetOrCreateGroupForTesting(requester.GetStreamKey())
-                  .GetAttemptManagerForTesting()
+                  .attempt_manager()
                   ->GetQuicAttemptResultForTesting(),
               Optional(IsError(ERR_DNS_NO_MATCHING_SUPPORTED_ALPN)));
   // No matching ALPN should not update
@@ -5323,7 +5372,8 @@
   const HttpStreamKey stream_key =
       StreamKeyBuilder("https://www.example.org").Build();
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
   endpoint_request->set_crypto_ready(true);
 
   // The first request triggers DNS resolution.
@@ -5332,7 +5382,7 @@
   ASSERT_FALSE(requester1.result().has_value());
 
   AttemptManager* manager =
-      pool().GetGroupForTesting(stream_key)->GetAttemptManagerForTesting();
+      pool().GetGroupForTesting(stream_key)->attempt_manager();
 
   // The second request should not trigger a QUIC attempt in AttemptManager.
   StreamRequester requester2(stream_key);
@@ -5359,7 +5409,8 @@
 }
 
 TEST_F(HttpStreamPoolAttemptManagerTest, QuicPreconnect) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
   endpoint_request
       ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
       .CompleteStartSynchronously(OK);
@@ -5403,7 +5454,8 @@
   tcp_data.set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
   socket_factory()->AddSocketDataProvider(&tcp_data);
 
-  FakeServiceEndpointRequest* endpoint_request1 = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request1 =
+      resolver()->AddFakeRequest();
   endpoint_request1
       ->add_endpoint(
           ServiceEndpointBuilder().add_ip_endpoint(kCommonEndPoint).endpoint())
@@ -5416,7 +5468,8 @@
   RunUntilIdle();
   EXPECT_THAT(requester1.result(), Optional(IsOk()));
 
-  FakeServiceEndpointRequest* endpoint_request2 = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request2 =
+      resolver()->AddFakeRequest();
   endpoint_request2
       ->add_endpoint(
           ServiceEndpointBuilder().add_ip_endpoint(kCommonEndPoint).endpoint())
@@ -5455,7 +5508,8 @@
   tcp_data1.set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
   socket_factory()->AddSocketDataProvider(&tcp_data1);
 
-  FakeServiceEndpointRequest* endpoint_request1 = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request1 =
+      resolver()->AddFakeRequest();
   endpoint_request1
       ->add_endpoint(ServiceEndpointBuilder().add_v6("2001:db8::1").endpoint())
       .CompleteStartSynchronously(OK);
@@ -5482,7 +5536,8 @@
   tcp_data2.set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
   socket_factory()->AddSocketDataProvider(&tcp_data2);
 
-  FakeServiceEndpointRequest* endpoint_request2 = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request2 =
+      resolver()->AddFakeRequest();
   endpoint_request2
       ->add_endpoint(ServiceEndpointBuilder().add_v6("2001:db8::2").endpoint())
       .CompleteStartSynchronously(OK);
@@ -5547,7 +5602,8 @@
   quic_data2.set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
   socket_factory()->AddSocketDataProvider(&quic_data2);
 
-  FakeServiceEndpointRequest* endpoint_request1 = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request1 =
+      resolver()->AddFakeRequest();
   endpoint_request1
       ->add_endpoint(
           ServiceEndpointBuilder().add_ip_endpoint(kCommonEndPoint).endpoint())
@@ -5560,7 +5616,8 @@
   RunUntilIdle();
   EXPECT_THAT(requester1.result(), Optional(IsOk()));
 
-  FakeServiceEndpointRequest* endpoint_request2 = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request2 =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester2;
   requester2.set_destination(kAltDestination)
@@ -5601,7 +5658,8 @@
   tcp_data.set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
   socket_factory()->AddSocketDataProvider(&tcp_data);
 
-  FakeServiceEndpointRequest* endpoint_request1 = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request1 =
+      resolver()->AddFakeRequest();
   endpoint_request1
       ->add_endpoint(
           ServiceEndpointBuilder().add_ip_endpoint(kCommonEndPoint).endpoint())
@@ -5614,7 +5672,8 @@
   RunUntilIdle();
   EXPECT_THAT(requester1.result(), Optional(IsOk()));
 
-  FakeServiceEndpointRequest* endpoint_request2 = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request2 =
+      resolver()->AddFakeRequest();
   endpoint_request2
       ->add_endpoint(
           ServiceEndpointBuilder().add_ip_endpoint(kCommonEndPoint).endpoint())
@@ -5639,7 +5698,8 @@
 // In production code, we currently disable both IP-based pooling and QUIC at
 // the same time.
 TEST_F(HttpStreamPoolAttemptManagerTest, QuicMatchingIpSessionDisabled) {
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
   endpoint_request
       ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
       .CompleteStartSynchronously(OK);
@@ -5658,7 +5718,7 @@
   EXPECT_THAT(requester.result(), Optional(IsOk()));
   ASSERT_FALSE(pool()
                    .GetOrCreateGroupForTesting(requester.GetStreamKey())
-                   .GetAttemptManagerForTesting()
+                   .attempt_manager()
                    ->GetQuicAttemptResultForTesting()
                    .has_value());
 }
@@ -5667,7 +5727,8 @@
   constexpr base::TimeDelta kDelay = base::Milliseconds(10);
   quic_session_pool()->SetTimeDelayForWaitingJobForTesting(kDelay);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
   endpoint_request
       ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
       .CompleteStartSynchronously(OK);
@@ -5689,7 +5750,8 @@
   constexpr base::TimeDelta kDelay = base::Milliseconds(10);
   quic_session_pool()->SetTimeDelayForWaitingJobForTesting(kDelay);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
   endpoint_request
       ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
       .CompleteStartSynchronously(OK);
@@ -5735,7 +5797,8 @@
 
   quic_session_pool()->SetTimeDelayForWaitingJobForTesting(kDelay);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   SequencedSocketData tcp_data;
   socket_factory()->AddSocketDataProvider(&tcp_data);
@@ -5756,7 +5819,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(requester.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
 
   // Provide an IP address. QUIC attempt isn't triggered yet since it's not
   // ready for cryptographic handshakes, but the stream attempt delay timer
@@ -5798,7 +5861,8 @@
 
   quic_session_pool()->SetTimeDelayForWaitingJobForTesting(kQuicDelay);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   // QUIC attempt stalls forever.
   auto quic_data = std::make_unique<MockQuicData>(quic_version());
@@ -5819,7 +5883,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(requester.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
 
   // Provide an IP address. QUIC attempt isn't triggered yet since it's not
   // ready for cryptographic handshakes.
@@ -5863,7 +5927,8 @@
 
   quic_session_pool()->SetTimeDelayForWaitingJobForTesting(kDelay);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   MockConnectCompleter connect_completer;
   SequencedSocketData tcp_data;
@@ -5879,7 +5944,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(preconnector.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
 
   // Provide an IP address. QUIC attempt isn't triggered yet since it's not
   // ready for cryptographic handshakes.
@@ -5917,7 +5982,8 @@
   constexpr base::TimeDelta kDelay = base::Milliseconds(10);
   quic_session_pool()->SetTimeDelayForWaitingJobForTesting(kDelay);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   auto quic_data = std::make_unique<MockQuicData>(quic_version());
   quic_data->AddConnect(SYNCHRONOUS, ERR_IO_PENDING);
@@ -6359,7 +6425,7 @@
   AttemptManager* origin_manager =
       pool()
           .GetOrCreateGroupForTesting(requester.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   ASSERT_TRUE(origin_manager);
   EXPECT_EQ(origin_manager->GetPriority(), RequestPriority::LOW);
 
@@ -6368,9 +6434,8 @@
           .set_destination(url::SchemeHostPort(
               url::kHttpsScheme, kAlternative.host(), kAlternative.port()))
           .Build();
-  AttemptManager* alt_manager = pool()
-                                    .GetOrCreateGroupForTesting(alt_stream_key)
-                                    .GetAttemptManagerForTesting();
+  AttemptManager* alt_manager =
+      pool().GetOrCreateGroupForTesting(alt_stream_key).attempt_manager();
   ASSERT_TRUE(alt_manager);
   EXPECT_EQ(alt_manager->GetPriority(), RequestPriority::LOW);
 
@@ -6591,10 +6656,8 @@
   failing_requester.WaitForResult();
   EXPECT_THAT(failing_requester.result(),
               Optional(IsError(ERR_CONNECTION_REFUSED)));
-  EXPECT_TRUE(pool()
-                  .GetGroupForTesting(stream_key)
-                  ->GetAttemptManagerForTesting()
-                  ->is_failing());
+  EXPECT_TRUE(
+      pool().GetGroupForTesting(stream_key)->attempt_manager()->is_failing());
 
   // Subsequent requests (jobs) are paused until the first request is destroyed.
   std::vector<std::unique_ptr<StreamRequester>> requesters;
@@ -6625,7 +6688,8 @@
   for (auto& requester : requesters) {
     requester->ResetRequest();
   }
-  WaitForAttemptManagerComplete(*pool().GetGroupForTesting(stream_key));
+  WaitForAttemptManagerComplete(
+      pool().GetGroupForTesting(stream_key)->attempt_manager());
   EXPECT_FALSE(pool().GetGroupForTesting(stream_key));
   EXPECT_EQ(pool().TotalActiveStreamCount(), 0u);
 }
@@ -6791,7 +6855,8 @@
   // Actual test: Create a request that starts a QuicSessionAttempt, which
   // is later destroyed since there is a matching IP session.
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   MockConnectCompleter quic_completer;
   MockQuicData quic_data(quic_version());
@@ -6967,7 +7032,8 @@
   ASSERT_TRUE(MakeTestEchKeys("www.example.org", /*max_name_len=*/128,
                               &ech_config_list));
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   SequencedSocketData tcp_data;
   tcp_data.set_connect_data(MockConnect(SYNCHRONOUS, OK));
@@ -7079,7 +7145,7 @@
 
   EXPECT_THAT(pool()
                   .GetOrCreateGroupForTesting(stream_key)
-                  .GetAttemptManagerForTesting()
+                  .attempt_manager()
                   ->GetQuicAttemptResultForTesting(),
               Optional(IsError(ERR_ABORTED)));
 }
@@ -7272,7 +7338,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(requester.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   EXPECT_THAT(manager->TcpBasedAttemptCount(), 0u);
   EXPECT_THAT(manager->GetQuicAttemptResultForTesting(),
               Optional(IsError(ERR_NETWORK_CHANGED)));
@@ -7286,7 +7352,8 @@
   base::test::ScopedFeatureList feature_list;
   feature_list.InitAndEnableFeature(net::features::kAsyncQuicSession);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   endpoint_request
       ->add_endpoint(ServiceEndpointBuilder().add_v4("192.0.2.1").endpoint())
@@ -7321,7 +7388,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(requester.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   EXPECT_THAT(manager->TcpBasedAttemptCount(), 0u);
   EXPECT_THAT(manager->GetQuicAttemptResultForTesting(),
               Optional(IsError(ERR_NAME_NOT_RESOLVED)));
@@ -7365,7 +7432,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(requester.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   EXPECT_THAT(manager->TcpBasedAttemptCount(), 0u);
   EXPECT_THAT(manager->GetQuicAttemptResultForTesting(),
               Optional(IsError(ERR_SSL_CLIENT_AUTH_CERT_NEEDED)));
@@ -7404,7 +7471,7 @@
   AttemptManager* manager =
       pool()
           .GetOrCreateGroupForTesting(requester.GetStreamKey())
-          .GetAttemptManagerForTesting();
+          .attempt_manager();
   EXPECT_THAT(manager->TcpBasedAttemptCount(), 0u);
   EXPECT_THAT(manager->GetQuicAttemptResultForTesting(),
               Optional(IsError(ERR_CERT_DATE_INVALID)));
@@ -7418,7 +7485,8 @@
   data.set_connect_data(MockConnect(&completer));
   socket_factory()->AddSocketDataProvider(&data);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester;
   requester.set_destination(kDefaultDestination)
@@ -7455,7 +7523,8 @@
   SSLSocketDataProvider ssl(ASYNC, OK);
   socket_factory()->AddSSLSocketDataProvider(&ssl);
 
-  FakeServiceEndpointRequest* endpoint_request = resolver()->AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> endpoint_request =
+      resolver()->AddFakeRequest();
 
   StreamRequester requester;
   requester.set_destination(kDefaultDestination)
diff --git a/net/http/http_stream_pool_group.cc b/net/http/http_stream_pool_group.cc
index 3fe2e7f..7161ac7 100644
--- a/net/http/http_stream_pool_group.cc
+++ b/net/http/http_stream_pool_group.cc
@@ -358,10 +358,6 @@
 
   attempt_manager_.reset();
 
-  if (on_attempt_manager_complete_callback_for_testing_) {
-    std::move(on_attempt_manager_complete_callback_for_testing_).Run();
-  }
-
   if (should_resume_paused_job) {
     ResumePausedJob();
   } else {
@@ -401,12 +397,6 @@
   CleanupIdleStreamSockets(CleanupMode::kTimeoutOnly, "For testing");
 }
 
-void HttpStreamPool::Group::SetOnAttemptManagerCompleteCallbackForTesting(
-    base::OnceClosure callback) {
-  CHECK(on_attempt_manager_complete_callback_for_testing_.is_null());
-  on_attempt_manager_complete_callback_for_testing_ = std::move(callback);
-}
-
 bool HttpStreamPool::Group::IsFailing() const {
   // If we don't have an AttemptManager the group is not considered as failing
   // because we destroy an AttemptManager after all in-flight attempts are
diff --git a/net/http/http_stream_pool_group.h b/net/http/http_stream_pool_group.h
index 4293e92..6339a188 100644
--- a/net/http/http_stream_pool_group.h
+++ b/net/http/http_stream_pool_group.h
@@ -193,13 +193,6 @@
 
   void CleanupTimedoutIdleStreamSocketsForTesting();
 
-  AttemptManager* GetAttemptManagerForTesting() const {
-    return attempt_manager_.get();
-  }
-
-  void SetOnAttemptManagerCompleteCallbackForTesting(
-      base::OnceClosure callback);
-
  private:
   FRIEND_TEST_ALL_PREFIXES(HttpStreamPoolGroupTest, ComparePausedJobSet);
 
@@ -276,8 +269,6 @@
   // them to avoid dangling pointers.
   PausedJobSet resumed_jobs_;
 
-  base::OnceClosure on_attempt_manager_complete_callback_for_testing_;
-
   base::WeakPtrFactory<Group> weak_ptr_factory_{this};
 };
 
diff --git a/net/http/http_stream_pool_group_unittest.cc b/net/http/http_stream_pool_group_unittest.cc
index 1155cec..ea4a2fe 100644
--- a/net/http/http_stream_pool_group_unittest.cc
+++ b/net/http/http_stream_pool_group_unittest.cc
@@ -514,7 +514,7 @@
   delegate1.reset();
   delegate2.reset();
   delegate3.reset();
-  WaitForAttemptManagerComplete(*GetTestGroup());
+  WaitForAttemptManagerComplete(GetTestGroup()->attempt_manager());
   ASSERT_FALSE(GetTestGroup());
 }
 
diff --git a/net/http/http_stream_pool_test_util.cc b/net/http/http_stream_pool_test_util.cc
index 245eaa2..c23ce72 100644
--- a/net/http/http_stream_pool_test_util.cc
+++ b/net/http/http_stream_pool_test_util.cc
@@ -9,6 +9,7 @@
 #include "net/base/features.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_stream_pool.h"
+#include "net/http/http_stream_pool_attempt_manager.h"
 #include "net/http/http_stream_pool_group.h"
 #include "net/http/http_stream_pool_job.h"
 #include "net/log/net_log_with_source.h"
@@ -97,12 +98,14 @@
 
 FakeServiceEndpointResolver::~FakeServiceEndpointResolver() = default;
 
-FakeServiceEndpointRequest* FakeServiceEndpointResolver::AddFakeRequest() {
+base::WeakPtr<FakeServiceEndpointRequest>
+FakeServiceEndpointResolver::AddFakeRequest() {
   std::unique_ptr<FakeServiceEndpointRequest> request =
       std::make_unique<FakeServiceEndpointRequest>();
-  FakeServiceEndpointRequest* raw_request = request.get();
+  base::WeakPtr<FakeServiceEndpointRequest> weak_request =
+      request->weak_ptr_factory_.GetWeakPtr();
   requests_.emplace_back(std::move(request));
-  return raw_request;
+  return weak_request;
 }
 
 void FakeServiceEndpointResolver::OnShutdown() {}
@@ -278,9 +281,10 @@
                        group_id.disable_cert_network_fetches());
 }
 
-void WaitForAttemptManagerComplete(HttpStreamPool::Group& group) {
+void WaitForAttemptManagerComplete(
+    HttpStreamPool::AttemptManager* attempt_manager) {
   base::RunLoop run_loop;
-  group.SetOnAttemptManagerCompleteCallbackForTesting(run_loop.QuitClosure());
+  attempt_manager->SetOnCompleteCallbackForTesting(run_loop.QuitClosure());
   run_loop.Run();
 }
 
diff --git a/net/http/http_stream_pool_test_util.h b/net/http/http_stream_pool_test_util.h
index 280cd8af..e547ceb 100644
--- a/net/http/http_stream_pool_test_util.h
+++ b/net/http/http_stream_pool_test_util.h
@@ -12,6 +12,7 @@
 #include <string>
 #include <vector>
 
+#include "base/memory/weak_ptr.h"
 #include "base/test/test_future.h"
 #include "net/base/completion_once_callback.h"
 #include "net/base/net_errors.h"
@@ -100,6 +101,8 @@
   void ChangeRequestPriority(RequestPriority priority) override;
 
  private:
+  friend class FakeServiceEndpointResolver;
+
   raw_ptr<Delegate> delegate_;
 
   int start_result_ = ERR_IO_PENDING;
@@ -108,6 +111,8 @@
   bool endpoints_crypto_ready_ = false;
   ResolveErrorInfo resolve_error_info_;
   RequestPriority priority_ = RequestPriority::IDLE;
+
+  base::WeakPtrFactory<FakeServiceEndpointRequest> weak_ptr_factory_{this};
 };
 
 // A fake HostResolver that implements the ServiceEndpointRequest API using
@@ -127,7 +132,7 @@
   // CreateServiceEndpointRequest() consumes the request. You will need to call
   // this method multiple times when you expect multiple
   // CreateServiceEndpointRequest() calls.
-  FakeServiceEndpointRequest* AddFakeRequest();
+  base::WeakPtr<FakeServiceEndpointRequest> AddFakeRequest();
 
   // HostResolver methods:
   void OnShutdown() override;
@@ -344,8 +349,9 @@
 // Convert a ClientSocketPool::GroupId to an HttpStreamKey.
 HttpStreamKey GroupIdToHttpStreamKey(const ClientSocketPool::GroupId& group_id);
 
-// Wait for the `group`'s current AttemptManager completion.
-void WaitForAttemptManagerComplete(HttpStreamPool::Group& group);
+// Wait for the `attempt_manager`'s completion.
+void WaitForAttemptManagerComplete(
+    HttpStreamPool::AttemptManager* attempt_manager);
 
 }  // namespace net
 
diff --git a/net/http/mock_http_cache.cc b/net/http/mock_http_cache.cc
index a28ee40..69b386e 100644
--- a/net/http/mock_http_cache.cc
+++ b/net/http/mock_http_cache.cc
@@ -26,6 +26,7 @@
 #include "net/base/net_errors.h"
 #include "net/disk_cache/disk_cache_test_util.h"
 #include "net/http/http_cache_writers.h"
+#include "net/http/no_vary_search_cache_storage_file_operations.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace net {
@@ -712,9 +713,11 @@
     : MockHttpCache(std::make_unique<MockBackendFactory>()) {}
 
 MockHttpCache::MockHttpCache(
-    std::unique_ptr<HttpCache::BackendFactory> disk_cache_factory)
+    std::unique_ptr<HttpCache::BackendFactory> disk_cache_factory,
+    std::unique_ptr<NoVarySearchCacheStorageFileOperations> file_operations)
     : http_cache_(std::make_unique<MockNetworkLayer>(),
-                  std::move(disk_cache_factory)) {}
+                  std::move(disk_cache_factory),
+                  std::move(file_operations)) {}
 
 disk_cache::Backend* MockHttpCache::backend() {
   TestGetBackendCompletionCallback cb;
diff --git a/net/http/mock_http_cache.h b/net/http/mock_http_cache.h
index 619f21f..dc406eb 100644
--- a/net/http/mock_http_cache.h
+++ b/net/http/mock_http_cache.h
@@ -26,6 +26,7 @@
 #include "net/disk_cache/disk_cache.h"
 #include "net/http/http_cache.h"
 #include "net/http/http_transaction_test_util.h"
+#include "net/http/no_vary_search_cache_storage_file_operations.h"
 
 namespace net {
 
@@ -289,7 +290,9 @@
  public:
   MockHttpCache();
   explicit MockHttpCache(
-      std::unique_ptr<HttpCache::BackendFactory> disk_cache_factory);
+      std::unique_ptr<HttpCache::BackendFactory> disk_cache_factory,
+      std::unique_ptr<NoVarySearchCacheStorageFileOperations> file_operations =
+          nullptr);
 
   HttpCache* http_cache() { return &http_cache_; }
 
diff --git a/net/http/no_vary_search_cache.cc b/net/http/no_vary_search_cache.cc
index 1564d1cd..e320a56b 100644
--- a/net/http/no_vary_search_cache.cc
+++ b/net/http/no_vary_search_cache.cc
@@ -8,6 +8,7 @@
 #include <compare>
 #include <iostream>
 #include <limits>
+#include <map>
 #include <tuple>
 #include <type_traits>
 #include <utility>
@@ -544,11 +545,10 @@
     std::optional<std::string> query = query_string->query();
     CHECK(!query || query->find('#') == std::string::npos);
 
-    // Set `journal` to nullptr so no notification is fired for this
-    // insertion.
+    // Pass `journal_` so the merged entries are journalled as insertions.
     ReconstructURLAndDoInsert(base_url, std::move(base_url_cache_key), nvs_data,
                               std::move(query), query_string->update_time(),
-                              /*journal=*/nullptr);
+                              journal_);
   }
 }
 
@@ -859,16 +859,17 @@
 
   using QueryString = NoVarySearchCache::QueryString;
   // Get a list of every QueryString object in the map so that we can sort
-  // them to reconstruct the `lru_` list.
-  std::vector<QueryString*> all_query_strings;
-  all_query_strings.reserve(size);
+  // them to reconstruct the `lru_` list. std::multimap is used here as a
+  // workaround for the excessive binary size cost of std::sort.
+  std::multimap<base::Time, QueryString*> all_query_strings;
   for (auto& [base_url_cache_key, data_map] : cache.map_) {
     for (auto& [nvs_data, query_string_list] : data_map) {
       query_string_list.nvs_data_ref = &nvs_data;
       query_string_list.key_ref = &base_url_cache_key;
       NoVarySearchCache::ForEachQueryString(
           query_string_list.list, [&](QueryString* query_string) {
-            all_query_strings.push_back(query_string);
+            all_query_strings.emplace(query_string->update_time(),
+                                      query_string);
           });
     }
   }
@@ -876,15 +877,9 @@
     return std::nullopt;
   }
 
-  // Sort by `update_time`, which we use as an approximation of `use_time`
-  // during deserialization on the assumption that it won't make much
-  // difference.
-  std::ranges::sort(all_query_strings, std::less<base::Time>(),
-                    [](QueryString* qs) { return qs->update_time(); });
-
   // Insert each entry at the head of the list, so that the oldest entry ends
   // up at the tail.
-  for (QueryString* qs : all_query_strings) {
+  for (auto [_, qs] : all_query_strings) {
     qs->LruNode::InsertBefore(cache.lru_.head());
   }
 
diff --git a/net/http/no_vary_search_cache.h b/net/http/no_vary_search_cache.h
index 89aba4be..1f0c7f2b 100644
--- a/net/http/no_vary_search_cache.h
+++ b/net/http/no_vary_search_cache.h
@@ -95,7 +95,7 @@
 
     // Called when an entry is inserted or refreshed by the MaybeInsert()
     // method. Not called when MaybeInsert() results in no changes to the
-    // database.
+    // database. Also called by MergeFrom() for each merged entry.
     virtual void OnInsert(const std::string& base_url_cache_key,
                           const HttpNoVarySearchData& nvs_data,
                           const std::optional<std::string>& query,
@@ -198,8 +198,9 @@
 
   // Merge entries from `newer` in order from the least-recently-used to the
   // most-recently-used, treating them as newly used. Less recently-used entries
-  // will be evicted if necessary to avoid exceeding the maximum size. Journal
-  // methods are not called.
+  // will be evicted if necessary to avoid exceeding the maximum size.
+  // Journal::OnInsert() is called as if the entries were newly inserted (but
+  // with the original update_time).
   void MergeFrom(const NoVarySearchCache& newer);
 
   // Returns the size (number of stored original query strings) of the cache.
@@ -210,7 +211,7 @@
   size_t max_size() const { return max_size_; }
 
   // Returns true if the top-level map is empty. This should be equivalent to
-  // GetSizeForTesting() == 0 in the absence of bugs.
+  // size() == 0 in the absence of bugs.
   bool IsTopLevelMapEmptyForTesting() const;
 
  private:
diff --git a/net/http/no_vary_search_cache_storage_mock_file_operations.cc b/net/http/no_vary_search_cache_storage_mock_file_operations.cc
new file mode 100644
index 0000000..a4b1bae
--- /dev/null
+++ b/net/http/no_vary_search_cache_storage_mock_file_operations.cc
@@ -0,0 +1,15 @@
+// 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 "net/http/no_vary_search_cache_storage_mock_file_operations.h"
+
+namespace net {
+
+MockFileOperations::MockFileOperations() = default;
+MockFileOperations::~MockFileOperations() = default;
+
+MockWriter::MockWriter() = default;
+MockWriter::~MockWriter() = default;
+
+}  // namespace net
diff --git a/net/http/no_vary_search_cache_storage_mock_file_operations.h b/net/http/no_vary_search_cache_storage_mock_file_operations.h
new file mode 100644
index 0000000..80325b7
--- /dev/null
+++ b/net/http/no_vary_search_cache_storage_mock_file_operations.h
@@ -0,0 +1,52 @@
+// 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 NET_HTTP_NO_VARY_SEARCH_CACHE_STORAGE_MOCK_FILE_OPERATIONS_H_
+#define NET_HTTP_NO_VARY_SEARCH_CACHE_STORAGE_MOCK_FILE_OPERATIONS_H_
+
+#include "net/http/no_vary_search_cache_storage_file_operations.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace net {
+
+// Mock implementation of NoVarySearchCacheStorageFileOperations.
+class MockFileOperations : public NoVarySearchCacheStorageFileOperations {
+ public:
+  MockFileOperations();
+  ~MockFileOperations() override;
+
+  MOCK_METHOD((base::expected<LoadResult, base::File::Error>),
+              Load,
+              (std::string_view filename, size_t max_size),
+              (override));
+  MOCK_METHOD((base::expected<void, base::File::Error>),
+              AtomicSave,
+              (std::string_view filename,
+               base::span<const base::span<const uint8_t>> segments),
+              (override));
+  MOCK_METHOD((base::expected<std::unique_ptr<Writer>, base::File::Error>),
+              CreateWriter,
+              (std::string_view filename),
+              (override));
+};
+
+// Mock implementation of NoVarySearchCacheStorageFileOperations::Writer. This
+// can be returned from CreateWriter() after setting expectations, like this:
+//
+//  auto mock_writer = std::make_unique<StrictMock<MockWriter>>();
+//  EXPECT_CALL(*mock_writer, Write).WillOnce(Return(true));
+//  EXPECT_CALL(file_operations, CreateWriter)
+//    .WillOnce(Return(std::move(mock_writer)));
+//
+class MockWriter : public NoVarySearchCacheStorageFileOperations::Writer {
+ public:
+  MockWriter();
+  ~MockWriter() override;
+
+  MOCK_METHOD(bool, Write, (base::span<const uint8_t> data), (override));
+};
+
+}  // namespace net
+
+#endif  // NET_HTTP_NO_VARY_SEARCH_CACHE_STORAGE_MOCK_FILE_OPERATIONS_H_
diff --git a/net/http/no_vary_search_cache_storage_unittest.cc b/net/http/no_vary_search_cache_storage_unittest.cc
index 1dec151..baa390cd 100644
--- a/net/http/no_vary_search_cache_storage_unittest.cc
+++ b/net/http/no_vary_search_cache_storage_unittest.cc
@@ -36,6 +36,7 @@
 #include "net/base/features.h"
 #include "net/http/no_vary_search_cache.h"
 #include "net/http/no_vary_search_cache_storage_file_operations.h"
+#include "net/http/no_vary_search_cache_storage_mock_file_operations.h"
 #include "net/http/no_vary_search_cache_test_utils.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -821,29 +822,6 @@
   EXPECT_GT(journal_entry_size, 0u);
 }
 
-// Mock implementation of FileOperations for error injection.
-class MockFileOperations : public NoVarySearchCacheStorageFileOperations {
- public:
-  MOCK_METHOD((base::expected<LoadResult, base::File::Error>),
-              Load,
-              (std::string_view filename, size_t max_size),
-              (override));
-  MOCK_METHOD((base::expected<void, base::File::Error>),
-              AtomicSave,
-              (std::string_view filename,
-               base::span<const base::span<const uint8_t>> segments),
-              (override));
-  MOCK_METHOD((base::expected<std::unique_ptr<Writer>, base::File::Error>),
-              CreateWriter,
-              (std::string_view filename),
-              (override));
-};
-
-class MockWriter : public NoVarySearchCacheStorageFileOperations::Writer {
- public:
-  MOCK_METHOD(bool, Write, (base::span<const uint8_t> data), (override));
-};
-
 class NoVarySearchCacheStorageMockFilesystemTest
     : public NoVarySearchCacheStorageTestBase {
  public:
diff --git a/net/http/no_vary_search_cache_unittest.cc b/net/http/no_vary_search_cache_unittest.cc
index 552ba0c..8e14f3ff 100644
--- a/net/http/no_vary_search_cache_unittest.cc
+++ b/net/http/no_vary_search_cache_unittest.cc
@@ -39,9 +39,12 @@
 namespace nvs_test = no_vary_search_cache_test_utils;
 
 using ::testing::_;
+using ::testing::AllOf;
 using ::testing::EndsWith;
 using ::testing::Eq;
 using ::testing::Ge;
+using ::testing::InSequence;
+using ::testing::Le;
 using ::testing::Optional;
 
 constexpr size_t kMaxSize = 5;
@@ -1244,12 +1247,41 @@
 TEST_P(NoVarySearchCacheReplayTest, MergeFrom) {
   const auto test_cases = ReplayTestCases();
 
+  const base::Time before_inserts = base::Time::Now();
+
   for (const auto& [description, to_insert, no_vary_search_value, to_lookup] :
        test_cases) {
     cache().MaybeInsert(to_insert, TestHeaders(no_vary_search_value));
   }
 
+  const base::Time after_inserts = base::Time::Now();
+
   NoVarySearchCache target(kMaxSize);
+  ScopedMockJournal journal(target);
+
+  EXPECT_CALL(journal, OnErase).Times(0);
+
+  {
+    InSequence s;
+    for (const auto& [description, to_insert, no_vary_search_value, to_lookup] :
+         test_cases) {
+      auto expected_nvs_data = HttpNoVarySearchData::ParseFromHeaders(
+          TestHeaders(no_vary_search_value));
+      const GURL& url = to_insert.url;
+      std::optional<std::string_view> query;
+      if (url.has_query()) {
+        query = url.query_piece();
+      }
+      std::string base_url = url.spec();
+      if (size_t pos = base_url.find('?'); pos != std::string::npos) {
+        base_url = base_url.substr(0, pos);
+      }
+      EXPECT_CALL(journal,
+                  OnInsert(EndsWith(base_url), Eq(expected_nvs_data), Eq(query),
+                           AllOf(Ge(before_inserts), Le(after_inserts))));
+    }
+  }
+
   target.MergeFrom(cache());
 
   EXPECT_EQ(cache().size(), target.size());
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc
index ef16c0f..4ac56ba 100644
--- a/net/url_request/url_request_context_builder.cc
+++ b/net/url_request/url_request_context_builder.cc
@@ -21,6 +21,7 @@
 #include "base/types/pass_key.h"
 #include "build/build_config.h"
 #include "net/base/cache_type.h"
+#include "net/base/features.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_delegate_impl.h"
 #include "net/cert/cert_verifier.h"
@@ -39,6 +40,7 @@
 #include "net/http/http_network_session.h"
 #include "net/http/http_server_properties.h"
 #include "net/http/http_server_properties_manager.h"
+#include "net/http/no_vary_search_cache_storage_file_operations.h"
 #include "net/http/transport_security_persister.h"
 #include "net/http/transport_security_state.h"
 #include "net/log/net_log.h"
@@ -558,6 +560,8 @@
             std::move(http_transaction_factory), enable_shared_zstd_);
   }
 
+  std::unique_ptr<NoVarySearchCacheStorageFileOperations> file_operations;
+
   if (http_cache_enabled_) {
     std::unique_ptr<HttpCache::BackendFactory> http_cache_backend;
     if (http_cache_params_.type != HttpCacheParams::IN_MEMORY) {
@@ -581,6 +585,10 @@
           DISK_CACHE, backend_type, http_cache_params_.file_operations_factory,
           http_cache_params_.path, http_cache_params_.max_size,
           http_cache_params_.reset_cache);
+      if (base::FeatureList::IsEnabled(features::kHttpCacheNoVarySearch)) {
+        file_operations = NoVarySearchCacheStorageFileOperations::Create(
+            http_cache_params_.path);
+      }
     } else {
       http_cache_backend =
           HttpCache::DefaultBackend::InMemory(http_cache_params_.max_size);
@@ -591,7 +599,8 @@
 #endif
 
     http_transaction_factory = std::make_unique<HttpCache>(
-        std::move(http_transaction_factory), std::move(http_cache_backend));
+        std::move(http_transaction_factory), std::move(http_cache_backend),
+        std::move(file_operations));
   }
   context->set_http_transaction_factory(std::move(http_transaction_factory));
 
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 2c75034..b1a654a 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/android/android_info.h"
+#include "net/url_request/url_request.h"
 
 #include <stdint.h>
 
@@ -14,6 +14,7 @@
 #include <string_view>
 #include <utility>
 
+#include "base/android/android_info.h"
 #include "base/base64.h"
 #include "base/base64url.h"
 #include "base/compiler_specific.h"
@@ -109,6 +110,7 @@
 #include "net/http/http_status_code.h"
 #include "net/http/http_transaction_test_util.h"
 #include "net/http/http_util.h"
+#include "net/http/no_vary_search_cache_storage_file_operations.h"
 #include "net/http/transport_security_state.h"
 #include "net/http/transport_security_state_source.h"
 #include "net/log/file_net_log_observer.h"
@@ -144,7 +146,6 @@
 #include "net/url_request/referrer_policy.h"
 #include "net/url_request/static_http_user_agent_settings.h"
 #include "net/url_request/storage_access_status_cache.h"
-#include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_filter.h"
@@ -8798,7 +8799,8 @@
         network_layer->OnSuspend();
         std::unique_ptr<HttpTransactionFactory> factory =
             std::make_unique<HttpCache>(std::move(network_layer),
-                                        HttpCache::DefaultBackend::InMemory(0));
+                                        HttpCache::DefaultBackend::InMemory(0),
+                                        /*file_operations=*/nullptr);
         return factory;
       }));
   auto context = context_builder->Build();
diff --git a/pdf/pdf_ink_module.cc b/pdf/pdf_ink_module.cc
index 2af8a969..727ce88 100644
--- a/pdf/pdf_ink_module.cc
+++ b/pdf/pdf_ink_module.cc
@@ -351,8 +351,6 @@
            &PdfInkModule::HandleFinishTextAnnotationMessage},
           {"getAnnotationBrush",
            &PdfInkModule::HandleGetAnnotationBrushMessage},
-          {"getTextAnnotFontNames",
-           &PdfInkModule::HandleGetTextAnnotFontNamesMessage},
           {"setAnnotationBrush",
            &PdfInkModule::HandleSetAnnotationBrushMessage},
           {"setAnnotationMode", &PdfInkModule::HandleSetAnnotationModeMessage},
@@ -1308,12 +1306,6 @@
   MaybeSetCursor();
 }
 
-void PdfInkModule::HandleGetTextAnnotFontNamesMessage(
-    const base::Value::Dict& message) {
-  // TODO(crbug.com/409439509): Fill in this method. For now, just create it
-  // so the backend doesn't CHECK when it's sent from the frontend.
-}
-
 void PdfInkModule::HandleStartTextAnnotationMessage(
     const base::Value::Dict& message) {
   // TODO(crbug.com/409439509): Fill in this method. For now, just create it
diff --git a/pdf/pdf_ink_module.h b/pdf/pdf_ink_module.h
index 45adf9d..cf02d0f 100644
--- a/pdf/pdf_ink_module.h
+++ b/pdf/pdf_ink_module.h
@@ -388,7 +388,6 @@
   void HandleAnnotationUndoMessage(const base::Value::Dict& message);
   void HandleFinishTextAnnotationMessage(const base::Value::Dict& message);
   void HandleGetAnnotationBrushMessage(const base::Value::Dict& message);
-  void HandleGetTextAnnotFontNamesMessage(const base::Value::Dict& message);
   void HandleSetAnnotationBrushMessage(const base::Value::Dict& message);
   void HandleSetAnnotationModeMessage(const base::Value::Dict& message);
   void HandleStartTextAnnotationMessage(const base::Value::Dict& message);
diff --git a/pdf/pdfium/pdfium_text_fragment_finder.cc b/pdf/pdfium/pdfium_text_fragment_finder.cc
index c9397315..ac0ab4a 100644
--- a/pdf/pdfium/pdfium_text_fragment_finder.cc
+++ b/pdf/pdfium/pdfium_text_fragment_finder.cc
@@ -296,6 +296,10 @@
                             std::ref(text_fragment_suffix_), fragment));
 
     if (text_fragment_end_) {
+      // If a text fragment end was found, then the text fragment start list
+      // should be cleared except for the start range that was used in the
+      // search.
+      text_fragment_starts_ = {start_range};
       FinishTextFragmentSearch();
       return;
     }
diff --git a/pdf/pdfium/pdfium_text_fragment_finder_unittest.cc b/pdf/pdfium/pdfium_text_fragment_finder_unittest.cc
index 011e2df..d452aee 100644
--- a/pdf/pdfium/pdfium_text_fragment_finder_unittest.cc
+++ b/pdf/pdfium/pdfium_text_fragment_finder_unittest.cc
@@ -44,11 +44,10 @@
   const auto highlights = finder.FindTextFragments({"Google"});
   ASSERT_EQ(highlights.size(), 1u);
 
-  static constexpr char16_t kExpectedString[] = u"Google";
+  static constexpr std::u16string_view kExpectedString = u"Google";
   const auto& range = highlights[0];
   EXPECT_EQ(range.GetText(), kExpectedString);
-  EXPECT_EQ(range.char_count(),
-            static_cast<int>(base::span_from_cstring(kExpectedString).size()));
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString.size()));
   EXPECT_EQ(range.char_index(), 9);
   EXPECT_EQ(range.page_index(), 0);
 }
@@ -63,12 +62,11 @@
   const auto highlights = finder.FindTextFragments({"spanner,database"});
   ASSERT_EQ(highlights.size(), 1u);
 
-  static constexpr char16_t kExpectedString[] =
+  static constexpr std::u16string_view kExpectedString =
       u"Spanner: Google\x2019s Globally-Distributed Database\r";
   const auto& range = highlights[0];
   EXPECT_EQ(range.GetText(), kExpectedString);
-  EXPECT_EQ(range.char_count(),
-            static_cast<int>(base::span_from_cstring(kExpectedString).size()));
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString.size()));
   EXPECT_EQ(range.char_index(), 0);
   EXPECT_EQ(range.page_index(), 0);
 }
@@ -83,11 +81,10 @@
   const auto highlights = finder.FindTextFragments({"how,-many"});
   ASSERT_EQ(highlights.size(), 1u);
 
-  static constexpr char16_t kExpectedString[] = u"how";
+  static constexpr std::u16string_view kExpectedString = u"how";
   const auto& range = highlights[0];
   EXPECT_EQ(range.GetText(), kExpectedString);
-  EXPECT_EQ(range.char_count(),
-            static_cast<int>(base::span_from_cstring(kExpectedString).size()));
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString.size()));
   EXPECT_EQ(range.char_index(), 4141);
   EXPECT_EQ(range.page_index(), 0);
 }
@@ -103,13 +100,12 @@
   ASSERT_EQ(highlights.size(), 1u);
   const auto& range = highlights[0];
 
-  static constexpr char16_t kExpectedString[] =
+  static constexpr std::u16string_view kExpectedString =
       u"This\r\npaper describes how Spanner is structured, its feature "
       u"set,\r\nthe rationale underlying various design decisions, and "
       u"a\r\nnovel time API that exposes clock uncertainty. This API\r";
   EXPECT_EQ(range.GetText(), kExpectedString);
-  EXPECT_EQ(range.char_count(),
-            static_cast<int>(base::span_from_cstring(kExpectedString).size()));
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString.size()));
   EXPECT_EQ(range.char_index(), 704);
   EXPECT_EQ(range.page_index(), 0);
 }
@@ -124,11 +120,10 @@
   const auto highlights = finder.FindTextFragments({"is-,Google"});
   ASSERT_EQ(highlights.size(), 1u);
 
-  static constexpr char16_t kExpectedString[] = u"Google";
+  static constexpr std::u16string_view kExpectedString = u"Google";
   const auto& range = highlights[0];
   EXPECT_EQ(range.GetText(), kExpectedString);
-  EXPECT_EQ(range.char_count(),
-            static_cast<int>(base::span_from_cstring(kExpectedString).size()));
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString.size()));
   EXPECT_EQ(range.char_index(), 489);
   EXPECT_EQ(range.page_index(), 0);
 }
@@ -143,11 +138,10 @@
   const auto highlights = finder.FindTextFragments({"of-,Google,-'s"});
   ASSERT_EQ(highlights.size(), 1u);
 
-  static constexpr char16_t kExpectedString[] = u"Google";
+  static constexpr std::u16string_view kExpectedString = u"Google";
   const auto& range = highlights[0];
   EXPECT_EQ(range.GetText(), kExpectedString);
-  EXPECT_EQ(range.char_count(),
-            static_cast<int>(base::span_from_cstring(kExpectedString).size()));
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString.size()));
   EXPECT_EQ(range.char_index(), 2072);
   EXPECT_EQ(range.page_index(), 0);
 }
@@ -164,12 +158,11 @@
   ASSERT_EQ(highlights.size(), 1u);
   const auto& range = highlights[0];
 
-  static constexpr char16_t kExpectedString[] =
+  static constexpr std::u16string_view kExpectedString =
       u"API that exposes clock uncertainty. This API\r\nand its "
       u"implementation";
   EXPECT_EQ(range.GetText(), kExpectedString);
-  EXPECT_EQ(range.char_count(),
-            static_cast<int>(base::span_from_cstring(kExpectedString).size()));
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString.size()));
   EXPECT_EQ(range.char_index(), 840);
   EXPECT_EQ(range.page_index(), 0);
 }
@@ -185,12 +178,11 @@
       finder.FindTextFragments({"and-,applications,old,-timestamps"});
   ASSERT_EQ(highlights.size(), 1u);
 
-  static constexpr char16_t kExpectedString[] =
+  static constexpr std::u16string_view kExpectedString =
       u"applications can read data at old";
   const auto& range = highlights[0];
   EXPECT_EQ(range.GetText(), kExpectedString);
-  EXPECT_EQ(range.char_count(),
-            static_cast<int>(base::span_from_cstring(kExpectedString).size()));
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString.size()));
   EXPECT_EQ(range.char_index(), 3591);
   EXPECT_EQ(range.page_index(), 0);
 }
@@ -207,33 +199,30 @@
                                 "and-,applications,old,-timestamps"});
   ASSERT_EQ(highlights.size(), 4u);
 
-  static constexpr char16_t kExpectedString1[] = u"Google";
-  static constexpr int kExpectedString1Length =
-      static_cast<int>(base::span_from_cstring(kExpectedString1).size());
-  static constexpr char16_t kExpectedString2[] =
+  static constexpr std::u16string_view kExpectedString1 = u"Google";
+  static constexpr std::u16string_view kExpectedString2 =
       u"applications can read data at old";
   auto range = highlights[0];
   EXPECT_EQ(range.GetText(), kExpectedString1);
-  EXPECT_EQ(range.char_count(), kExpectedString1Length);
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString1.size()));
   EXPECT_EQ(range.char_index(), 9);
   EXPECT_EQ(range.page_index(), 0);
 
   range = highlights[1];
   EXPECT_EQ(range.GetText(), kExpectedString1);
-  EXPECT_EQ(range.char_count(), kExpectedString1Length);
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString1.size()));
   EXPECT_EQ(range.char_index(), 489);
   EXPECT_EQ(range.page_index(), 0);
 
   range = highlights[2];
   EXPECT_EQ(range.GetText(), kExpectedString1);
-  EXPECT_EQ(range.char_count(), kExpectedString1Length);
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString1.size()));
   EXPECT_EQ(range.char_index(), 2072);
   EXPECT_EQ(range.page_index(), 0);
 
   range = highlights[3];
   EXPECT_EQ(range.GetText(), kExpectedString2);
-  EXPECT_EQ(range.char_count(),
-            static_cast<int>(base::span_from_cstring(kExpectedString2).size()));
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString2.size()));
   EXPECT_EQ(range.char_index(), 3591);
   EXPECT_EQ(range.page_index(), 0);
 }
@@ -250,42 +239,37 @@
                                 "second-,page", "second-,page,in,-document"});
   ASSERT_EQ(highlights.size(), 5u);
 
-  static constexpr char16_t kExpectedString1[] = u"Link";
-  static constexpr int kExpectedString1Length =
-      static_cast<int>(base::span_from_cstring(kExpectedString1).size());
-  static constexpr char16_t kExpectedString2[] = u"Page";
-  static constexpr char16_t kExpectedString3[] = u"page\r";
-  static constexpr char16_t kExpectedString4[] = u"Page in";
+  static constexpr std::u16string_view kExpectedString1 = u"Link";
+  static constexpr std::u16string_view kExpectedString2 = u"Page";
+  static constexpr std::u16string_view kExpectedString3 = u"page\r";
+  static constexpr std::u16string_view kExpectedString4 = u"Page in";
   auto range = highlights[0];
   EXPECT_EQ(range.GetText(), kExpectedString1);
-  EXPECT_EQ(range.char_count(), kExpectedString1Length);
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString1.size()));
   EXPECT_EQ(range.char_index(), 0);
   EXPECT_EQ(range.page_index(), 0);
 
   range = highlights[1];
   EXPECT_EQ(range.GetText(), kExpectedString1);
-  EXPECT_EQ(range.char_count(), kExpectedString1Length);
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString1.size()));
   EXPECT_EQ(range.char_index(), 27);
   EXPECT_EQ(range.page_index(), 0);
 
   range = highlights[2];
   EXPECT_EQ(range.GetText(), kExpectedString2);
-  EXPECT_EQ(range.char_count(),
-            static_cast<int>(base::span_from_cstring(kExpectedString2).size()));
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString2.size()));
   EXPECT_EQ(range.char_index(), 7);
   EXPECT_EQ(range.page_index(), 1);
 
   range = highlights[3];
   EXPECT_EQ(range.GetText(), kExpectedString3);
-  EXPECT_EQ(range.char_count(),
-            static_cast<int>(base::span_from_cstring(kExpectedString3).size()));
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString3.size()));
   EXPECT_EQ(range.char_index(), 59);
   EXPECT_EQ(range.page_index(), 0);
 
   range = highlights[4];
   EXPECT_EQ(range.GetText(), kExpectedString4);
-  EXPECT_EQ(range.char_count(),
-            static_cast<int>(base::span_from_cstring(kExpectedString4).size()));
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString4.size()));
   EXPECT_EQ(range.char_index(), 7);
   EXPECT_EQ(range.page_index(), 1);
 }
@@ -341,6 +325,27 @@
   EXPECT_TRUE(highlights.empty());
 }
 
+TEST_P(PDFiumTextFragmentFinderTest,
+       TextStartAndEnd_FindsCorrectInstanceOfStart) {
+  NiceMock<SearchStringTestClient> client;
+  std::unique_ptr<PDFiumEngine> engine =
+      InitializeEngine(&client, FILE_PATH_LITERAL("link_annots.pdf"));
+  ASSERT_TRUE(engine);
+
+  PDFiumTextFragmentFinder finder(engine.get());
+  // "second" appears on both pages of the PDF.
+  const auto highlights = finder.FindTextFragments({"second,document"});
+  ASSERT_EQ(highlights.size(), 1u);
+
+  static constexpr std::u16string_view kExpectedString =
+      u"Second Page in Document";
+  const auto& range = highlights[0];
+  EXPECT_EQ(range.GetText(), kExpectedString);
+  EXPECT_EQ(range.char_count(), static_cast<int>(kExpectedString.size()));
+  EXPECT_EQ(range.char_index(), 0);
+  EXPECT_EQ(range.page_index(), 1);
+}
+
 INSTANTIATE_TEST_SUITE_P(All, PDFiumTextFragmentFinderTest, testing::Bool());
 
 }  // namespace chrome_pdf
diff --git a/printing/backend/cups_ipp_constants.cc b/printing/backend/cups_ipp_constants.cc
index 80b573c..e20ad20 100644
--- a/printing/backend/cups_ipp_constants.cc
+++ b/printing/backend/cups_ipp_constants.cc
@@ -33,6 +33,7 @@
 constexpr char kIppMediaCol[] = "media-col";  // PWG 5100.7
 constexpr char kIppDuplex[] = CUPS_SIDES;
 constexpr char kIppResolution[] = "printer-resolution";  // RFC 8011
+constexpr char kIppPrintQuality[] = "print-quality";     // RFC 8011
 
 // collation values
 constexpr char kCollated[] = "separate-documents-collated-copies";
diff --git a/printing/backend/cups_ipp_constants.h b/printing/backend/cups_ipp_constants.h
index cfaa699..726b6be 100644
--- a/printing/backend/cups_ipp_constants.h
+++ b/printing/backend/cups_ipp_constants.h
@@ -33,6 +33,7 @@
 COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppMediaCol[];
 COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppDuplex[];
 COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppResolution[];
+COMPONENT_EXPORT(PRINT_BACKEND) extern const char kIppPrintQuality[];
 
 // collation values
 COMPONENT_EXPORT(PRINT_BACKEND) extern const char kCollated[];
diff --git a/printing/mojom/print.mojom b/printing/mojom/print.mojom
index ae7143b..662bfe9 100644
--- a/printing/mojom/print.mojom
+++ b/printing/mojom/print.mojom
@@ -53,6 +53,15 @@
   kHpPjlColorAsGrayYes,  // Used in HP printer PPDs.
 };
 
+// Print job quality values (from rfc8011).
+[EnableIf=is_chromeos]
+enum Quality {
+  kUnknownQuality = -1,
+  kDraft = 3,
+  kNormal = 4,
+  kHigh = 5
+};
+
 // Print job duplex mode values.
 enum DuplexMode {
   kUnknownDuplexMode = -1,
diff --git a/printing/print_settings.h b/printing/print_settings.h
index 43574fa6..ead9c4a 100644
--- a/printing/print_settings.h
+++ b/printing/print_settings.h
@@ -325,6 +325,9 @@
     print_scaling_ = print_scaling;
   }
   mojom::PrintScalingType print_scaling() const { return print_scaling_; }
+
+  void set_quality(mojom::Quality quality) { quality_ = quality; }
+  mojom::Quality quality() const { return quality_; }
 #endif  // BUILDFLAG(IS_CHROMEOS)
 
 #if BUILDFLAG(ENABLE_OOP_PRINTING_NO_OOP_BASIC_PRINT_DIALOG)
@@ -467,6 +470,9 @@
   // Print scaling type.
   mojom::PrintScalingType print_scaling_ =
       mojom::PrintScalingType::kUnknownPrintScalingType;
+
+  // Print qulity for the printer to use.
+  mojom::Quality quality_ = mojom::Quality::kUnknownQuality;
 #endif  // BUILDFLAG(IS_CHROMEOS)
 };
 
diff --git a/printing/printing_context_chromeos.cc b/printing/printing_context_chromeos.cc
index d0c7c6d..0730c29 100644
--- a/printing/printing_context_chromeos.cc
+++ b/printing/printing_context_chromeos.cc
@@ -305,6 +305,12 @@
                  PrintScalingTypeToIPPString(settings.print_scaling()).c_str());
   }
 
+  // print quality
+  if (settings.quality() != mojom::Quality::kUnknownQuality) {
+    ippAddInteger(options, IPP_TAG_JOB, IPP_TAG_ENUM, kIppPrintQuality,
+                  static_cast<int>(settings.quality()));
+  }
+
   std::map<std::string, std::vector<int>> multival;
   std::string media_source;
   for (const auto& setting : settings.advanced_settings()) {
diff --git a/remoting/base/crash/BUILD.gn b/remoting/base/crash/BUILD.gn
index 0909b55c..14d886ba 100644
--- a/remoting/base/crash/BUILD.gn
+++ b/remoting/base/crash/BUILD.gn
@@ -5,26 +5,19 @@
 import("//remoting/build/config/remoting_build.gni")
 
 source_set("crash") {
-  public_deps = [ ":breakpad_utils" ]
-  if (enable_chromoting_crashpad) {
+  public_deps = []
+  if (is_linux || is_win) {
     public_deps += [ ":crashpad" ]
-  } else {
-    public_deps += [ ":breakpad" ]
+  }
+  if (is_win) {
+    public_deps += [
+      ":breakpad",
+      ":breakpad_utils",
+    ]
   }
 }
 
-source_set("breakpad_utils") {
-  sources = [
-    "breakpad_utils.cc",
-    "breakpad_utils.h",
-  ]
-  deps = [
-    "//base",
-    "//remoting/base:remoting_base_version",
-  ]
-}
-
-if (enable_chromoting_crashpad) {
+if (is_linux || is_win) {
   source_set("crashpad_db_mgr") {
     sources = [
       "crashpad_database_manager.cc",
@@ -38,15 +31,27 @@
       "//third_party/crashpad/crashpad/util",
     ]
   }
+
   source_set("crashpad") {
     sources = [
-      "crash_reporting.cc",
-      "crash_reporting.h",
-      "crashpad.h",
-      "crashpad_linux.cc",
-      "crashpad_linux.h",
+      "crash_reporting_crashpad.cc",
+      "crash_reporting_crashpad.h",
     ]
+    if (is_linux) {
+      sources += [
+        "crashpad_linux.cc",
+        "crashpad_linux.h",
+      ]
+    }
+    if (is_win) {
+      sources += [
+        "crashpad_win.cc",
+        "crashpad_win.h",
+      ]
+    }
+
     configs += [ "//remoting/build/config:host_implementation" ]
+
     deps = [
       ":crashpad_db_mgr",
       "//base",
@@ -57,11 +62,27 @@
       "//third_party/crashpad/crashpad/util",
     ]
   }
-} else {
+}
+
+if (is_win) {
+  source_set("breakpad_utils") {
+    sources = [
+      "breakpad_utils.cc",
+      "breakpad_utils.h",
+    ]
+    deps = [
+      "//base",
+      "//remoting/base:remoting_base_version",
+    ]
+  }
+
   source_set("breakpad") {
     sources = [
-      "crash_reporting.cc",
-      "crash_reporting.h",
+      "breakpad_server.cc",
+      "breakpad_win.cc",
+      "breakpad_win.h",
+      "crash_reporting_breakpad.cc",
+      "crash_reporting_breakpad.h",
     ]
 
     configs += [
@@ -72,19 +93,9 @@
     deps = [
       ":breakpad_utils",
       "//base",
+      "//remoting/base:logging",
       "//remoting/base:remoting_base_version",
+      "//third_party/breakpad:breakpad_handler",
     ]
-
-    if (is_win) {
-      sources += [
-        "breakpad_server.cc",
-        "breakpad_win.cc",
-        "breakpad_win.h",
-      ]
-      deps += [
-        "//remoting/base:logging",
-        "//third_party/breakpad:breakpad_handler",
-      ]
-    }
   }
 }
diff --git a/remoting/base/crash/breakpad_server.cc b/remoting/base/crash/breakpad_server.cc
index 4a071cd..5eed1655 100644
--- a/remoting/base/crash/breakpad_server.cc
+++ b/remoting/base/crash/breakpad_server.cc
@@ -15,7 +15,7 @@
 #include "base/win/security_descriptor.h"
 #include "base/win/sid.h"
 #include "remoting/base/crash/breakpad_utils.h"
-#include "remoting/base/crash/crash_reporting.h"
+#include "remoting/base/crash/crash_reporting_breakpad.h"
 #include "remoting/base/logging.h"
 #include "remoting/base/version.h"
 #include "third_party/breakpad/breakpad/src/client/windows/crash_generation/crash_generation_server.h"
diff --git a/remoting/base/crash/crash_reporting.cc b/remoting/base/crash/crash_reporting_breakpad.cc
similarity index 62%
copy from remoting/base/crash/crash_reporting.cc
copy to remoting/base/crash/crash_reporting_breakpad.cc
index 73f7e651..07746c5 100644
--- a/remoting/base/crash/crash_reporting.cc
+++ b/remoting/base/crash/crash_reporting_breakpad.cc
@@ -2,34 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "remoting/base/crash/crash_reporting.h"
+#include "remoting/base/crash/crash_reporting_breakpad.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "remoting/base/crash/breakpad_win.h"
 #endif  // BUILDFLAG(IS_WIN)
 
-#if BUILDFLAG(IS_LINUX)
-#include "remoting/base/crash/crashpad_linux.h"
-#endif  // BUILDFLAG(IS_LINUX)
-
 namespace remoting {
 
-void LogAndCleanupCrashDatabase() {
-#if BUILDFLAG(IS_LINUX)
-  CrashpadLinux::GetInstance().LogAndCleanupCrashpadDatabase();
-#endif  // BUILDFLAG(IS_LINUX)
-}
-
 // Not implemented for Mac, see https://crbug.com/714714
-void InitializeCrashReporting() {
+void InitializeBreakpadReporting() {
   // Touch the object to make sure it is initialized.
 #if BUILDFLAG(IS_WIN)
   BreakpadWin::GetInstance().Initialize();
 #endif  // BUILDFLAG(IS_WIN)
-
-#if BUILDFLAG(IS_LINUX)
-  CrashpadLinux::GetInstance().Initialize();
-#endif  // BUILDFLAG(IS_LINUX)
 }
 
 #if BUILDFLAG(IS_WIN)
diff --git a/remoting/base/crash/crash_reporting.h b/remoting/base/crash/crash_reporting_breakpad.h
similarity index 80%
rename from remoting/base/crash/crash_reporting.h
rename to remoting/base/crash/crash_reporting_breakpad.h
index e26895d..9d66258 100644
--- a/remoting/base/crash/crash_reporting.h
+++ b/remoting/base/crash/crash_reporting_breakpad.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef REMOTING_BASE_CRASH_CRASH_REPORTING_H_
-#define REMOTING_BASE_CRASH_CRASH_REPORTING_H_
+#ifndef REMOTING_BASE_CRASH_CRASH_REPORTING_BREAKPAD_H_
+#define REMOTING_BASE_CRASH_CRASH_REPORTING_BREAKPAD_H_
 
 #include <string>
 
@@ -12,11 +12,7 @@
 
 namespace remoting {
 
-// Query and log the entries in the crash database.
-// This will also verify that crash entries were uploaded and will clean up
-// old entries to help reduce disk space usage.
-void LogAndCleanupCrashDatabase();
-
+#if BUILDFLAG(IS_WIN)
 // Initializes collection and upload of crash reports. The caller has to ensure
 // that the user has agreed to crash dump reporting.
 //
@@ -26,9 +22,8 @@
 // be caught and reported. This should not be a problem as static non-POD
 // objects are not allowed by the style guide and exceptions to this rule are
 // rare.
-void InitializeCrashReporting();
+void InitializeBreakpadReporting();
 
-#if BUILDFLAG(IS_WIN)
 // Initializes a client for out-of-process (OOP) crash reporting using the
 // server process which owns the pipe referenced by |crash_server_pipe_handle|.
 // This is used for processes which do not have permission to write to the
@@ -44,4 +39,4 @@
 
 }  // namespace remoting
 
-#endif  // REMOTING_BASE_CRASH_CRASH_REPORTING_H_
+#endif  // REMOTING_BASE_CRASH_CRASH_REPORTING_BREAKPAD_H_
diff --git a/remoting/base/crash/crash_reporting.cc b/remoting/base/crash/crash_reporting_crashpad.cc
similarity index 65%
rename from remoting/base/crash/crash_reporting.cc
rename to remoting/base/crash/crash_reporting_crashpad.cc
index 73f7e651..572c0609 100644
--- a/remoting/base/crash/crash_reporting.cc
+++ b/remoting/base/crash/crash_reporting_crashpad.cc
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "remoting/base/crash/crash_reporting.h"
+#include "remoting/base/crash/crash_reporting_crashpad.h"
 
 #if BUILDFLAG(IS_WIN)
-#include "remoting/base/crash/breakpad_win.h"
+#include "remoting/base/crash/crashpad_win.h"
 #endif  // BUILDFLAG(IS_WIN)
 
 #if BUILDFLAG(IS_LINUX)
@@ -21,10 +21,10 @@
 }
 
 // Not implemented for Mac, see https://crbug.com/714714
-void InitializeCrashReporting() {
+void InitializeCrashpadReporting() {
   // Touch the object to make sure it is initialized.
 #if BUILDFLAG(IS_WIN)
-  BreakpadWin::GetInstance().Initialize();
+  CrashpadWin::GetInstance().Initialize();
 #endif  // BUILDFLAG(IS_WIN)
 
 #if BUILDFLAG(IS_LINUX)
@@ -32,11 +32,4 @@
 #endif  // BUILDFLAG(IS_LINUX)
 }
 
-#if BUILDFLAG(IS_WIN)
-void InitializeOopCrashClient(const std::string& server_pipe_handle) {
-  // Touch the object to make sure it is initialized.
-  BreakpadWin::GetInstance().Initialize(server_pipe_handle);
-}
-#endif  // BUILDFLAG(IS_WIN)
-
 }  // namespace remoting
diff --git a/remoting/base/crash/crash_reporting_crashpad.h b/remoting/base/crash/crash_reporting_crashpad.h
new file mode 100644
index 0000000..b1e8fe8
--- /dev/null
+++ b/remoting/base/crash/crash_reporting_crashpad.h
@@ -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.
+
+#ifndef REMOTING_BASE_CRASH_CRASH_REPORTING_CRASHPAD_H_
+#define REMOTING_BASE_CRASH_CRASH_REPORTING_CRASHPAD_H_
+
+#include "build/build_config.h"
+#include "build/buildflag.h"
+
+namespace remoting {
+
+// Query and log the entries in the crash database.
+// This will also verify that crash entries were uploaded and will clean up
+// old entries to help reduce disk space usage.
+void LogAndCleanupCrashDatabase();
+
+// Initializes collection and upload of crash reports. The caller has to ensure
+// that the user has agreed to crash dump reporting.
+//
+// Crash reporting has to be initialized as early as possible (e.g. the first
+// thing in main()) to catch crashes occurring during process startup.
+// Crashes which occur during the global static construction phase will not
+// be caught and reported. This should not be a problem as static non-POD
+// objects are not allowed by the style guide and exceptions to this rule are
+// rare.
+void InitializeCrashpadReporting();
+
+}  // namespace remoting
+
+#endif  // REMOTING_BASE_CRASH_CRASH_REPORTING_CRASHPAD_H_
diff --git a/remoting/base/crash/crashpad.h b/remoting/base/crash/crashpad.h
deleted file mode 100644
index 437a8928..0000000
--- a/remoting/base/crash/crashpad.h
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2024 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef REMOTING_BASE_CRASH_CRASHPAD_H_
-#define REMOTING_BASE_CRASH_CRASHPAD_H_
-
-namespace remoting {
-
-// Initializes collection and upload of crash reports. This will only be
-// called if the user has already opted in to having their crash dumps
-// uploaded.
-//
-// Crash reporting has to be initialized as early as possible (e.g. the first
-// thing in main()) to catch crashes occurring during process startup.
-// Crashes which occur during the global static construction phase will not
-// be caught and reported. This should not be a problem as static non-POD
-// objects are not allowed by the style guide and exceptions to this rule are
-// rare.
-void InitializeCrashReporting();
-
-}  // namespace remoting
-
-#endif  // REMOTING_BASE_CRASH_CRASHPAD_H_
diff --git a/remoting/base/crash/crashpad_database_manager.cc b/remoting/base/crash/crashpad_database_manager.cc
index a8dbe0f..2839ffe 100644
--- a/remoting/base/crash/crashpad_database_manager.cc
+++ b/remoting/base/crash/crashpad_database_manager.cc
@@ -11,11 +11,17 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/i18n/time_formatting.h"
+#include "base/path_service.h"
 #include "base/strings/string_number_conversions.h"
 #include "remoting/base/file_path_util_linux.h"
 #include "third_party/crashpad/crashpad/client/crash_report_database.h"
 #include "third_party/crashpad/crashpad/client/settings.h"
 
+#if BUILDFLAG(IS_WIN)
+#include "base/base_paths.h"
+#include "base/strings/utf_string_conversions.h"
+#endif  // BUILDFLAG(IS_WIN)
+
 namespace {
 
 const base::FilePath::CharType kChromotingCrashpadDatabasePath[] =
@@ -38,7 +44,12 @@
 namespace remoting {
 
 base::FilePath GetCrashpadDatabasePath() {
-  base::FilePath database_path = GetConfigDirectoryPath();
+  base::FilePath database_path;
+#if BUILDFLAG(IS_WIN)
+  base::PathService::Get(base::BasePathKey::DIR_ASSETS, &database_path);
+#else
+  database_path = GetConfigDirectoryPath();
+#endif
   return database_path.Append(kChromotingCrashpadDatabasePath);
 }
 
@@ -51,8 +62,13 @@
   base::FilePath database_path = GetCrashpadDatabasePath();
   base::File::Error error;
   if (!base::CreateDirectoryAndGetError(database_path, &error)) {
+#if BUILDFLAG(IS_WIN)
+    logger_->LogError("Unable to get directory for crash database: " +
+                      base::WideToUTF8(database_path.value()));
+#else
     logger_->LogError("Unable to get directory for crash database: " +
                       database_path.value());
+#endif
     logger_->LogError("File Error: " + base::File::ErrorToString(error));
     return false;
   }
@@ -154,7 +170,11 @@
   } else {
     logger_->Log("  Crash id: " + id + " (http://go/crash/" + id + ")");
   }
+#if BUILDFLAG(IS_WIN)
+  logger_->Log("    path: " + base::WideToUTF8(report.file_path.value()));
+#else
   logger_->Log("    path: " + report.file_path.value());
+#endif
   logger_->Log("    uuid: " + report.uuid.ToString());
   logger_->Log("    created: " +
                TimeFormatHTTP(base::Time::FromTimeT(report.creation_time)));
diff --git a/remoting/base/crash/crashpad_win.cc b/remoting/base/crash/crashpad_win.cc
new file mode 100644
index 0000000..29c7eb7
--- /dev/null
+++ b/remoting/base/crash/crashpad_win.cc
@@ -0,0 +1,104 @@
+// 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 "remoting/base/crash/crashpad_win.h"
+
+#include "base/base_paths.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/logging.h"
+#include "base/no_destructor.h"
+#include "base/path_service.h"
+#include "base/time/time.h"
+#include "remoting/base/crash/crashpad_database_manager.h"
+#include "remoting/base/logging.h"
+#include "remoting/base/version.h"
+#include "third_party/crashpad/crashpad/client/crash_report_database.h"
+#include "third_party/crashpad/crashpad/client/crashpad_client.h"
+
+namespace remoting {
+
+constexpr wchar_t kChromotingCrashpadHandler[] = L"crashpad-handler";
+constexpr char kDefaultCrashpadUploadUrl[] =
+    "https://clients2.google.com/cr/report";
+
+CrashpadWin::CrashpadWin() : database_(*this) {}
+
+bool CrashpadWin::Initialize() {
+  if (!database_.InitializeCrashpadDatabase()) {
+    LOG(ERROR) << "Failed to initialize database for Crashpad";
+    return false;
+  }
+
+  // We only initialize crash handling if the user has consented to record and
+  // upload reports, so we can simply enable it here.
+  if (!database_.EnableReportUploads()) {
+    LOG(WARNING) << "Unable to enable Crashpad uploads.";
+  }
+
+  // Leave metrics_path empty because this option is not used (or supported) on
+  // non-Chromium builds.
+  base::FilePath metrics_path;
+
+  std::map<std::string, std::string> annotations;
+  annotations["prod"] = "Chromoting_Win";
+  annotations["ver"] = REMOTING_VERSION_STRING;
+  annotations["plat"] = std::string("Windows");
+
+  std::vector<std::string> arguments;
+  // Make sure Crashpad's generate_dump tool includes monitor-self annotations.
+  // This creates a second crashpad instance that monitors the handler so it can
+  // report crashes in the handler.
+  arguments.push_back("--monitor-self-annotation=ptype=crashpad-handler");
+
+  base::FilePath handler_path;
+  if (!GetCrashpadHandlerPath(&handler_path)) {
+    return false;
+  }
+
+  crashpad::CrashpadClient client;
+  if (!client.StartHandler(handler_path, GetCrashpadDatabasePath(),
+                           metrics_path, kDefaultCrashpadUploadUrl, annotations,
+                           arguments, false, false)) {
+    LOG(ERROR) << "Failed to start Crashpad handler.";
+    return false;
+  }
+
+  HOST_LOG << "Crashpad handler started.";
+  return true;
+}
+
+void CrashpadWin::LogAndCleanupCrashpadDatabase() {
+  database_.LogCompletedCrashpadReports();
+  database_.LogPendingCrashpadReports();
+  database_.CleanupCompletedCrashpadReports();
+}
+
+// static
+CrashpadWin& CrashpadWin::GetInstance() {
+  static base::NoDestructor<CrashpadWin> instance;
+  return *instance;
+}
+
+// private
+
+// CrashpadDatabaseManager::Logger overrides
+void CrashpadWin::Log(const std::string message) const {
+  HOST_LOG << message;
+}
+
+void CrashpadWin::LogError(const std::string message) const {
+  LOG(ERROR) << message;
+}
+
+bool CrashpadWin::GetCrashpadHandlerPath(base::FilePath* handler_path) {
+  if (!base::PathService::Get(base::DIR_EXE, handler_path)) {
+    LOG(ERROR) << "Unable to get exe dir for crashpad handler";
+    return false;
+  }
+  *handler_path = handler_path->Append(kChromotingCrashpadHandler);
+  return true;
+}
+
+}  // namespace remoting
diff --git a/remoting/base/crash/crashpad_win.h b/remoting/base/crash/crashpad_win.h
new file mode 100644
index 0000000..d450853
--- /dev/null
+++ b/remoting/base/crash/crashpad_win.h
@@ -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.
+
+#ifndef REMOTING_BASE_CRASH_CRASHPAD_WIN_H_
+#define REMOTING_BASE_CRASH_CRASHPAD_WIN_H_
+
+#include "base/files/file_path.h"
+#include "remoting/base/crash/crashpad_database_manager.h"
+
+namespace remoting {
+
+class CrashpadWin : CrashpadDatabaseManager::Logger {
+ public:
+  CrashpadWin();
+
+  CrashpadWin(const CrashpadWin&) = delete;
+  CrashpadWin& operator=(const CrashpadWin&) = delete;
+
+  bool Initialize();
+  void LogAndCleanupCrashpadDatabase();
+
+  static CrashpadWin& GetInstance();
+
+ private:
+  bool GetCrashpadHandlerPath(base::FilePath* handler_path);
+
+  // CrashpadDatabaseManager::Logger overrides
+  void Log(std::string message) const override;
+  void LogError(std::string message) const override;
+
+  remoting::CrashpadDatabaseManager database_;
+};
+
+}  // namespace remoting
+
+#endif  // REMOTING_BASE_CRASH_CRASHPAD_WIN_H_
diff --git a/remoting/build/config/BUILD.gn b/remoting/build/config/BUILD.gn
index 1d840d30..8760d21 100644
--- a/remoting/build/config/BUILD.gn
+++ b/remoting/build/config/BUILD.gn
@@ -46,9 +46,6 @@
     if (!is_mac && is_chrome_branded && is_official_build) {
       defines += [ "REMOTING_ENABLE_CRASH_REPORTING" ]
     }
-    if (enable_chromoting_crashpad) {
-      defines += [ "REMOTING_ENABLE_CRASHPAD" ]
-    }
   }
   if (is_win) {
     defines += host_predefines
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn
index 6b65802..13fb0cc 100644
--- a/remoting/host/BUILD.gn
+++ b/remoting/host/BUILD.gn
@@ -1206,13 +1206,13 @@
         "//remoting/host/mac:host_service_main",
       ]
     } else {
-      deps += [
-        "//remoting/host/crash:crash_uploader_main",
-        "//remoting/host/remote_open_url:main",
-      ]
+      deps += [ "//remoting/host/remote_open_url:main" ]
     }
     if (is_win) {
-      deps += [ "//remoting/host/security_key:main" ]
+      deps += [
+        "//remoting/host/crash:crash_uploader_main",
+        "//remoting/host/security_key:main",
+      ]
     }
   }
 
diff --git a/remoting/host/crash/BUILD.gn b/remoting/host/crash/BUILD.gn
index 7114685d..eb9dbd4d 100644
--- a/remoting/host/crash/BUILD.gn
+++ b/remoting/host/crash/BUILD.gn
@@ -15,48 +15,6 @@
   }
 }
 
-source_set("crash_uploader") {
-  sources = [
-    "crash_directory_watcher.cc",
-    "crash_directory_watcher.h",
-    "crash_file_uploader.cc",
-    "crash_file_uploader.h",
-    "minidump_handler.cc",
-    "minidump_handler.h",
-  ]
-  deps = [
-    "//base",
-    "//remoting/base",
-    "//remoting/base/crash",
-    "//remoting/base/crash:breakpad_utils",
-    "//remoting/host:common_headers",
-    "//remoting/host/base",
-    "//services/network:network_service",
-    "//services/network/public/cpp",
-  ]
-}
-
-source_set("crash_uploader_main_headers") {
-  sources = [ "crash_uploader_main.h" ]
-  public_deps = [ "//remoting/host:host_main_headers" ]
-}
-
-source_set("crash_uploader_main") {
-  configs += [ "//remoting/build/config:host_implementation" ]
-
-  sources = [ "crash_uploader_main.cc" ]
-  deps = [
-    ":crash_uploader",
-    ":crash_uploader_main_headers",
-    "//base",
-    "//mojo/core/embedder:embedder",
-    "//remoting/base",
-    "//remoting/base/crash",
-    "//remoting/host:common_headers",
-    "//remoting/host/base",
-  ]
-}
-
 if (is_linux || is_win) {
   executable("remoting_crashpad_handler") {
     sources = [ "crashpad_handler.cc" ]
@@ -91,6 +49,48 @@
 }
 
 if (is_win) {
+  source_set("crash_uploader") {
+    sources = [
+      "crash_directory_watcher.cc",
+      "crash_directory_watcher.h",
+      "crash_file_uploader.cc",
+      "crash_file_uploader.h",
+      "minidump_handler.cc",
+      "minidump_handler.h",
+    ]
+    deps = [
+      "//base",
+      "//remoting/base",
+      "//remoting/base/crash",
+      "//remoting/base/crash:breakpad_utils",
+      "//remoting/host:common_headers",
+      "//remoting/host/base",
+      "//services/network:network_service",
+      "//services/network/public/cpp",
+    ]
+  }
+
+  source_set("crash_uploader_main_headers") {
+    sources = [ "crash_uploader_main.h" ]
+    public_deps = [ "//remoting/host:host_main_headers" ]
+  }
+
+  source_set("crash_uploader_main") {
+    configs += [ "//remoting/build/config:host_implementation" ]
+
+    sources = [ "crash_uploader_main.cc" ]
+    deps = [
+      ":crash_uploader",
+      ":crash_uploader_main_headers",
+      "//base",
+      "//mojo/core/embedder:embedder",
+      "//remoting/base",
+      "//remoting/base/crash",
+      "//remoting/host:common_headers",
+      "//remoting/host/base",
+    ]
+  }
+
   executable("remoting_crash_uploader") {
     sources = [ "crash_uploader_entry_point.cc" ]
 
diff --git a/remoting/host/daemon_process_win.cc b/remoting/host/daemon_process_win.cc
index a166b7d..bcee097c 100644
--- a/remoting/host/daemon_process_win.cc
+++ b/remoting/host/daemon_process_win.cc
@@ -35,7 +35,7 @@
 #include "mojo/public/cpp/system/message_pipe.h"
 #include "remoting/base/auto_thread.h"
 #include "remoting/base/auto_thread_task_runner.h"
-#include "remoting/base/crash/crash_reporting.h"
+#include "remoting/base/crash/crash_reporting_breakpad.h"
 #include "remoting/base/logging.h"
 #include "remoting/base/scoped_sc_handle_win.h"
 #include "remoting/host/base/host_exit_codes.h"
diff --git a/remoting/host/host_main.cc b/remoting/host/host_main.cc
index a0012bb..9408862e 100644
--- a/remoting/host/host_main.cc
+++ b/remoting/host/host_main.cc
@@ -24,7 +24,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
 #include "mojo/core/embedder/embedder.h"
-#include "remoting/base/crash/crash_reporting.h"
+#include "remoting/base/crash/crash_reporting_crashpad.h"
 #include "remoting/base/logging.h"
 #include "remoting/host/base/host_exit_codes.h"
 #include "remoting/host/base/switches.h"
@@ -42,6 +42,8 @@
 
 #include <commctrl.h>
 #include <shellapi.h>
+
+#include "remoting/base/crash/crash_reporting_breakpad.h"
 #endif  // BUILDFLAG(IS_WIN)
 
 namespace remoting {
@@ -230,12 +232,14 @@
   // the crash reports uploaded.
   if (IsUsageStatsAllowed()) {
 #if BUILDFLAG(IS_LINUX)
-    InitializeCrashReporting();
+    InitializeCrashpadReporting();
 #elif BUILDFLAG(IS_WIN)
     // TODO: joedow - Enable crash reporting for the RDP process.
-    if (process_type == kProcessTypeDesktop ||
-        process_type == kProcessTypeDaemon) {
-      InitializeCrashReporting();
+    if (process_type == kProcessTypeDaemon) {
+      InitializeBreakpadReporting();
+    } else if (process_type == kProcessTypeDesktop) {
+      // TODO(garykac): Switch to use InitializeCrashpadReporting();
+      InitializeBreakpadReporting();
     } else if (command_line->HasSwitch(kCrashServerPipeHandle)) {
       InitializeOopCrashClient(
           command_line->GetSwitchValueASCII(kCrashServerPipeHandle));
diff --git a/remoting/host/it2me/it2me_native_messaging_host_main.cc b/remoting/host/it2me/it2me_native_messaging_host_main.cc
index 1a9ddba..209f773 100644
--- a/remoting/host/it2me/it2me_native_messaging_host_main.cc
+++ b/remoting/host/it2me/it2me_native_messaging_host_main.cc
@@ -17,7 +17,6 @@
 #include "mojo/core/embedder/embedder.h"
 #include "net/base/network_change_notifier.h"
 #include "remoting/base/auto_thread_task_runner.h"
-#include "remoting/base/crash/crash_reporting.h"
 #include "remoting/base/host_settings.h"
 #include "remoting/base/logging.h"
 #include "remoting/host/base/host_exit_codes.h"
@@ -44,10 +43,15 @@
 #include "remoting/host/mac/permission_utils.h"
 #endif  // BUILDFLAG(IS_APPLE)
 
+#if BUILDFLAG(IS_LINUX)
+#include "remoting/base/crash/crash_reporting_crashpad.h"
+#endif  // BUILDFLAG(IS_LINUX)
+
 #if BUILDFLAG(IS_WIN)
 #include <windows.h>
 
 #include <commctrl.h>
+#include "remoting/base/crash/crash_reporting_breakpad.h"
 #endif  // BUILDFLAG(IS_WIN)
 
 namespace remoting {
@@ -100,7 +104,11 @@
   // needs to be initialized first, so that the preference for crash-reporting
   // can be looked up in the config file.
   if (IsUsageStatsAllowed()) {
-    InitializeCrashReporting();
+#if BUILDFLAG(IS_LINUX)
+    InitializeCrashpadReporting();
+#elif BUILDFLAG(IS_WIN)
+    InitializeBreakpadReporting();
+#endif  // BUILDFLAG(IS_LINUX)
   }
 #endif  // defined(REMOTING_ENABLE_CRASH_REPORTING)
 
diff --git a/remoting/host/remote_open_url/remote_open_url_main.cc b/remoting/host/remote_open_url/remote_open_url_main.cc
index 29ee547d..89a8838f 100644
--- a/remoting/host/remote_open_url/remote_open_url_main.cc
+++ b/remoting/host/remote_open_url/remote_open_url_main.cc
@@ -18,7 +18,6 @@
 #include "base/task/single_thread_task_runner.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
-#include "remoting/base/crash/crash_reporting.h"
 #include "remoting/base/host_settings.h"
 #include "remoting/base/logging.h"
 #include "remoting/host/base/host_exit_codes.h"
@@ -28,6 +27,14 @@
 #include "remoting/host/usage_stats_consent.h"
 #include "ui/base/l10n/l10n_util.h"
 
+#if BUILDFLAG(IS_LINUX)
+#include "remoting/base/crash/crash_reporting_crashpad.h"
+#endif  // BUILDFLAG(IS_LINUX)
+
+#if BUILDFLAG(IS_WIN)
+#include "remoting/base/crash/crash_reporting_breakpad.h"
+#endif  // BUILDFLAG(IS_WIN)
+
 namespace remoting {
 
 int RemoteOpenUrlMain(int argc, char** argv) {
@@ -44,7 +51,11 @@
 
 #if defined(REMOTING_ENABLE_CRASH_REPORTING)
   if (IsUsageStatsAllowed()) {
-    InitializeCrashReporting();
+#if BUILDFLAG(IS_LINUX)
+    InitializeCrashpadReporting();
+#elif BUILDFLAG(IS_WIN)
+    InitializeBreakpadReporting();
+#endif  // BUILDFLAG(IS_LINUX)
   }
 #endif  // defined(REMOTING_ENABLE_CRASH_REPORTING)
 
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index 1fe53f8..d39e13b7 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -56,7 +56,6 @@
 #include "remoting/base/cloud_session_authz_service_client_factory.h"
 #include "remoting/base/corp_session_authz_service_client_factory.h"
 #include "remoting/base/cpu_utils.h"
-#include "remoting/base/crash/crash_reporting.h"
 #include "remoting/base/errors.h"
 #include "remoting/base/host_settings.h"
 #include "remoting/base/instance_identity_token_getter.h"
@@ -166,6 +165,7 @@
 #endif  // BUILDFLAG(IS_WIN)
 
 #if BUILDFLAG(IS_LINUX)
+#include "remoting/base/crash/crash_reporting_crashpad.h"
 #include "remoting/host/host_wtmpdb_logger.h"
 #endif  // BUILDFLAG(IS_LINUX)
 
@@ -2154,10 +2154,12 @@
     return kInitializationFailed;
   }
 
-#if defined(REMOTING_ENABLE_CRASH_REPORTING)
+#if BUILDFLAG(IS_LINUX)
   // Log and cleanup the crash database. We do this after a short delay so that
   // the crash database has a chance to be updated properly if we just got
   // relaunched after a crash.
+  // TODO(garykac): When Crashpad is enabled for the network process on Windows
+  // we will need to enable this code on Windows as well.
   if (IsUsageStatsAllowed()) {
     scoped_refptr<base::SequencedTaskRunner> task_runner_crashdb =
         base::ThreadPool::CreateSequencedTaskRunner(
diff --git a/remoting/host/security_key/remote_security_key_main.cc b/remoting/host/security_key/remote_security_key_main.cc
index f782077..1b39ccc 100644
--- a/remoting/host/security_key/remote_security_key_main.cc
+++ b/remoting/host/security_key/remote_security_key_main.cc
@@ -17,7 +17,6 @@
 #include "build/build_config.h"
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
-#include "remoting/base/crash/crash_reporting.h"
 #include "remoting/base/logging.h"
 #include "remoting/host/base/host_exit_codes.h"
 #include "remoting/host/chromoting_host_services_client.h"
@@ -25,9 +24,14 @@
 #include "remoting/host/security_key/security_key_message_handler.h"
 #include "remoting/host/usage_stats_consent.h"
 
+#if BUILDFLAG(IS_LINUX)
+#include "remoting/base/crash/crash_reporting_crashpad.h"
+#endif  // BUILDFLAG(IS_LINUX)
+
 #if BUILDFLAG(IS_WIN)
 #include <windows.h>
 
+#include "remoting/base/crash/crash_reporting_breakpad.h"
 #include "remoting/host/win/acl_util.h"
 #endif  // BUILDFLAG(IS_WIN)
 
@@ -92,7 +96,11 @@
 
 #if defined(REMOTING_ENABLE_CRASH_REPORTING)
   if (IsUsageStatsAllowed()) {
-    InitializeCrashReporting();
+#if BUILDFLAG(IS_LINUX)
+    InitializeCrashpadReporting();
+#elif BUILDFLAG(IS_WIN)
+    InitializeBreakpadReporting();
+#endif  // BUILDFLAG(IS_LINUX)
   }
 #endif  // defined(REMOTING_ENABLE_CRASH_REPORTING)
 
diff --git a/remoting/host/setup/me2me_native_messaging_host_main.cc b/remoting/host/setup/me2me_native_messaging_host_main.cc
index b24d266..4a7e34c 100644
--- a/remoting/host/setup/me2me_native_messaging_host_main.cc
+++ b/remoting/host/setup/me2me_native_messaging_host_main.cc
@@ -23,7 +23,6 @@
 #include "build/build_config.h"
 #include "mojo/core/embedder/embedder.h"
 #include "remoting/base/auto_thread_task_runner.h"
-#include "remoting/base/crash/crash_reporting.h"
 #include "remoting/base/gaia_oauth_client.h"
 #include "remoting/base/logging.h"
 #include "remoting/base/url_request_context_getter.h"
@@ -42,11 +41,16 @@
 #include "base/apple/scoped_nsautorelease_pool.h"
 #endif  // BUILDFLAG(IS_APPLE)
 
+#if BUILDFLAG(IS_LINUX)
+#include "remoting/base/crash/crash_reporting_crashpad.h"
+#endif  // BUILDFLAG(IS_LINUX)
+
 #if BUILDFLAG(IS_WIN)
 #include <windows.h>
 
 #include "base/process/process_info.h"
 #include "base/win/registry.h"
+#include "remoting/base/crash/crash_reporting_breakpad.h"
 #include "remoting/host/pairing_registry_delegate_win.h"
 #endif  // BUILDFLAG(IS_WIN)
 
@@ -95,7 +99,11 @@
   // needs to be initialized first, so that the preference for crash-reporting
   // can be looked up in the config file.
   if (IsUsageStatsAllowed()) {
-    InitializeCrashReporting();
+#if BUILDFLAG(IS_LINUX)
+    InitializeCrashpadReporting();
+#elif BUILDFLAG(IS_WIN)
+    InitializeBreakpadReporting();
+#endif  // BUILDFLAG(IS_LINUX)
   }
 #endif  // defined(REMOTING_ENABLE_CRASH_REPORTING)
 
diff --git a/remoting/host/setup/start_host_main.cc b/remoting/host/setup/start_host_main.cc
index e70f00f..028002ed 100644
--- a/remoting/host/setup/start_host_main.cc
+++ b/remoting/host/setup/start_host_main.cc
@@ -29,7 +29,6 @@
 #include "net/ssl/client_cert_store.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "remoting/base/certificate_helpers.h"
-#include "remoting/base/crash/crash_reporting.h"
 #include "remoting/base/logging.h"
 #include "remoting/base/url_request_context_getter.h"
 #include "remoting/host/setup/cloud_host_starter.h"
@@ -46,6 +45,7 @@
 #endif  // BUILDFLAG(IS_POSIX)
 
 #if BUILDFLAG(IS_LINUX)
+#include "remoting/base/crash/crash_reporting_crashpad.h"
 #include "remoting/host/setup/daemon_controller_delegate_linux.h"
 #include "remoting/host/setup/start_host_as_root.h"
 #endif  // BUILDFLAG(IS_LINUX)
@@ -54,6 +54,7 @@
 #include <windows.h>
 
 #include "base/process/process_info.h"
+#include "remoting/base/crash/crash_reporting_breakpad.h"
 #endif  // BUILDFLAG(IS_WIN)
 
 namespace remoting {
@@ -444,7 +445,11 @@
   // We don't have a config file yet so we can't use IsUsageStatsAllowed(),
   // instead we can just check the command line parameter.
   if (params.enable_crash_reporting) {
-    InitializeCrashReporting();
+#if BUILDFLAG(IS_LINUX)
+    InitializeCrashpadReporting();
+#elif BUILDFLAG(IS_WIN)
+    InitializeBreakpadReporting();
+#endif  // BUILDFLAG(IS_LINUX)
   }
 #endif  // defined(REMOTING_ENABLE_CRASH_REPORTING)
 
diff --git a/remoting/host/webauthn/remote_webauthn_main.cc b/remoting/host/webauthn/remote_webauthn_main.cc
index 8c5d920e..bfd2319b 100644
--- a/remoting/host/webauthn/remote_webauthn_main.cc
+++ b/remoting/host/webauthn/remote_webauthn_main.cc
@@ -18,7 +18,6 @@
 #include "mojo/core/embedder/embedder.h"
 #include "mojo/core/embedder/scoped_ipc_support.h"
 #include "remoting/base/auto_thread_task_runner.h"
-#include "remoting/base/crash/crash_reporting.h"
 #include "remoting/base/logging.h"
 #include "remoting/host/base/host_exit_codes.h"
 #include "remoting/host/chromoting_host_services_client.h"
@@ -28,8 +27,14 @@
 #include "remoting/host/webauthn/remote_webauthn_caller_security_utils.h"
 #include "remoting/host/webauthn/remote_webauthn_native_messaging_host.h"
 
+#if BUILDFLAG(IS_LINUX)
+#include "remoting/base/crash/crash_reporting_crashpad.h"
+#endif  // BUILDFLAG(IS_LINUX)
+
 #if BUILDFLAG(IS_WIN)
 #include <windows.h>
+
+#include "remoting/base/crash/crash_reporting_breakpad.h"
 #endif  // BUILDFLAG(IS_WIN)
 
 namespace remoting {
@@ -45,7 +50,11 @@
 
 #if defined(REMOTING_ENABLE_CRASH_REPORTING)
   if (IsUsageStatsAllowed()) {
-    InitializeCrashReporting();
+#if BUILDFLAG(IS_LINUX)
+    InitializeCrashpadReporting();
+#elif BUILDFLAG(IS_WIN)
+    InitializeBreakpadReporting();
+#endif  // BUILDFLAG(IS_LINUX)
   }
 #endif  // defined(REMOTING_ENABLE_CRASH_REPORTING)
 
diff --git a/sandbox/policy/win/sandbox_warmup.cc b/sandbox/policy/win/sandbox_warmup.cc
index 97d4e5a..4cfcf6e6 100644
--- a/sandbox/policy/win/sandbox_warmup.cc
+++ b/sandbox/policy/win/sandbox_warmup.cc
@@ -6,15 +6,10 @@
 
 #include <windows.h>
 
-#include "base/check_op.h"
+#include "base/check.h"
 #include "base/no_destructor.h"
 #include "sandbox/policy/win/hook_util/hook_util.h"
-
-// Prototype for ProcessPrng.
-// See: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng
-extern "C" {
-BOOL WINAPI ProcessPrng(PBYTE pbData, SIZE_T cbData);
-}
+#include "sandbox/win/src/win_utils.h"
 
 namespace sandbox::policy {
 
@@ -28,27 +23,10 @@
   return g_user_default_lcid;
 }
 
-// Import bcryptprimitives!ProcessPrng rather than cryptbase!RtlGenRandom to
-// avoid opening a handle to \\Device\KsecDD in the renderer.
-decltype(&ProcessPrng) GetProcessPrng() {
-  HMODULE hmod = LoadLibraryW(L"bcryptprimitives.dll");
-  CHECK(hmod);
-  decltype(&ProcessPrng) process_prng_fn =
-      reinterpret_cast<decltype(&ProcessPrng)>(
-          GetProcAddress(hmod, "ProcessPrng"));
-  CHECK(process_prng_fn);
-  return process_prng_fn;
-}
-
 }  // namespace
 
 void WarmupRandomnessInfrastructure() {
-  BYTE data[1];
-  // TODO(crbug.com/40088338) Call a warmup function exposed by boringssl.
-  static decltype(&ProcessPrng) process_prng_fn = GetProcessPrng();
-  BOOL success = process_prng_fn(data, sizeof(data));
-  // ProcessPrng is documented to always return TRUE.
-  CHECK(success);
+  sandbox::WarmupRandomnessInfrastructure();
 }
 
 bool HookDwriteGetUserDefaultLCID() {
diff --git a/sandbox/win/src/process_mitigations_unittest.cc b/sandbox/win/src/process_mitigations_unittest.cc
index c3a6d378..141fdb9 100644
--- a/sandbox/win/src/process_mitigations_unittest.cc
+++ b/sandbox/win/src/process_mitigations_unittest.cc
@@ -517,6 +517,9 @@
                   ->RevertedToSelf()) {
     // Need to warm up gdi32.dll for the test.
     CHECK(::LoadLibrary(L"gdi32.dll"));
+
+    // Need to warm up random for this test.
+    sandbox::WarmupRandomnessInfrastructure();
     return 0;
   }
 
diff --git a/sandbox/win/src/win_utils.cc b/sandbox/win/src/win_utils.cc
index 6ebaf2c..7c5923c 100644
--- a/sandbox/win/src/win_utils.cc
+++ b/sandbox/win/src/win_utils.cc
@@ -22,6 +22,7 @@
 #include <string>
 #include <vector>
 
+#include "base/check.h"
 #include "base/containers/span.h"
 #include "base/numerics/safe_math.h"
 #include "base/strings/string_util.h"
@@ -31,6 +32,12 @@
 #include "sandbox/win/src/nt_internals.h"
 #include "sandbox/win/src/sandbox_nt_util.h"
 
+// Prototype for ProcessPrng.
+// See: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng
+extern "C" {
+BOOL WINAPI ProcessPrng(PBYTE pbData, SIZE_T cbData);
+}
+
 namespace {
 
 NTSTATUS WrapQueryObject(HANDLE handle,
@@ -71,6 +78,18 @@
   return data;
 }
 
+// Import bcryptprimitives!ProcessPrng rather than cryptbase!RtlGenRandom to
+// avoid opening a handle to \\Device\KsecDD in the renderer.
+decltype(&ProcessPrng) GetProcessPrng() {
+  HMODULE hmod = LoadLibraryW(L"bcryptprimitives.dll");
+  CHECK(hmod);
+  decltype(&ProcessPrng) process_prng_fn =
+      reinterpret_cast<decltype(&ProcessPrng)>(
+          GetProcAddress(hmod, "ProcessPrng"));
+  CHECK(process_prng_fn);
+  return process_prng_fn;
+}
+
 }  // namespace
 
 namespace sandbox {
@@ -183,4 +202,13 @@
   return str.find_first_of(nul) != std::wstring::npos;
 }
 
+void WarmupRandomnessInfrastructure() {
+  BYTE data[1];
+  // TODO(crbug.com/40088338) Call a warmup function exposed by boringssl.
+  static decltype(&ProcessPrng) process_prng_fn = GetProcessPrng();
+  BOOL success = process_prng_fn(data, sizeof(data));
+  // ProcessPrng is documented to always return TRUE.
+  CHECK(success);
+}
+
 }  // namespace sandbox
diff --git a/sandbox/win/src/win_utils.h b/sandbox/win/src/win_utils.h
index 1efecc2..525992b9 100644
--- a/sandbox/win/src/win_utils.h
+++ b/sandbox/win/src/win_utils.h
@@ -80,6 +80,10 @@
 // Returns true if the string contains a NUL ('\0') character.
 bool ContainsNulCharacter(std::wstring_view str);
 
+// Call in a sandboxed process before target lockdown where modules should be
+// pre-loaded to support the infrastructure underlying crypto::RandBytes.
+void WarmupRandomnessInfrastructure();
+
 }  // namespace sandbox
 
 #endif  // SANDBOX_WIN_SRC_WIN_UTILS_H_
diff --git a/services/image_annotation/BUILD.gn b/services/image_annotation/BUILD.gn
index a7298a7..3331cc89 100644
--- a/services/image_annotation/BUILD.gn
+++ b/services/image_annotation/BUILD.gn
@@ -20,7 +20,6 @@
     "//components/manta",
     "//mojo/public/cpp/bindings",
     "//net",
-    "//services/data_decoder/public/mojom",
     "//services/image_annotation/public/mojom",
     "//services/network/public/cpp",
     "//ui/accessibility:ax_base",
@@ -37,7 +36,6 @@
   public_deps = [
     ":lib",
     "//base",
-    "//services/data_decoder/public/mojom",
     "//services/image_annotation/public/mojom",
     "//services/network/public/cpp",
     "//url",
@@ -56,9 +54,6 @@
     "//components/manta:manta",
     "//mojo/public/cpp/bindings",
     "//net",
-    "//services/data_decoder/public/cpp",
-    "//services/data_decoder/public/cpp:test_support",
-    "//services/data_decoder/public/mojom",
     "//services/image_annotation/public/cpp",
     "//services/image_annotation/public/mojom",
     "//services/network:test_support",
diff --git a/services/image_annotation/DEPS b/services/image_annotation/DEPS
index efd0fde..78396ac 100644
--- a/services/image_annotation/DEPS
+++ b/services/image_annotation/DEPS
@@ -2,7 +2,6 @@
   "+components/google",
   "+components/manta",
   "+net",
-  "+services/data_decoder",
   "+services/network",
   "+third_party/skia",
   "+ui/accessibility",
diff --git a/services/image_annotation/annotator.cc b/services/image_annotation/annotator.cc
index 456de81..083438c 100644
--- a/services/image_annotation/annotator.cc
+++ b/services/image_annotation/annotator.cc
@@ -970,26 +970,20 @@
 
   ReportServerResponseSizeBytes(json_response->size());
 
-  // Send JSON string to a dedicated service for safe parsing.
-  GetJsonParser()->Parse(
-      *json_response, base::JSON_PARSE_RFC,
-      base::BindOnce(&Annotator::OnResponseJsonParsed,
-                     weak_factory_.GetWeakPtr(), request_keys));
-}
+  base::JSONReader::Result result =
+      base::JSONReader::ReadAndReturnValueWithError(*json_response,
+                                                    base::JSON_PARSE_RFC);
 
-void Annotator::OnResponseJsonParsed(const std::set<RequestKey>& request_keys,
-                                     const std::optional<base::Value> json_data,
-                                     const std::optional<std::string>& error) {
-  const bool success = json_data.has_value() && !error.has_value();
+  const bool success = result.has_value();
   ReportJsonParseSuccess(success);
 
   // Extract annotation results for each request key with valid results.
   if (success) {
     ProcessResults(request_keys,
-                   UnpackJsonResponse(*json_data, min_ocr_confidence_));
+                   UnpackJsonResponse(*result, min_ocr_confidence_));
   } else {
     DVLOG(1) << "Parsing server response JSON failed with error: "
-             << error.value_or("No reason reported.");
+             << result.error().message;
     ProcessResults(request_keys, {});
   }
 }
@@ -1047,15 +1041,6 @@
   }
 }
 
-data_decoder::mojom::JsonParser* Annotator::GetJsonParser() {
-  if (!json_parser_) {
-    client_->BindJsonParser(json_parser_.BindNewPipeAndPassReceiver());
-    json_parser_.reset_on_disconnect();
-  }
-
-  return json_parser_.get();
-}
-
 void Annotator::RemoveRequestInfo(
     const RequestKey& request_key,
     const std::list<ClientRequestInfo>::iterator request_info_it,
@@ -1177,22 +1162,22 @@
     return;
   }
 
-  GetJsonParser()->Parse(
-      *json_response, base::JSON_PARSE_RFC,
-      base::BindOnce(&Annotator::OnServerLangsResponseJsonParsed,
-                     weak_factory_.GetWeakPtr()));
-}
+  base::JSONReader::Result result =
+      base::JSONReader::ReadAndReturnValueWithError(*json_response,
+                                                    base::JSON_PARSE_RFC);
 
-void Annotator::OnServerLangsResponseJsonParsed(
-    std::optional<base::Value> json_data,
-    const std::optional<std::string>& error) {
-  if (!json_data.has_value() || error.has_value()) {
+  if (!result.has_value()) {
     DVLOG(1) << "Parsing server langs response JSON failed with error: "
-             << error.value_or("No reason reported.");
+             << result.error().message;
     return;
   }
 
-  const base::Value::List* const langs = json_data->GetDict().FindList("langs");
+  if (!result->is_dict()) {
+    DVLOG(1) << "Server langs response JSON is not a dictionary.";
+    return;
+  }
+
+  const base::Value::List* const langs = result->GetDict().FindList("langs");
   if (!langs) {
     DVLOG(1) << "No langs in response JSON";
     return;
diff --git a/services/image_annotation/annotator.h b/services/image_annotation/annotator.h
index 92620da..07461a8a 100644
--- a/services/image_annotation/annotator.h
+++ b/services/image_annotation/annotator.h
@@ -24,7 +24,6 @@
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "services/data_decoder/public/mojom/json_parser.mojom.h"
 #include "services/image_annotation/public/mojom/image_annotation.mojom.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
@@ -49,9 +48,6 @@
    public:
     virtual ~Client() {}
 
-    virtual void BindJsonParser(
-        mojo::PendingReceiver<data_decoder::mojom::JsonParser> receiver) = 0;
-
     virtual std::vector<std::string> GetAcceptLanguages() = 0;
     virtual std::vector<std::string> GetTopLanguages() = 0;
     virtual void RecordLanguageMetrics(
@@ -198,10 +194,6 @@
       const GURL& server_url,
       const std::string& api_key);
 
-  // Create or reuse a connection to the data decoder service for safe JSON
-  // parsing.
-  data_decoder::mojom::JsonParser* GetJsonParser();
-
   // Removes the given request, reassigning local processing if its associated
   // image processor had some ongoing.
   void RemoveRequestInfo(const RequestKey& request_key,
@@ -256,10 +248,6 @@
   void OnServerLangsResponseReceived(
       const std::unique_ptr<std::string> json_response);
 
-  // Parse the JSON from the reply with server languages.
-  void OnServerLangsResponseJsonParsed(std::optional<base::Value> json_data,
-                                       const std::optional<std::string>& error);
-
   const std::unique_ptr<manta::AnchovyProvider> anchovy_provider_;
   const std::unique_ptr<Client> client_;
 
@@ -306,9 +294,6 @@
 
   mojo::ReceiverSet<mojom::Annotator> receivers_;
 
-  // Should not be used directly; GetJsonParser() should be called instead.
-  mojo::Remote<data_decoder::mojom::JsonParser> json_parser_;
-
   // A timer used to throttle server request frequency.
   std::unique_ptr<base::RepeatingTimer> server_request_timer_;
 
diff --git a/services/image_annotation/annotator_unittest.cc b/services/image_annotation/annotator_unittest.cc
index 7e9880a1..ef2c0c5 100644
--- a/services/image_annotation/annotator_unittest.cc
+++ b/services/image_annotation/annotator_unittest.cc
@@ -29,9 +29,6 @@
 #include "mojo/public/cpp/bindings/receiver_set.h"
 #include "net/base/net_errors.h"
 #include "net/http/http_status_code.h"
-#include "services/data_decoder/public/cpp/data_decoder.h"
-#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
-#include "services/data_decoder/public/mojom/json_parser.mojom.h"
 #include "services/image_annotation/annotator.h"
 #include "services/image_annotation/image_annotation_metrics.h"
 #include "services/image_annotation/public/mojom/image_annotation.mojom.h"
@@ -404,11 +401,6 @@
   }
 
  private:
-  // Annotator::Client implementation:
-  void BindJsonParser(mojo::PendingReceiver<data_decoder::mojom::JsonParser>
-                          receiver) override {
-    decoder_.GetService()->BindJsonParser(std::move(receiver));
-  }
   std::vector<std::string> GetAcceptLanguages() override {
     return accept_langs_;
   }
@@ -416,7 +408,6 @@
   void RecordLanguageMetrics(const std::string& page_language,
                              const std::string& requested_language) override {}
 
-  data_decoder::DataDecoder decoder_;
   std::vector<std::string> accept_langs_ = {"en", "it", "fr"};
   std::vector<std::string> top_langs_;
 };
@@ -429,7 +420,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -536,7 +526,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -644,7 +633,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -760,7 +748,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -817,7 +804,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -901,7 +887,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -996,7 +981,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -1087,7 +1071,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -1146,7 +1129,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -1222,7 +1204,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -1303,7 +1284,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -1373,7 +1353,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -1449,7 +1428,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -1537,7 +1515,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -1682,7 +1659,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -1791,7 +1767,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -1996,7 +1971,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -2195,7 +2169,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
   base::HistogramTester histogram_tester;
 
   Annotator annotator(
@@ -2295,7 +2268,6 @@
 TEST(AnnotatorTest, ApiKey) {
   base::test::TaskEnvironment test_task_env(
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
 
   // A call to a secure Google-owner server URL should include the specified API
   // key.
@@ -2470,7 +2442,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
 
   Annotator annotator(
       GURL(kTestServerUrl), GURL(kLangsServerUrl), std::string() /* api_key */,
@@ -2505,7 +2476,6 @@
       base::test::TaskEnvironment::TimeSource::MOCK_TIME);
   TestServerURLLoaderFactory test_url_factory(
       "https://ia-pa.googleapis.com/v1/");
-  data_decoder::test::InProcessDataDecoder in_process_data_decoder;
 
   Annotator annotator(
       GURL(kTestServerUrl), GURL(kLangsServerUrl), std::string() /* api_key */,
diff --git a/services/image_annotation/image_annotation_service.h b/services/image_annotation/image_annotation_service.h
index 4388429d..cfd9a208 100644
--- a/services/image_annotation/image_annotation_service.h
+++ b/services/image_annotation/image_annotation_service.h
@@ -13,7 +13,6 @@
 #include "base/metrics/field_trial_params.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
-#include "services/data_decoder/public/mojom/json_parser.mojom.h"
 #include "services/image_annotation/annotator.h"
 #include "services/image_annotation/public/mojom/image_annotation.mojom.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/services/network/cookie_settings_unittest.cc b/services/network/cookie_settings_unittest.cc
index 5634a0c..a50b811 100644
--- a/services/network/cookie_settings_unittest.cc
+++ b/services/network/cookie_settings_unittest.cc
@@ -1740,9 +1740,8 @@
   EXPECT_FALSE(settings.IsCookieAccessible(
       *cookie, GURL(kRwsMemberURL), net::SiteForCookies(), top_level_origin,
       net::FirstPartySetMetadata(
-          net::FirstPartySetEntry(primary, net::SiteType::kAssociated, 1u),
-          net::FirstPartySetEntry(primary, net::SiteType::kPrimary,
-                                  std::nullopt)),
+          net::FirstPartySetEntry(primary, net::SiteType::kAssociated),
+          net::FirstPartySetEntry(primary, net::SiteType::kPrimary)),
       GetCookieSettingOverrides(), &status));
   if (IsTPCDEnabled()) {
     EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
@@ -2250,9 +2249,8 @@
   EXPECT_FALSE(settings.AnnotateAndMoveUserBlockedCookies(
       GURL(kRwsMemberURL), net::SiteForCookies(), &origin,
       net::FirstPartySetMetadata(
-          net::FirstPartySetEntry(primary, net::SiteType::kAssociated, 1u),
-          net::FirstPartySetEntry(primary, net::SiteType::kPrimary,
-                                  std::nullopt)),
+          net::FirstPartySetEntry(primary, net::SiteType::kAssociated),
+          net::FirstPartySetEntry(primary, net::SiteType::kPrimary)),
       GetCookieSettingOverrides(), maybe_included_cookies, excluded_cookies));
 
   EXPECT_EQ(0u, maybe_included_cookies.size());
@@ -2295,9 +2293,8 @@
   url::Origin top_frame_origin = url::Origin::Create(GURL(kRwsOwnerURL));
 
   net::SchemefulSite primary((GURL(kRwsOwnerURL)));
-  net::FirstPartySetEntry frame_entry(primary, net::SiteType::kAssociated, 1u);
-  net::FirstPartySetEntry top_frame_entry(primary, net::SiteType::kPrimary,
-                                          std::nullopt);
+  net::FirstPartySetEntry frame_entry(primary, net::SiteType::kAssociated);
+  net::FirstPartySetEntry top_frame_entry(primary, net::SiteType::kPrimary);
 
   net::CookieAccessResultList maybe_included_cookies = {{*cookie, {}}};
   net::CookieAccessResultList excluded_cookies = {};
diff --git a/services/network/first_party_sets/first_party_sets_access_delegate_unittest.cc b/services/network/first_party_sets/first_party_sets_access_delegate_unittest.cc
index 01ad4e0a..868487a3 100644
--- a/services/network/first_party_sets/first_party_sets_access_delegate_unittest.cc
+++ b/services/network/first_party_sets/first_party_sets_access_delegate_unittest.cc
@@ -110,20 +110,15 @@
         /*entries=*/
         {
             {kSet1AssociatedSite1,
-             net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated,
-                                     0)},
+             net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated)},
             {kSet1AssociatedSite2,
-             net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated,
-                                     1)},
+             net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated)},
             {kSet1Primary,
-             net::FirstPartySetEntry(kSet1Primary, net::SiteType::kPrimary,
-                                     std::nullopt)},
+             net::FirstPartySetEntry(kSet1Primary, net::SiteType::kPrimary)},
             {kSet2AssociatedSite1,
-             net::FirstPartySetEntry(kSet2Primary, net::SiteType::kAssociated,
-                                     0)},
+             net::FirstPartySetEntry(kSet2Primary, net::SiteType::kAssociated)},
             {kSet2Primary,
-             net::FirstPartySetEntry(kSet2Primary, net::SiteType::kPrimary,
-                                     std::nullopt)},
+             net::FirstPartySetEntry(kSet2Primary, net::SiteType::kPrimary)},
         },
         /*aliases=*/{}));
   }
@@ -136,15 +131,14 @@
 };
 
 TEST_F(NoopFirstPartySetsAccessDelegateTest, ComputeMetadata) {
-  EXPECT_EQ(delegate().ComputeMetadata(kSet1AssociatedSite1, &kSet1Primary,
-                                       base::NullCallback()),
-            std::make_optional(std::make_pair(
-                net::FirstPartySetMetadata(
-                    net::FirstPartySetEntry(kSet1Primary,
-                                            net::SiteType::kAssociated, 0),
-                    net::FirstPartySetEntry(
-                        kSet1Primary, net::SiteType::kPrimary, std::nullopt)),
-                net::FirstPartySetsCacheFilter::MatchInfo())));
+  EXPECT_EQ(
+      delegate().ComputeMetadata(kSet1AssociatedSite1, &kSet1Primary,
+                                 base::NullCallback()),
+      std::make_optional(std::make_pair(
+          net::FirstPartySetMetadata(
+              net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated),
+              net::FirstPartySetEntry(kSet1Primary, net::SiteType::kPrimary)),
+          net::FirstPartySetsCacheFilter::MatchInfo())));
 }
 
 TEST_F(NoopFirstPartySetsAccessDelegateTest, FindEntries) {
@@ -153,11 +147,9 @@
                              base::NullCallback()),
       FirstPartySetsAccessDelegate::EntriesResult({
           {kSet1AssociatedSite1,
-           net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated,
-                                   0)},
+           net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated)},
           {kSet2AssociatedSite1,
-           net::FirstPartySetEntry(kSet2Primary, net::SiteType::kAssociated,
-                                   0)},
+           net::FirstPartySetEntry(kSet2Primary, net::SiteType::kAssociated)},
       }));
 }
 
@@ -175,20 +167,15 @@
         /*entries=*/
         {
             {kSet1AssociatedSite1,
-             net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated,
-                                     0)},
+             net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated)},
             {kSet1AssociatedSite2,
-             net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated,
-                                     1)},
+             net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated)},
             {kSet1Primary,
-             net::FirstPartySetEntry(kSet1Primary, net::SiteType::kPrimary,
-                                     std::nullopt)},
+             net::FirstPartySetEntry(kSet1Primary, net::SiteType::kPrimary)},
             {kSet2AssociatedSite1,
-             net::FirstPartySetEntry(kSet2Primary, net::SiteType::kAssociated,
-                                     0)},
+             net::FirstPartySetEntry(kSet2Primary, net::SiteType::kAssociated)},
             {kSet2Primary,
-             net::FirstPartySetEntry(kSet2Primary, net::SiteType::kPrimary,
-                                     std::nullopt)},
+             net::FirstPartySetEntry(kSet2Primary, net::SiteType::kPrimary)},
         },
         /*aliases=*/{}));
   }
@@ -273,7 +260,7 @@
 
   delegate_remote()->NotifyReady(mojom::FirstPartySetsReadyEvent::New());
 
-  net::FirstPartySetEntry entry(kSet1Primary, net::SiteType::kAssociated, 0);
+  net::FirstPartySetEntry entry(kSet1Primary, net::SiteType::kAssociated);
   EXPECT_EQ(future.Get(),
             std::make_tuple(net::FirstPartySetMetadata(entry, entry),
                             net::FirstPartySetsCacheFilter::MatchInfo()));
@@ -286,15 +273,14 @@
 
   delegate_remote()->NotifyReady(mojom::FirstPartySetsReadyEvent::New());
 
-  EXPECT_THAT(future.Get(),
-              FirstPartySetsAccessDelegate::EntriesResult({
-                  {kSet1AssociatedSite1,
-                   net::FirstPartySetEntry(kSet1Primary,
-                                           net::SiteType::kAssociated, 0)},
-                  {kSet2AssociatedSite1,
-                   net::FirstPartySetEntry(kSet2Primary,
-                                           net::SiteType::kAssociated, 0)},
-              }));
+  EXPECT_THAT(
+      future.Get(),
+      FirstPartySetsAccessDelegate::EntriesResult({
+          {kSet1AssociatedSite1,
+           net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated)},
+          {kSet2AssociatedSite1,
+           net::FirstPartySetEntry(kSet2Primary, net::SiteType::kAssociated)},
+      }));
 }
 
 TEST_F(AsyncFirstPartySetsAccessDelegateTest, OverrideSets_ComputeMetadata) {
@@ -303,22 +289,21 @@
           {
               {kSet1AssociatedSite1,
                net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                   kSet3Primary, net::SiteType::kAssociated, 0))},
+                   kSet3Primary, net::SiteType::kAssociated))},
               {kSet3Primary,
                net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                   kSet3Primary, net::SiteType::kPrimary, std::nullopt))},
+                   kSet3Primary, net::SiteType::kPrimary))},
           })
           .value(),
       /*cache_filter=*/std::nullopt));
 
   EXPECT_EQ(ComputeMetadataAndWait(kSet3Primary, &kSet1AssociatedSite1),
-            std::make_tuple(
-                net::FirstPartySetMetadata(
-                    net::FirstPartySetEntry(
-                        kSet3Primary, net::SiteType::kPrimary, std::nullopt),
-                    net::FirstPartySetEntry(kSet3Primary,
-                                            net::SiteType::kAssociated, 0)),
-                net::FirstPartySetsCacheFilter::MatchInfo()));
+            std::make_tuple(net::FirstPartySetMetadata(
+                                net::FirstPartySetEntry(
+                                    kSet3Primary, net::SiteType::kPrimary),
+                                net::FirstPartySetEntry(
+                                    kSet3Primary, net::SiteType::kAssociated)),
+                            net::FirstPartySetsCacheFilter::MatchInfo()));
 }
 
 TEST_F(AsyncFirstPartySetsAccessDelegateTest, OverrideSets_FindEntries) {
@@ -327,7 +312,7 @@
           {
               {kSet3Primary,
                net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                   kSet3Primary, net::SiteType::kPrimary, std::nullopt))},
+                   kSet3Primary, net::SiteType::kPrimary))},
           })
           .value(),
       /*cache_filter=*/std::nullopt));
@@ -345,10 +330,10 @@
             {
                 {kSet3AssociatedSite1,
                  net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                     kSet3Primary, net::SiteType::kAssociated, 0))},
+                     kSet3Primary, net::SiteType::kAssociated))},
                 {kSet3Primary,
                  net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                     kSet3Primary, net::SiteType::kPrimary, std::nullopt))},
+                     kSet3Primary, net::SiteType::kPrimary))},
             })
             .value(),
         net::FirstPartySetsCacheFilter({{kSet1Primary, kClearAtRunId}},
@@ -361,31 +346,27 @@
   match_info.clear_at_run_id = kClearAtRunId;
   match_info.browser_run_id = kBrowserRunId;
 
-  EXPECT_EQ(
-      ComputeMetadataAndWait(kSet1Primary, &kSet1AssociatedSite1),
-      std::make_tuple(
-          net::FirstPartySetMetadata(
-              net::FirstPartySetEntry(kSet1Primary, net::SiteType::kPrimary,
-                                      /*site_index=*/std::nullopt),
-              net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated,
-                                      0)),
-          match_info));
+  EXPECT_EQ(ComputeMetadataAndWait(kSet1Primary, &kSet1AssociatedSite1),
+            std::make_tuple(net::FirstPartySetMetadata(
+                                net::FirstPartySetEntry(
+                                    kSet1Primary, net::SiteType::kPrimary),
+                                net::FirstPartySetEntry(
+                                    kSet1Primary, net::SiteType::kAssociated)),
+                            match_info));
 }
 
 TEST_F(SyncFirstPartySetsAccessDelegateTest, FindEntries) {
-  EXPECT_THAT(FindEntriesAndWait({kSet1AssociatedSite1, kSet2AssociatedSite1,
-                                  kSet3AssociatedSite1}),
-              FirstPartySetsAccessDelegate::EntriesResult({
-                  {kSet1AssociatedSite1,
-                   net::FirstPartySetEntry(kSet1Primary,
-                                           net::SiteType::kAssociated, 0)},
-                  {kSet2AssociatedSite1,
-                   net::FirstPartySetEntry(kSet2Primary,
-                                           net::SiteType::kAssociated, 0)},
-                  {kSet3AssociatedSite1,
-                   net::FirstPartySetEntry(kSet3Primary,
-                                           net::SiteType::kAssociated, 0)},
-              }));
+  EXPECT_THAT(
+      FindEntriesAndWait(
+          {kSet1AssociatedSite1, kSet2AssociatedSite1, kSet3AssociatedSite1}),
+      FirstPartySetsAccessDelegate::EntriesResult({
+          {kSet1AssociatedSite1,
+           net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated)},
+          {kSet2AssociatedSite1,
+           net::FirstPartySetEntry(kSet2Primary, net::SiteType::kAssociated)},
+          {kSet3AssociatedSite1,
+           net::FirstPartySetEntry(kSet3Primary, net::SiteType::kAssociated)},
+      }));
 }
 
 // Verifies the behaviors of the delegate when First-Party Sets are initially
@@ -461,7 +442,7 @@
 
   delegate_remote()->NotifyReady(mojom::FirstPartySetsReadyEvent::New());
 
-  net::FirstPartySetEntry entry(kSet1Primary, net::SiteType::kAssociated, 0);
+  net::FirstPartySetEntry entry(kSet1Primary, net::SiteType::kAssociated);
   EXPECT_EQ(future.Get(),
             std::make_tuple(net::FirstPartySetMetadata(entry, entry),
                             net::FirstPartySetsCacheFilter::MatchInfo()));
@@ -488,7 +469,7 @@
               FirstPartySetsAccessDelegate::EntriesResult(
                   {{kSet1AssociatedSite1,
                     net::FirstPartySetEntry(kSet1Primary,
-                                            net::SiteType::kAssociated, 0)}}));
+                                            net::SiteType::kAssociated)}}));
   FindEntriesAndWait({kSet1AssociatedSite1});
 
   delegate().SetEnabled(false);
@@ -530,17 +511,16 @@
       net::FirstPartySetsContextConfig::Create(
           {{kSet1AssociatedSite1,
             net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                kSet2Primary, net::SiteType::kAssociated, 0))}})
+                kSet2Primary, net::SiteType::kAssociated))}})
           .value(),
       /*cache_filter=*/std::nullopt));
   EXPECT_EQ(future.Get(),
-            std::make_tuple(
-                net::FirstPartySetMetadata(
-                    net::FirstPartySetEntry(
-                        kSet2Primary, net::SiteType::kPrimary, std::nullopt),
-                    net::FirstPartySetEntry(kSet2Primary,
-                                            net::SiteType::kAssociated, 0)),
-                net::FirstPartySetsCacheFilter::MatchInfo()));
+            std::make_tuple(net::FirstPartySetMetadata(
+                                net::FirstPartySetEntry(
+                                    kSet2Primary, net::SiteType::kPrimary),
+                                net::FirstPartySetEntry(
+                                    kSet2Primary, net::SiteType::kAssociated)),
+                            net::FirstPartySetsCacheFilter::MatchInfo()));
   ComputeMetadataAndWait(kSet1AssociatedSite1, &kSet1AssociatedSite1);
 }
 
@@ -558,14 +538,14 @@
       net::FirstPartySetsContextConfig::Create(
           {{kSet1AssociatedSite1,
             net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                kSet2Primary, net::SiteType::kAssociated, 0))}})
+                kSet2Primary, net::SiteType::kAssociated))}})
           .value(),
       /*cache_filter=*/std::nullopt));
   EXPECT_EQ(future.Get(),
             FirstPartySetsAccessDelegate::EntriesResult(
                 {{kSet1AssociatedSite1,
                   net::FirstPartySetEntry(kSet2Primary,
-                                          net::SiteType::kAssociated, 0)}}));
+                                          net::SiteType::kAssociated)}}));
   FindEntriesAndWait({kSet1AssociatedSite1});
 }
 
@@ -615,7 +595,7 @@
   delegate_remote()->NotifyReady(mojom::FirstPartySetsReadyEvent::New());
   base::RunLoop().RunUntilIdle();
 
-  net::FirstPartySetEntry entry(kSet1Primary, net::SiteType::kAssociated, 0);
+  net::FirstPartySetEntry entry(kSet1Primary, net::SiteType::kAssociated);
   EXPECT_EQ(
       std::make_optional(
           std::make_pair(net::FirstPartySetMetadata(entry, entry),
@@ -639,11 +619,9 @@
                              base::NullCallback()),
       FirstPartySetsAccessDelegate::EntriesResult({
           {kSet1AssociatedSite1,
-           net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated,
-                                   0)},
+           net::FirstPartySetEntry(kSet1Primary, net::SiteType::kAssociated)},
           {kSet2AssociatedSite1,
-           net::FirstPartySetEntry(kSet2Primary, net::SiteType::kAssociated,
-                                   0)},
+           net::FirstPartySetEntry(kSet2Primary, net::SiteType::kAssociated)},
       }));
 }
 
@@ -654,23 +632,22 @@
           {
               {kSet1AssociatedSite1,
                net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                   kSet3Primary, net::SiteType::kAssociated, 0))},
+                   kSet3Primary, net::SiteType::kAssociated))},
               {kSet3Primary,
                net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                   kSet3Primary, net::SiteType::kPrimary, std::nullopt))},
+                   kSet3Primary, net::SiteType::kPrimary))},
           })
           .value(),
       /*cache_filter=*/std::nullopt));
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(ComputeMetadataAndWait(kSet3Primary, &kSet1AssociatedSite1),
-            std::make_tuple(
-                net::FirstPartySetMetadata(
-                    net::FirstPartySetEntry(
-                        kSet3Primary, net::SiteType::kPrimary, std::nullopt),
-                    net::FirstPartySetEntry(kSet3Primary,
-                                            net::SiteType::kAssociated, 0)),
-                net::FirstPartySetsCacheFilter::MatchInfo()));
+            std::make_tuple(net::FirstPartySetMetadata(
+                                net::FirstPartySetEntry(
+                                    kSet3Primary, net::SiteType::kPrimary),
+                                net::FirstPartySetEntry(
+                                    kSet3Primary, net::SiteType::kAssociated)),
+                            net::FirstPartySetsCacheFilter::MatchInfo()));
 }
 
 TEST_F(AsyncNonwaitingFirstPartySetsAccessDelegateTest,
@@ -680,7 +657,7 @@
           {
               {kSet3Primary,
                net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                   kSet3Primary, net::SiteType::kPrimary, std::nullopt))},
+                   kSet3Primary, net::SiteType::kPrimary))},
           })
           .value(),
       /*cache_filter=*/std::nullopt));
diff --git a/services/network/first_party_sets/first_party_sets_manager_unittest.cc b/services/network/first_party_sets/first_party_sets_manager_unittest.cc
index 39e8405..c849e91 100644
--- a/services/network/first_party_sets/first_party_sets_manager_unittest.cc
+++ b/services/network/first_party_sets/first_party_sets_manager_unittest.cc
@@ -89,11 +89,10 @@
   net::SchemefulSite example_test(GURL("https://example.test"));
   net::SchemefulSite aaaa(GURL("https://aaaa.test"));
 
-  SetCompleteSets({{aaaa, net::FirstPartySetEntry(
-                              example_test, net::SiteType::kAssociated, 0)},
-                   {example_test,
-                    net::FirstPartySetEntry(
-                        example_test, net::SiteType::kPrimary, std::nullopt)}},
+  SetCompleteSets({{aaaa, net::FirstPartySetEntry(example_test,
+                                                  net::SiteType::kAssociated)},
+                   {example_test, net::FirstPartySetEntry(
+                                      example_test, net::SiteType::kPrimary)}},
                   {{example_cctld, example_test}});
 
   EXPECT_THAT(manager().FindEntries(
@@ -124,11 +123,10 @@
   net::SchemefulSite example_test(GURL("https://example.test"));
   net::SchemefulSite aaaa(GURL("https://aaaa.test"));
 
-  SetCompleteSets({{aaaa, net::FirstPartySetEntry(
-                              example_test, net::SiteType::kAssociated, 0)},
-                   {example_test,
-                    net::FirstPartySetEntry(
-                        example_test, net::SiteType::kPrimary, std::nullopt)}},
+  SetCompleteSets({{aaaa, net::FirstPartySetEntry(example_test,
+                                                  net::SiteType::kAssociated)},
+                   {example_test, net::FirstPartySetEntry(
+                                      example_test, net::SiteType::kPrimary)}},
                   {{example_cctld, example_test}});
 
   EXPECT_THAT(
@@ -139,13 +137,11 @@
       }),
       UnorderedElementsAre(
           Pair(example_test,
-               net::FirstPartySetEntry(example_test, net::SiteType::kPrimary,
-                                       std::nullopt)),
+               net::FirstPartySetEntry(example_test, net::SiteType::kPrimary)),
           Pair(example_cctld,
-               net::FirstPartySetEntry(example_test, net::SiteType::kPrimary,
-                                       std::nullopt)),
+               net::FirstPartySetEntry(example_test, net::SiteType::kPrimary)),
           Pair(aaaa, net::FirstPartySetEntry(example_test,
-                                             net::SiteType::kAssociated, 0))));
+                                             net::SiteType::kAssociated))));
 }
 
 TEST_F(FirstPartySetsManagerEnabledTest, SetCompleteSets_Idempotent) {
@@ -157,9 +153,8 @@
 
   // The second call to SetCompleteSets should have no effect.
   SetCompleteSets(
-      {{aaaa, net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)},
-       {example, net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                         std::nullopt)}},
+      {{aaaa, net::FirstPartySetEntry(example, net::SiteType::kAssociated)},
+       {example, net::FirstPartySetEntry(example, net::SiteType::kPrimary)}},
       {});
   EXPECT_THAT(FindEntriesAndWait({
                   aaaa,
@@ -199,18 +194,14 @@
     SetCompleteSets(
         {
             {net::SchemefulSite(GURL("https://associatedSite1.test")),
-             net::FirstPartySetEntry(example_test, net::SiteType::kAssociated,
-                                     0)},
+             net::FirstPartySetEntry(example_test, net::SiteType::kAssociated)},
             {net::SchemefulSite(GURL("https://associatedSite3.test")),
-             net::FirstPartySetEntry(example_test, net::SiteType::kAssociated,
-                                     0)},
+             net::FirstPartySetEntry(example_test, net::SiteType::kAssociated)},
             {example_test,
-             net::FirstPartySetEntry(example_test, net::SiteType::kPrimary,
-                                     std::nullopt)},
+             net::FirstPartySetEntry(example_test, net::SiteType::kPrimary)},
             {net::SchemefulSite(GURL("https://associatedSite2.test")),
-             net::FirstPartySetEntry(foo, net::SiteType::kAssociated, 0)},
-            {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary,
-                                          std::nullopt)},
+             net::FirstPartySetEntry(foo, net::SiteType::kAssociated)},
+            {foo, net::FirstPartySetEntry(foo, net::SiteType::kPrimary)},
         },
         {{example_cctld, example_test}});
 
@@ -243,7 +234,7 @@
   {
     net::FirstPartySetEntry entry(
         net::SchemefulSite(GURL("https://example.test")),
-        net::SiteType::kAssociated, 0);
+        net::SiteType::kAssociated);
 
     EXPECT_EQ(future.Get(), net::FirstPartySetMetadata(entry, entry));
   }
@@ -266,14 +257,13 @@
       future.Get(),
       UnorderedElementsAre(
           Pair(associatedSite1,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)),
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)),
           Pair(example_cctld,
-               net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                       std::nullopt)),
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)),
           Pair(associatedSite2,
                net::FirstPartySetEntry(
                    net::SchemefulSite(GURL("https://foo.test")),
-                   net::SiteType::kAssociated, 0))));
+                   net::SiteType::kAssociated))));
 }
 
 class AsyncNonwaitingFirstPartySetsManagerTest
@@ -296,7 +286,7 @@
 
   net::FirstPartySetEntry entry(
       net::SchemefulSite(GURL("https://example.test")),
-      net::SiteType::kAssociated, 0);
+      net::SiteType::kAssociated);
 
   EXPECT_EQ(net::FirstPartySetMetadata(entry, entry),
             manager().ComputeMetadata(associatedSite, &associatedSite,
@@ -323,14 +313,13 @@
                             base::NullCallback()),
       Optional(UnorderedElementsAre(
           Pair(associatedSite1,
-               net::FirstPartySetEntry(example, net::SiteType::kAssociated, 0)),
+               net::FirstPartySetEntry(example, net::SiteType::kAssociated)),
           Pair(example_cctld,
-               net::FirstPartySetEntry(example, net::SiteType::kPrimary,
-                                       std::nullopt)),
+               net::FirstPartySetEntry(example, net::SiteType::kPrimary)),
           Pair(associatedSite2,
                net::FirstPartySetEntry(
                    net::SchemefulSite(GURL("https://foo.test")),
-                   net::SiteType::kAssociated, 0)))));
+                   net::SiteType::kAssociated)))));
 }
 
 }  // namespace network
diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc
index 536ed91..ff18b7c 100644
--- a/services/network/public/cpp/features.cc
+++ b/services/network/public/cpp/features.cc
@@ -534,7 +534,7 @@
 // https://wicg.github.io/shared-storage/#batch-update
 BASE_FEATURE(kSharedStorageTransactionalBatchUpdate,
              "SharedStorageTransactionalBatchUpdate",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 // Kill switch for the Interest Group API, i.e. if disabled, the
 // API exposure will be disabled regardless of the OT config.
diff --git a/services/network/public/cpp/first_party_sets_mojom_traits.cc b/services/network/public/cpp/first_party_sets_mojom_traits.cc
index ed006bf9..6829b88 100644
--- a/services/network/public/cpp/first_party_sets_mojom_traits.cc
+++ b/services/network/public/cpp/first_party_sets_mojom_traits.cc
@@ -25,14 +25,6 @@
 
 namespace mojo {
 
-bool StructTraits<network::mojom::SiteIndexDataView,
-                  net::FirstPartySetEntry::SiteIndex>::
-    Read(network::mojom::SiteIndexDataView index,
-         net::FirstPartySetEntry::SiteIndex* out) {
-  *out = net::FirstPartySetEntry::SiteIndex(index.value());
-  return true;
-}
-
 bool EnumTraits<network::mojom::SiteType, net::SiteType>::FromMojom(
     network::mojom::SiteType site_type,
     net::SiteType* out) {
@@ -76,7 +68,7 @@
   if (!entry.ReadSiteType(&site_type))
     return false;
 
-  *out = net::FirstPartySetEntry(primary, site_type, std::nullopt);
+  *out = net::FirstPartySetEntry(primary, site_type);
   return true;
 }
 
diff --git a/services/network/public/cpp/first_party_sets_mojom_traits.h b/services/network/public/cpp/first_party_sets_mojom_traits.h
index 06edbdf8..ddf92b5c 100644
--- a/services/network/public/cpp/first_party_sets_mojom_traits.h
+++ b/services/network/public/cpp/first_party_sets_mojom_traits.h
@@ -22,18 +22,6 @@
 
 template <>
 struct COMPONENT_EXPORT(FIRST_PARTY_SETS_MOJOM_TRAITS)
-    StructTraits<network::mojom::SiteIndexDataView,
-                 net::FirstPartySetEntry::SiteIndex> {
-  static uint32_t value(const net::FirstPartySetEntry::SiteIndex& i) {
-    return i.value();
-  }
-
-  static bool Read(network::mojom::SiteIndexDataView index,
-                   net::FirstPartySetEntry::SiteIndex* out);
-};
-
-template <>
-struct COMPONENT_EXPORT(FIRST_PARTY_SETS_MOJOM_TRAITS)
     EnumTraits<network::mojom::SiteType, net::SiteType> {
   static network::mojom::SiteType ToMojom(net::SiteType site_type);
 
diff --git a/services/network/public/cpp/first_party_sets_mojom_traits_unittest.cc b/services/network/public/cpp/first_party_sets_mojom_traits_unittest.cc
index 6325001..2fb07e6 100644
--- a/services/network/public/cpp/first_party_sets_mojom_traits_unittest.cc
+++ b/services/network/public/cpp/first_party_sets_mojom_traits_unittest.cc
@@ -26,16 +26,6 @@
 using testing::Key;
 using testing::UnorderedElementsAre;
 
-TEST(FirstPartySetsTraitsTest, Roundtrips_SiteIndex) {
-  net::FirstPartySetEntry::SiteIndex original(1337);
-  net::FirstPartySetEntry::SiteIndex round_tripped;
-
-  EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::SiteIndex>(
-      original, round_tripped));
-
-  EXPECT_EQ(original, round_tripped);
-}
-
 TEST(FirstPartySetsTraitsTest, Roundtrips_SiteType) {
   for (net::SiteType site_type : {
            net::SiteType::kPrimary,
@@ -52,7 +42,7 @@
 TEST(FirstPartySetsTraitsTest, Roundtrips_FirstPartySetEntry) {
   net::SchemefulSite primary(GURL("https://primary.test"));
 
-  net::FirstPartySetEntry original(primary, net::SiteType::kAssociated, 1);
+  net::FirstPartySetEntry original(primary, net::SiteType::kAssociated);
   net::FirstPartySetEntry round_tripped;
 
   EXPECT_TRUE(mojo::test::SerializeAndDeserialize<mojom::FirstPartySetEntry>(
@@ -66,10 +56,9 @@
   net::SchemefulSite frame_owner(GURL("https://frame.test"));
   net::SchemefulSite top_frame_owner(GURL("https://top_frame.test"));
 
-  net::FirstPartySetEntry frame_entry(frame_owner, net::SiteType::kAssociated,
-                                      1);
+  net::FirstPartySetEntry frame_entry(frame_owner, net::SiteType::kAssociated);
   net::FirstPartySetEntry top_frame_entry(top_frame_owner,
-                                          net::SiteType::kAssociated, 2);
+                                          net::SiteType::kAssociated);
 
   auto make_metadata = [&]() {
     // Use non-default values to ensure serialization/deserialization works
@@ -100,20 +89,18 @@
       base::Version("1.2.3"),
       /*entries=*/
       {
-          {a,
-           net::FirstPartySetEntry(a, net::SiteType::kPrimary, std::nullopt)},
-          {b, net::FirstPartySetEntry(a, net::SiteType::kAssociated, 0)},
-          {c,
-           net::FirstPartySetEntry(a, net::SiteType::kService, std::nullopt)},
+          {a, net::FirstPartySetEntry(a, net::SiteType::kPrimary)},
+          {b, net::FirstPartySetEntry(a, net::SiteType::kAssociated)},
+          {c, net::FirstPartySetEntry(a, net::SiteType::kService)},
       },
       /*aliases=*/{{c_cctld, c}});
 
   original.ApplyManuallySpecifiedSet(
       net::LocalSetDeclaration::Create(
           /*set_entries=*/{{a, net::FirstPartySetEntry(
-                                   a, net::SiteType::kPrimary, std::nullopt)},
+                                   a, net::SiteType::kPrimary)},
                            {b, net::FirstPartySetEntry(
-                                   a, net::SiteType::kAssociated, 0)}},
+                                   a, net::SiteType::kAssociated)}},
           /*aliases=*/{{b_cctld, b}})
           .value());
 
@@ -138,20 +125,18 @@
       base::Version(),
       /*entries=*/
       {
-          {a,
-           net::FirstPartySetEntry(a, net::SiteType::kPrimary, std::nullopt)},
-          {b, net::FirstPartySetEntry(a, net::SiteType::kAssociated, 0)},
-          {c,
-           net::FirstPartySetEntry(a, net::SiteType::kService, std::nullopt)},
+          {a, net::FirstPartySetEntry(a, net::SiteType::kPrimary)},
+          {b, net::FirstPartySetEntry(a, net::SiteType::kAssociated)},
+          {c, net::FirstPartySetEntry(a, net::SiteType::kService)},
       },
       /*aliases=*/{{c_cctld, c}});
 
   original.ApplyManuallySpecifiedSet(
       net::LocalSetDeclaration::Create(
           /*set_entries=*/{{a, net::FirstPartySetEntry(
-                                   a, net::SiteType::kPrimary, std::nullopt)},
+                                   a, net::SiteType::kPrimary)},
                            {b, net::FirstPartySetEntry(
-                                   a, net::SiteType::kAssociated, 0)}},
+                                   a, net::SiteType::kAssociated)}},
           /*aliases=*/{{b_cctld, b}})
           .value());
 
@@ -180,12 +165,12 @@
   const net::FirstPartySetsContextConfig original =
       net::FirstPartySetsContextConfig::Create(
           {
-              {a, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                      a, net::SiteType::kPrimary, std::nullopt))},
-              {b, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                      a, net::SiteType::kAssociated, 0))},
+              {a, net::FirstPartySetEntryOverride(
+                      net::FirstPartySetEntry(a, net::SiteType::kPrimary))},
+              {b, net::FirstPartySetEntryOverride(
+                      net::FirstPartySetEntry(a, net::SiteType::kAssociated))},
               {b_alias, net::FirstPartySetEntryOverride(net::FirstPartySetEntry(
-                            a, net::SiteType::kAssociated, 0))},
+                            a, net::SiteType::kAssociated))},
               {c, net::FirstPartySetEntryOverride()},
           },
           {{b_alias, b}})
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn
index 378bbd2..9631e36 100644
--- a/services/network/public/mojom/BUILD.gn
+++ b/services/network/public/mojom/BUILD.gn
@@ -1229,10 +1229,6 @@
         {
           types = [
             {
-              mojom = "network.mojom.SiteIndex"
-              cpp = "::net::FirstPartySetEntry::SiteIndex"
-            },
-            {
               mojom = "network.mojom.SiteType"
               cpp = "::net::SiteType"
             },
diff --git a/services/network/public/mojom/first_party_sets.mojom b/services/network/public/mojom/first_party_sets.mojom
index d38b51f..571014d 100644
--- a/services/network/public/mojom/first_party_sets.mojom
+++ b/services/network/public/mojom/first_party_sets.mojom
@@ -7,12 +7,6 @@
 import "mojo/public/mojom/base/version.mojom";
 import "services/network/public/mojom/schemeful_site.mojom";
 
-// This struct should match net::FirstPartySetEntry::SiteIndex in
-// //net/first_party_sets/first_party_set_entry.h
-struct SiteIndex {
-  uint32 value;
-};
-
 // This enum should match //net/first_party_sets/first_party_set_entry.h.
 enum SiteType {
   kPrimary,
diff --git a/services/on_device_model/public/cpp/test_support/fake_service.cc b/services/on_device_model/public/cpp/test_support/fake_service.cc
index 44c08c8c3..a72ba5c6 100644
--- a/services/on_device_model/public/cpp/test_support/fake_service.cc
+++ b/services/on_device_model/public/cpp/test_support/fake_service.cc
@@ -201,6 +201,19 @@
     remote->OnResponse(std::move(chunk));
   }
 
+  if (options->constraint) {
+    const auto& constraint = *options->constraint;
+    auto chunk = mojom::ResponseChunk::New();
+    if (constraint.is_json_schema()) {
+      chunk->text = "Constraint: json " + constraint.get_json_schema() + "\n";
+    } else if (constraint.is_regex()) {
+      chunk->text = "Constraint: regex " + constraint.get_regex() + "\n";
+    } else {
+      chunk->text = "Constraint: unknown\n";
+    }
+    remote->OnResponse(std::move(chunk));
+  }
+
   int output_token_count = 0;
   if (settings_->model_execute_result.empty()) {
     for (const auto& context : context_) {
diff --git a/services/tracing/DEPS b/services/tracing/DEPS
index 2eb00c1..28f8a93 100644
--- a/services/tracing/DEPS
+++ b/services/tracing/DEPS
@@ -4,6 +4,7 @@
   "+third_party/perfetto/include",
   "+third_party/perfetto/protos/perfetto",
   "+third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h",
+  "+third_party/snappy"
 ]
 
 specific_include_rules = {
diff --git a/services/tracing/public/cpp/BUILD.gn b/services/tracing/public/cpp/BUILD.gn
index b84c420e..59e94b3 100644
--- a/services/tracing/public/cpp/BUILD.gn
+++ b/services/tracing/public/cpp/BUILD.gn
@@ -136,6 +136,7 @@
       "//build:chromecast_buildflags",
       "//components/system_cpu:system_cpu",
       "//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite",
+      "//third_party/snappy:snappy",
     ]
 
     if (!(is_chromeos && target_cpu == "arm64" && current_cpu == "arm")) {
diff --git a/services/tracing/public/cpp/trace_startup_config.cc b/services/tracing/public/cpp/trace_startup_config.cc
index e48529b..b25c07a 100644
--- a/services/tracing/public/cpp/trace_startup_config.cc
+++ b/services/tracing/public/cpp/trace_startup_config.cc
@@ -9,6 +9,7 @@
 #include <memory>
 #include <string>
 
+#include "base/base64.h"
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/json/json_reader.h"
@@ -25,6 +26,7 @@
 #include "services/tracing/public/cpp/perfetto/perfetto_config.h"
 #include "services/tracing/public/mojom/perfetto_service.mojom.h"
 #include "third_party/perfetto/protos/perfetto/config/track_event/track_event_config.gen.h"
+#include "third_party/snappy/src/snappy.h"
 
 #if BUILDFLAG(IS_ANDROID)
 #include "base/android/early_trace_event_binding.h"
@@ -120,7 +122,9 @@
     DCHECK(IsEnabled());
   } else if (EnableFromConfigHandle()) {
     DCHECK(IsEnabled());
-  } else if (EnableFromConfigFile()) {
+  } else if (EnableFromJsonConfigFile()) {
+    DCHECK(IsEnabled());
+  } else if (EnableFromPerfettoConfigFile()) {
     DCHECK(IsEnabled());
   } else if (EnableFromBackgroundTracing()) {
     DCHECK(IsEnabled());
@@ -283,7 +287,7 @@
   return true;
 }
 
-bool TraceStartupConfig::EnableFromConfigFile() {
+bool TraceStartupConfig::EnableFromJsonConfigFile() {
 #if BUILDFLAG(IS_ANDROID)
   base::FilePath trace_config_file(kAndroidTraceConfigFile);
 #else
@@ -318,11 +322,57 @@
     DLOG(WARNING) << "Cannot read the trace config file correctly.";
     return false;
   }
-  is_enabled_ = ParseTraceConfigFileContent(trace_config_file_content);
-  if (!is_enabled_) {
+  auto config = ParseTraceJsonConfigFileContent(trace_config_file_content);
+  if (!config) {
     DLOG(WARNING) << "Cannot parse the trace config file correctly.";
+    return false;
   }
-  return is_enabled_;
+  perfetto_config_ = *config;
+  is_enabled_ = true;
+  return true;
+}
+
+bool TraceStartupConfig::EnableFromPerfettoConfigFile() {
+  auto* command_line = base::CommandLine::ForCurrentProcess();
+  if (!command_line->HasSwitch(switches::kTracePerfettoConfigFile)) {
+    return false;
+  }
+  base::FilePath config_file =
+      command_line->GetSwitchValuePath(switches::kTracePerfettoConfigFile);
+
+  if (config_file.empty()) {
+    DLOG(WARNING) << "--perfetto-config-file needs a config file path.";
+    return false;
+  }
+
+  if (!base::PathExists(config_file)) {
+    DLOG(WARNING) << "The perfetto config file does not exist.";
+    return false;
+  }
+
+  std::string config_text;
+  if (!base::ReadFileToString(config_file, &config_text)) {
+    DLOG(WARNING) << "Cannot read the trace config file correctly.";
+    return false;
+  }
+
+  std::optional<perfetto::TraceConfig> config;
+  if (base::FilePath::CompareEqualIgnoreCase(config_file.Extension(),
+                                             FILE_PATH_LITERAL(".pb"))) {
+    config = ParseSerializedPerfettoConfig(base::as_byte_span(config_text));
+  } else {
+    config = ParseEncodedPerfettoConfig(config_text);
+  }
+  if (!config) {
+    DLOG(WARNING) << "Failed to parse perfetto config file.";
+    return false;
+  }
+  if (AdaptPerfettoConfigForChrome(&*config)) {
+    DLOG(WARNING) << "Failed to adapt perfetto config file.";
+  }
+  perfetto_config_ = *config;
+  is_enabled_ = true;
+  return true;
 }
 
 bool TraceStartupConfig::EnableFromBackgroundTracing() {
@@ -352,28 +402,29 @@
   return true;
 }
 
-bool TraceStartupConfig::ParseTraceConfigFileContent(
+std::optional<perfetto::TraceConfig>
+TraceStartupConfig::ParseTraceJsonConfigFileContent(
     const std::string& content) {
   std::optional<base::Value::Dict> value = base::JSONReader::ReadDict(content);
   if (!value) {
-    return false;
+    return std::nullopt;
   }
 
   auto* trace_config_dict = value->FindDict(kTraceConfigParam);
   if (!trace_config_dict) {
-    return false;
+    return std::nullopt;
   }
 
   auto chrome_config =
       base::trace_event::TraceConfig(std::move(*trace_config_dict));
-  perfetto_config_ = tracing::GetDefaultPerfettoConfig(
+  perfetto::TraceConfig perfetto_config = tracing::GetDefaultPerfettoConfig(
       chrome_config, false, output_format_ != OutputFormat::kProto,
       perfetto::protos::gen::ChromeConfig::USER_INITIATED, "");
 
   int startup_duration_in_seconds =
       value->FindInt(kStartupDurationParam).value_or(0);
   if (startup_duration_in_seconds > 0) {
-    perfetto_config_.set_duration_ms(startup_duration_in_seconds * 1000);
+    perfetto_config.set_duration_ms(startup_duration_in_seconds * 1000);
   }
 
   if (auto* result_file = value->FindString(kResultFileParam)) {
@@ -386,7 +437,39 @@
         "_chrometrace.log");
   }
 
-  return true;
+  return perfetto_config;
+}
+
+std::optional<perfetto::TraceConfig>
+TraceStartupConfig::ParseSerializedPerfettoConfig(
+    const base::span<const uint8_t>& config_bytes) {
+  perfetto::TraceConfig config;
+  if (config_bytes.empty()) {
+    return std::nullopt;
+  }
+  if (config.ParseFromArray(config_bytes.data(), config_bytes.size())) {
+    return config;
+  }
+  return std::nullopt;
+}
+
+std::optional<perfetto::TraceConfig>
+TraceStartupConfig::ParseEncodedPerfettoConfig(
+    const std::string& config_string) {
+  std::string serialized_config;
+  if (!base::Base64Decode(config_string, &serialized_config,
+                          base::Base64DecodePolicy::kForgiving)) {
+    return std::nullopt;
+  }
+
+  // `serialized_config` may optionally be compressed.
+  std::string decompressed_config;
+  if (!snappy::Uncompress(serialized_config.data(), serialized_config.size(),
+                          &decompressed_config)) {
+    return ParseSerializedPerfettoConfig(base::as_byte_span(serialized_config));
+  }
+
+  return ParseSerializedPerfettoConfig(base::as_byte_span(decompressed_config));
 }
 
 }  // namespace tracing
diff --git a/services/tracing/public/cpp/trace_startup_config.h b/services/tracing/public/cpp/trace_startup_config.h
index 452e4a0..ea424dc 100644
--- a/services/tracing/public/cpp/trace_startup_config.h
+++ b/services/tracing/public/cpp/trace_startup_config.h
@@ -6,6 +6,7 @@
 #define SERVICES_TRACING_PUBLIC_CPP_TRACE_STARTUP_CONFIG_H_
 
 #include "base/component_export.h"
+#include "base/containers/span.h"
 #include "base/files/file_path.h"
 #include "base/trace_event/trace_config.h"
 #include "build/build_config.h"
@@ -154,11 +155,17 @@
   TraceStartupConfig();
 
   bool EnableFromCommandLine();
-  bool EnableFromConfigFile();
+  bool EnableFromJsonConfigFile();
+  bool EnableFromPerfettoConfigFile();
   bool EnableFromConfigHandle();
   bool EnableFromBackgroundTracing();
 
-  bool ParseTraceConfigFileContent(const std::string& content);
+  std::optional<perfetto::TraceConfig> ParseTraceJsonConfigFileContent(
+      const std::string& content);
+  std::optional<perfetto::TraceConfig> ParseSerializedPerfettoConfig(
+      const base::span<const uint8_t>& config_bytes);
+  std::optional<perfetto::TraceConfig> ParseEncodedPerfettoConfig(
+      const std::string& config_string);
 
   bool is_enabled_ = false;
   perfetto::TraceConfig perfetto_config_;
diff --git a/services/tracing/public/cpp/trace_startup_config_unittest.cc b/services/tracing/public/cpp/trace_startup_config_unittest.cc
index a24d668..ecbe887b 100644
--- a/services/tracing/public/cpp/trace_startup_config_unittest.cc
+++ b/services/tracing/public/cpp/trace_startup_config_unittest.cc
@@ -7,19 +7,39 @@
 #include <algorithm>
 
 #include "base/at_exit.h"
+#include "base/base64.h"
 #include "base/command_line.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/memory/ptr_util.h"
+#include "base/path_service.h"
+#include "base/test/test_proto_loader.h"
+#include "base/threading/thread_restrictions.h"
 #include "components/tracing/common/tracing_switches.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h"
 #include "third_party/perfetto/protos/perfetto/config/data_source_config.gen.h"
+#include "third_party/perfetto/protos/perfetto/config/trace_config.gen.h"
 
 namespace tracing {
 
 namespace {
 
+perfetto::protos::gen::TraceConfig ParseTracingConfigFromText(
+    const std::string& proto_text) {
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::TestProtoLoader config_loader(
+      base::PathService::CheckedGet(base::DIR_GEN_TEST_DATA_ROOT)
+          .Append(FILE_PATH_LITERAL(
+              "third_party/perfetto/protos/perfetto/config/config.descriptor")),
+      "perfetto.protos.TraceConfig");
+  std::string serialized_message;
+  config_loader.ParseFromText(proto_text, serialized_message);
+  perfetto::protos::gen::TraceConfig destination;
+  destination.ParseFromString(serialized_message);
+  return destination;
+}
+
 const char kTraceConfig[] =
     "{"
     "\"enable_argument_filter\":true,"
@@ -32,6 +52,20 @@
     "\"record_mode\":\"record-continuously\""
     "}";
 
+constexpr const char kPerfettoConfig[] = R"pb(
+  duration_ms: 10000
+  buffers: { size_kb: 4 fill_policy: RING_BUFFER }
+  data_sources: {
+    config: {
+      name: "track_event"
+      track_event_config: {
+        disabled_categories: [ "excluded" ]
+        enabled_categories: [ "included" ]
+      }
+    }
+  }
+)pb";
+
 std::string GetTraceConfigFileContent(std::string trace_config,
                                       std::string startup_duration,
                                       std::string result_file) {
@@ -105,7 +139,7 @@
   EXPECT_FALSE(startup_config_->IsEnabled());
 }
 
-TEST_F(TraceStartupConfigTest, ValidContent) {
+TEST_F(TraceStartupConfigTest, ValidJsonContent) {
   std::string content =
       GetTraceConfigFileContent(kTraceConfig, "10", "trace_result_file.log");
 
@@ -132,6 +166,47 @@
             startup_config_->GetResultFile());
 }
 
+TEST_F(TraceStartupConfigTest, ValidProtoContent) {
+  std::string config_string =
+      ParseTracingConfigFromText(kPerfettoConfig).SerializeAsString();
+
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath config_file =
+      temp_dir.GetPath().Append(FILE_PATH_LITERAL("config.pb"));
+  ASSERT_TRUE(base::WriteFile(config_file, config_string));
+  base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
+      switches::kTracePerfettoConfigFile, config_file);
+
+  Initialize();
+  ASSERT_TRUE(startup_config_->IsEnabled());
+  auto config = startup_config_->GetPerfettoConfig();
+  ASSERT_EQ(1, config.data_sources_size());
+  EXPECT_EQ("track_event", config.data_sources()[0].config().name());
+  EXPECT_EQ(10000U, config.duration_ms());
+}
+
+TEST_F(TraceStartupConfigTest, ValidBase64Content) {
+  std::string config_string =
+      ParseTracingConfigFromText(kPerfettoConfig).SerializeAsString();
+  std::string serialized_config = base::Base64Encode(config_string);
+
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath config_file =
+      temp_dir.GetPath().Append(FILE_PATH_LITERAL("config.txt"));
+  ASSERT_TRUE(base::WriteFile(config_file, serialized_config));
+  base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
+      switches::kTracePerfettoConfigFile, config_file);
+
+  Initialize();
+  ASSERT_TRUE(startup_config_->IsEnabled());
+  auto config = startup_config_->GetPerfettoConfig();
+  ASSERT_EQ(1, config.data_sources_size());
+  EXPECT_EQ("track_event", config.data_sources()[0].config().name());
+  EXPECT_EQ(10000U, config.duration_ms());
+}
+
 TEST_F(TraceStartupConfigTest, ValidContentWithOnlyTraceConfig) {
   std::string content = GetTraceConfigFileContent(kTraceConfig, "", "");
 
@@ -228,7 +303,7 @@
   EXPECT_FALSE(startup_config_->IsEnabled());
 }
 
-TEST_F(TraceStartupConfigTest, InvalidContent) {
+TEST_F(TraceStartupConfigTest, InvalidJsonContent) {
   std::string content = "invalid trace config file content";
 
   base::FilePath trace_config_file;
@@ -244,6 +319,22 @@
   EXPECT_FALSE(startup_config_->IsEnabled());
 }
 
+TEST_F(TraceStartupConfigTest, InvalidProtoContent) {
+  std::string content = "invalid trace config file content";
+
+  base::FilePath trace_config_file;
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  ASSERT_TRUE(
+      base::CreateTemporaryFileInDir(temp_dir.GetPath(), &trace_config_file));
+  ASSERT_TRUE(base::WriteFile(trace_config_file, content));
+  base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
+      switches::kTracePerfettoConfigFile, trace_config_file);
+
+  Initialize();
+  EXPECT_FALSE(startup_config_->IsEnabled());
+}
+
 TEST_F(TraceStartupConfigTest, EmptyContent) {
   base::FilePath trace_config_file;
   base::ScopedTempDir temp_dir;
diff --git a/services/viz/public/mojom/compositing/layer_context.mojom b/services/viz/public/mojom/compositing/layer_context.mojom
index cfaafd6..33ac1db 100644
--- a/services/viz/public/mojom/compositing/layer_context.mojom
+++ b/services/viz/public/mojom/compositing/layer_context.mojom
@@ -40,6 +40,11 @@
   // Links trace events on the display side to other events in the client.
   uint64 trace_id;
 
+  // Indicates that this frame is submitted after the primary main frame
+  // navigating to a session history item, identified by this item sequence
+  // number.
+  int64 primary_main_frame_item_sequence_number;
+
   // The page scale factors set by the tree's client. All must be positive,
   // non-zero.
   float page_scale_factor;
diff --git a/services/webnn/BUILD.gn b/services/webnn/BUILD.gn
index e5104713..4236929c 100644
--- a/services/webnn/BUILD.gn
+++ b/services/webnn/BUILD.gn
@@ -24,6 +24,17 @@
   deps = [ ":buildflags" ]
 }
 
+# Public WebNN native headers so they can be visible for dependencies
+# of webnn native.
+source_set("webnn_headers") {
+  sources = [
+    # Backend headers must be included here so that dependent targets can
+    # still include them, even if the backends are disabled.
+    "d3d12_backend.h",
+  ]
+  public_deps = [ "//base" ]
+}
+
 component("webnn_service") {
   defines = [ "IS_WEBNN_SERVICE_IMPL" ]
 
@@ -50,6 +61,7 @@
 
   deps = [
     ":buildflags",
+    ":webnn_headers",
     ":webnn_switches",
     "//base",
     "//gpu/command_buffer/service:gles2",
@@ -157,8 +169,15 @@
 
   if (webnn_use_ort) {
     sources += [
+      "ort/model_editor.cc",
+      "ort/model_editor.h",
+      "ort/ort_data_type.cc",
+      "ort/ort_data_type.h",
+      "ort/ort_status.cc",
+      "ort/ort_status.h",
       "ort/platform_functions_ort.cc",
       "ort/platform_functions_ort.h",
+      "ort/scoped_ort_types.h",
     ]
     deps += [ "//third_party/onnxruntime_headers" ]
   }
@@ -208,13 +227,16 @@
 
   if (webnn_use_ort) {
     sources += [
+      "ort/model_editor_test.cc",
       "ort/platform_functions_ort_test.cc",
+      "ort/test_base_ort.cc",
       "ort/test_base_ort.h",
     ]
   }
 
   deps = [
     ":buildflags",
+    ":webnn_headers",
     ":webnn_service",
     "//base",
     "//base/test:test_support",
diff --git a/services/webnn/coreml/graph_builder_coreml.cc b/services/webnn/coreml/graph_builder_coreml.cc
index 6586540..92d4854 100644
--- a/services/webnn/coreml/graph_builder_coreml.cc
+++ b/services/webnn/coreml/graph_builder_coreml.cc
@@ -1831,7 +1831,7 @@
         AddOperationForReshape(input_id, internal_operand_id, block));
     // Points the input_id to the reshaped node's coreml identifier, so that
     // subsequent operations find the correct inputs.
-    id_to_operand_info_map()[input_id].coreml_name =
+    id_to_operand_info_map()[input_id]->coreml_name =
         GetOperandInfo(internal_operand_id).coreml_name;
   }
   return base::ok();
@@ -5734,12 +5734,12 @@
   // Prefix is added to internal operands generated for WebNN operations that
   // need to be decomposed into multiple CoreML operations.
   CHECK(id_to_operand_info_map()
-            .try_emplace(
-                operand_id,
-                OperandInfo(base::JoinString({kInternalNamePrefix,
+            .try_emplace(operand_id, std::make_unique<OperandInfo>(
+                                         base::JoinString(
+                                             {kInternalNamePrefix,
                                               base::NumberToString(operand_id)},
                                              kStringSeparator),
-                            dimensions, mil_data_type))
+                                         dimensions, mil_data_type))
             .second);
   return operand_id;
 }
@@ -5780,11 +5780,11 @@
 void GraphBuilderCoreml::UpdateCoreMLInputInfoMap(OperandId operand_id) {
   const mojom::Operand& operand = GetOperand(operand_id);
   CHECK(id_to_operand_info_map()
-            .try_emplace(operand_id,
-                         OperandInfo(GetCoreMLNameFromOperand(operand_id),
-                                     operand.descriptor.shape(),
-                                     OperandTypeToMILDataType(
-                                         operand.descriptor.data_type())))
+            .try_emplace(operand_id, std::make_unique<OperandInfo>(
+                                         GetCoreMLNameFromOperand(operand_id),
+                                         operand.descriptor.shape(),
+                                         OperandTypeToMILDataType(
+                                             operand.descriptor.data_type())))
             .second);
 }
 
@@ -6073,7 +6073,7 @@
 GraphBuilderCoreml::Result::GetOperandInfo(OperandId operand_id) const {
   auto it = id_to_operand_info_map.find(operand_id);
   CHECK(it != id_to_operand_info_map.end());
-  return it->second;
+  return *it->second;
 }
 
 }  // namespace webnn::coreml
diff --git a/services/webnn/coreml/graph_builder_coreml.h b/services/webnn/coreml/graph_builder_coreml.h
index e85bcb6..b20f3df 100644
--- a/services/webnn/coreml/graph_builder_coreml.h
+++ b/services/webnn/coreml/graph_builder_coreml.h
@@ -24,6 +24,7 @@
 #include "services/webnn/public/mojom/webnn_context_provider.mojom.h"
 #include "services/webnn/public/mojom/webnn_error.mojom-forward.h"
 #include "services/webnn/public/mojom/webnn_graph.mojom.h"
+#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
 #include "third_party/coremltools/mlmodel/format/MIL.pb.h"
 #include "third_party/coremltools/mlmodel/format/Model.pb.h"
 
@@ -100,7 +101,10 @@
     [[nodiscard]] const OperandInfo& GetOperandInfo(OperandId operand_id) const;
 
     const base::FilePath ml_package_dir;
-    std::map<OperandId, OperandInfo> id_to_operand_info_map;
+    // `std::unique_ptr` is used for values to provide pointer stabiliy for
+    // `GetOperandInfo`.
+    absl::flat_hash_map<OperandId, std::unique_ptr<OperandInfo>>
+        id_to_operand_info_map;
   };
 
   // Factory method that creates a GraphBuilderCoreml, builds and serializes the
@@ -572,7 +576,8 @@
   const base::FilePath& ml_package_dir() const {
     return result_->ml_package_dir;
   }
-  std::map<OperandId, OperandInfo>& id_to_operand_info_map() const {
+  absl::flat_hash_map<OperandId, std::unique_ptr<OperandInfo>>&
+  id_to_operand_info_map() const {
     return result_->id_to_operand_info_map;
   }
 
diff --git a/services/webnn/d3d12_backend.h b/services/webnn/d3d12_backend.h
new file mode 100644
index 0000000..fe68fd8
--- /dev/null
+++ b/services/webnn/d3d12_backend.h
@@ -0,0 +1,56 @@
+// 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 SERVICES_WEBNN_D3D12_BACKEND_H_
+#define SERVICES_WEBNN_D3D12_BACKEND_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/component_export.h"
+#include "third_party/microsoft_dxheaders/src/include/directx/d3d12.h"
+
+// Windows SDK headers should be included after DirectX headers.
+#include <wrl.h>
+
+namespace webnn::native::d3d12 {
+
+// WebNNSharedFence is a wrapper of an ID3D12Fence and fence value which is
+// signaled when the execution on GPU is completed.
+struct COMPONENT_EXPORT(WEBNN_SERVICE) WebNNSharedFence {
+  virtual Microsoft::WRL::ComPtr<ID3D12Fence> GetD3D12Fence() const = 0;
+  virtual uint64_t GetFenceValue() const = 0;
+  virtual ~WebNNSharedFence() = default;
+};
+
+// WebNNTensor is a native interface which exposes a WebNN tensor in the
+// GPU service using backend-specific APIs. Implemented by the WebNN
+// service and called by shared image backings to access the tensor.
+class COMPONENT_EXPORT(WEBNN_SERVICE) WebNNTensor {
+ public:
+  // Begin WebNN access to the underlying buffer held in the `WebNNTensor`
+  // instance. Input is a fence which will be waited on by WebNN before
+  // execution resumes. If successful, EndAccessWebNN() must be called to
+  // BeginAccessWebNN() again.
+  virtual bool BeginAccessWebNN(Microsoft::WRL::ComPtr<ID3D12Fence> wait_fence,
+                                uint64_t wait_fence_value) = 0;
+
+  // End WebNN access to the underlying buffer held in the `WebNNTensor`
+  // instance. Outputs a fence to be signaled by WebNN after execution
+  // completes. If successful, BeginAccessWebNN() must be called to restore
+  // access to WebNN and to EndAccessWebNN() again.
+  virtual std::unique_ptr<WebNNSharedFence> EndAccessWebNN() = 0;
+
+  // Retrieves the underlying buffer held in the `WebNNTensor` instance.
+  // The returned tensor buffer is a committed resource which cannot be used
+  // externally until EndAccessWebNN() is called.
+  virtual ID3D12Resource* GetD3D12Buffer() const = 0;
+
+  virtual ~WebNNTensor() = default;
+};
+
+}  // namespace webnn::native::d3d12
+
+#endif  // SERVICES_WEBNN_D3D12_BACKEND_H_
diff --git a/services/webnn/dml/command_recorder.cc b/services/webnn/dml/command_recorder.cc
index 19cfffd..1ed95c3b 100644
--- a/services/webnn/dml/command_recorder.cc
+++ b/services/webnn/dml/command_recorder.cc
@@ -126,6 +126,15 @@
         command_queue_->submission_fence(), last_submitted_fence_value_));
   }
 
+  // Before command submission, ensure interop tensors are not accessed by
+  // the command queue until existing GPU work using them has been completed.
+  for (auto& [command_buffer, webnn_tensor_impl] : command_tensor_impls_) {
+    if (webnn_tensor_impl) {
+      RETURN_IF_FAILED(webnn_tensor_impl->WaitForExternalFenceAndReset(
+          command_queue_.get()));
+    }
+  }
+
   RETURN_IF_FAILED(command_queue_->ExecuteCommandList(command_list_.Get()));
   last_submitted_fence_value_ = command_queue_->GetLastFenceValue();
 
diff --git a/services/webnn/dml/context_impl_dml.cc b/services/webnn/dml/context_impl_dml.cc
index a8279d7..b64452a 100644
--- a/services/webnn/dml/context_impl_dml.cc
+++ b/services/webnn/dml/context_impl_dml.cc
@@ -934,6 +934,10 @@
   CHECK(hr == E_OUTOFMEMORY || hr == DXGI_ERROR_DEVICE_RESET);
 }
 
+CommandQueue* ContextImplDml::GetCommandQueue() const {
+  return adapter_->command_queue();
+}
+
 void ContextImplDml::RemoveDeviceForTesting() {
   CHECK_IS_TEST();
 
diff --git a/services/webnn/dml/context_impl_dml.h b/services/webnn/dml/context_impl_dml.h
index 4479df9..37e9c25 100644
--- a/services/webnn/dml/context_impl_dml.h
+++ b/services/webnn/dml/context_impl_dml.h
@@ -18,6 +18,7 @@
 namespace webnn::dml {
 
 class Adapter;
+class CommandQueue;
 class CommandRecorder;
 class TensorImplDml;
 
@@ -59,6 +60,8 @@
   // gracefully terminate the GPU process.
   void HandleContextLostOrCrash(std::string_view message_for_log, HRESULT hr);
 
+  CommandQueue* GetCommandQueue() const;
+
   void RemoveDeviceForTesting();
 
   // The test cases can override the graph/tensor creating behavior by
diff --git a/services/webnn/dml/graph_impl_dml.cc b/services/webnn/dml/graph_impl_dml.cc
index 3a7d431..8e3395a 100644
--- a/services/webnn/dml/graph_impl_dml.cc
+++ b/services/webnn/dml/graph_impl_dml.cc
@@ -55,6 +55,8 @@
 #include "services/webnn/webnn_constant_operand.h"
 #include "services/webnn/webnn_context_impl.h"
 #include "services/webnn/webnn_utils.h"
+#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
+#include "third_party/abseil-cpp/absl/container/flat_hash_set.h"
 #include "third_party/fp16/src/include/fp16.h"
 
 namespace webnn::dml {
@@ -77,7 +79,7 @@
 using IdToOperandMap = base::flat_map<OperandId, OperandPtr>;
 // A map of all node outputs in `dml::GraphBuilderDml` using the mojom operand
 // id as key.
-using IdToNodeOutputMap = std::map<OperandId, const NodeOutput*>;
+using IdToNodeOutputMap = absl::flat_hash_map<OperandId, const NodeOutput*>;
 
 static constexpr auto kDmlFloatDataTypes =
     base::MakeFixedFlatSet<DML_TENSOR_DATA_TYPE>(
@@ -254,7 +256,7 @@
     const base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>&
         constant_operands) {
   base::CheckedNumeric<size_t> total_byte_length(0);
-  std::map<OperandId, D3D12_RANGE> key_to_d3d12_range_map;
+  absl::flat_hash_map<OperandId, D3D12_RANGE> key_to_d3d12_range_map;
 
   for (const auto& [operand_id, constant_operand] : constant_operands) {
     auto& d3d12_range = key_to_d3d12_range_map[operand_id];
@@ -287,7 +289,7 @@
     const base::flat_map<std::string, OperandDescriptor>&
         names_to_descriptors) {
   base::CheckedNumeric<size_t> total_byte_length(0);
-  std::map<std::string, D3D12_RANGE> key_to_d3d12_range_map;
+  absl::flat_hash_map<std::string, D3D12_RANGE> key_to_d3d12_range_map;
 
   for (auto& [name, descriptor] : names_to_descriptors) {
     auto& d3d12_range = key_to_d3d12_range_map[name];
@@ -324,7 +326,7 @@
 // `buffer_variant` for both constants uploading and binding. For GPU doesn't
 // support UMA, pass a upload buffer and a default buffer via `buffer_variant`
 // for uploading and binding separately.
-base::expected<std::map<OperandId, DML_BUFFER_BINDING>, HRESULT>
+base::expected<absl::flat_hash_map<OperandId, DML_BUFFER_BINDING>, HRESULT>
 UploadAndCreateConstantBufferBinding(
     CommandRecorder* command_recorder,
     const base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>&
@@ -357,7 +359,7 @@
 
   RETURN_UNEXPECTED_IF_FAILED(buffer_to_map->Map(0, nullptr, &mapped_buffer));
 
-  std::map<OperandId, DML_BUFFER_BINDING> key_to_buffer_binding_map;
+  absl::flat_hash_map<OperandId, DML_BUFFER_BINDING> key_to_buffer_binding_map;
   for (auto& [operand_id, constant_operand] : constant_operands) {
     // Copy the input data to the upload heap with byte offset
     const auto& d3d12_range =
@@ -392,7 +394,8 @@
 
 HRESULT MapAndCopyInputDataToBuffer(
     const base::flat_map<std::string, mojo_base::BigBuffer>& named_inputs,
-    const std::map<std::string, D3D12_RANGE>& input_name_to_d3d12_range_map,
+    const absl::flat_hash_map<std::string, D3D12_RANGE>&
+        input_name_to_d3d12_range_map,
     ID3D12Resource* buffer) {
   // Map entire resource to copy the array buffer of input one by one
   // with byte offset.
@@ -451,7 +454,7 @@
         constant_operands,
     GraphBuilderDml& graph_builder,
     IdToNodeOutputMap& id_to_node_output_map,
-    std::unordered_map<OperandId, uint32_t>& constant_id_to_input_index_map) {
+    absl::flat_hash_map<OperandId, uint32_t>& constant_id_to_input_index_map) {
   const OperandDescriptor operand_descriptor =
       constant_operands.at(operand_id)->descriptor();
 
@@ -692,7 +695,8 @@
 }
 
 std::optional<const Operation*> GetFusibleActivationFromOperation(
-    const std::map<const Operation*, raw_ptr<const Operation, CtnExperimental>>&
+    const absl::flat_hash_map<const Operation*,
+                              raw_ptr<const Operation, CtnExperimental>>&
         operation_to_fusible_standalone_activation_map,
     const Operation* operation) {
   const auto activation_iterator =
@@ -704,8 +708,9 @@
   return std::optional<const Operation*>();
 }
 
-std::optional<OperandId> GetFusibleTransposeInputId(
-    const std::map<OperandId, raw_ptr<const Operation, CtnExperimental>>&
+std::optional<uint64_t> GetFusibleTransposeInputId(
+    const absl::flat_hash_map<OperandId,
+                              raw_ptr<const Operation, CtnExperimental>>&
         output_id_to_fusible_transpose_map,
     OperandId input_id) {
   const auto transpose_iterator =
@@ -1303,18 +1308,19 @@
   // fused into preceding operations.
   // The key is the preceding operation which can support fusion. The value is
   // the standalone activation which can be fused into the preceding operation.
-  std::map<const Operation*, raw_ptr<const Operation, CtnExperimental>>
+  absl::flat_hash_map<const Operation*,
+                      raw_ptr<const Operation, CtnExperimental>>
       operation_to_fusible_standalone_activation_map;
 
   // A map of all transposes that can be fused into the following matmul using
   // transpose's output operand id as the key.
-  std::map<OperandId, raw_ptr<const Operation, CtnExperimental>>
+  absl::flat_hash_map<OperandId, raw_ptr<const Operation, CtnExperimental>>
       output_id_to_fusible_transpose_map;
 
   // A set of all operations in `mojom::GraphInfo` which can be fused into
   // another operation. No DirectML operator node will be created for operations
   // in this set.
-  std::unordered_set<const Operation*> fusible_operations_set;
+  absl::flat_hash_set<const Operation*> fusible_operations_set;
 };
 
 // The method gets the graph fusion information from `mojom::GraphInfo`, based
@@ -1332,7 +1338,7 @@
 
   // A map of all fusible activations in `mojom::GraphInfo` using activation's
   // input operand id as the key.
-  std::map<OperandId, raw_ptr<const Operation, CtnExperimental>>
+  absl::flat_hash_map<OperandId, raw_ptr<const Operation, CtnExperimental>>
       input_id_to_activation_map;
 
   // The case we're interested in includes a fusible base operation with exactly
@@ -1381,7 +1387,7 @@
 
   // A map of all matmul operations in `mojom::GraphInfo` using matmul's input
   // operand id as the key.
-  std::map<OperandId, const Operation*> input_id_to_matmul_map;
+  absl::flat_hash_map<OperandId, const Operation*> input_id_to_matmul_map;
 
   // This is a scenario where transpose can be fused into the following matmul.
   // The transpose output solely feeds matmul. The transposed input can be
@@ -1408,7 +1414,7 @@
   GraphFusionInfo graph_fusion_info;
   // A map to record how many times each operand id is used as one
   // operation's input edge or the graph's output edge.
-  std::map<OperandId, uint32_t> operand_id_to_use_count_map;
+  absl::flat_hash_map<OperandId, uint32_t> operand_id_to_use_count_map;
   for (const auto& pair : graph_info->id_to_operand_map) {
     operand_id_to_use_count_map[pair.first] = 0;
   }
@@ -1526,14 +1532,15 @@
     Adapter* adapter,
     const ContextProperties& context_properties,
     const Operation* operation,
-    const std::map<const Operation*, raw_ptr<const Operation, CtnExperimental>>&
+    const absl::flat_hash_map<const Operation*,
+                              raw_ptr<const Operation, CtnExperimental>>&
         operation_to_fusible_standalone_activation_map,
     mojom::GraphInfoPtr& graph_info,
     base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>&
         constant_operands,
     GraphBuilderDml& graph_builder,
     IdToNodeOutputMap& id_to_node_output_map,
-    std::unordered_map<OperandId, uint32_t>& constant_id_to_input_index_map,
+    absl::flat_hash_map<OperandId, uint32_t>& constant_id_to_input_index_map,
     OperandId& next_operand_id) {
   const auto& batch_normalization = operation->get_batch_normalization();
   auto& id_to_operand_map = graph_info->id_to_operand_map;
@@ -1780,7 +1787,8 @@
     const ContextProperties& context_properties,
     const IdToOperandMap& id_to_operand_map,
     const Operation* operation,
-    const std::map<const Operation*, raw_ptr<const Operation, CtnExperimental>>&
+    const absl::flat_hash_map<const Operation*,
+                              raw_ptr<const Operation, CtnExperimental>>&
         operation_to_fusible_standalone_activation_map,
     GraphBuilderDml& graph_builder,
     IdToNodeOutputMap& id_to_node_output_map) {
@@ -2299,7 +2307,8 @@
     const ContextProperties& context_properties,
     const IdToOperandMap& id_to_operand_map,
     const Operation* operation,
-    const std::map<const Operation*, raw_ptr<const Operation, CtnExperimental>>&
+    const absl::flat_hash_map<const Operation*,
+                              raw_ptr<const Operation, CtnExperimental>>&
         operation_to_fusible_standalone_activation_map,
     GraphBuilderDml& graph_builder,
     IdToNodeOutputMap& id_to_node_output_map) {
@@ -3566,7 +3575,7 @@
         constant_operands,
     GraphBuilderDml& graph_builder,
     IdToNodeOutputMap& id_to_node_output_map,
-    std::unordered_map<OperandId, uint32_t>& constant_id_to_input_index_map,
+    absl::flat_hash_map<OperandId, uint32_t>& constant_id_to_input_index_map,
     OperandId& next_operand_id) {
   // Check feature level by referring to MSDN doc:
   // https://learn.microsoft.com/en-us/windows/ai/directml/api/ns-directml-dml_activation_gelu_operator_desc
@@ -3719,7 +3728,8 @@
     const ContextProperties& context_properties,
     const IdToOperandMap& id_to_operand_map,
     const Operation* operation,
-    const std::map<const Operation*, raw_ptr<const Operation, CtnExperimental>>&
+    const absl::flat_hash_map<const Operation*,
+                              raw_ptr<const Operation, CtnExperimental>>&
         operation_to_fusible_standalone_activation_map,
     GraphBuilderDml& graph_builder,
     IdToNodeOutputMap& id_to_node_output_map) {
@@ -3852,7 +3862,7 @@
         constant_operands,
     GraphBuilderDml& graph_builder,
     IdToNodeOutputMap& id_to_node_output_map,
-    std::unordered_map<OperandId, uint32_t>& constant_id_to_input_index_map,
+    absl::flat_hash_map<OperandId, uint32_t>& constant_id_to_input_index_map,
     OperandId& next_operand_id) {
   mojom::Operation::Tag op_tag;
   std::optional<OperandId> initial_hidden_state_operand_id;
@@ -4247,14 +4257,15 @@
     const ContextProperties& context_properties,
     const NormalizationPtr& normalization,
     const Operation* operation,
-    const std::map<const Operation*, raw_ptr<const Operation, CtnExperimental>>&
+    const absl::flat_hash_map<const Operation*,
+                              raw_ptr<const Operation, CtnExperimental>>&
         operation_to_fusible_standalone_activation_map,
     mojom::GraphInfoPtr& graph_info,
     base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>&
         constant_operands,
     GraphBuilderDml& graph_builder,
     IdToNodeOutputMap& id_to_node_output_map,
-    std::unordered_map<OperandId, uint32_t>& constant_id_to_input_index_map,
+    absl::flat_hash_map<OperandId, uint32_t>& constant_id_to_input_index_map,
     OperandId& next_operand_id,
     base::span<const uint32_t> mean_variance_axes,
     base::span<const uint32_t> scale_bias_broadcast_axes,
@@ -4480,7 +4491,7 @@
         constant_operands,
     GraphBuilderDml& graph_builder,
     IdToNodeOutputMap& id_to_node_output_map,
-    std::unordered_map<OperandId, uint32_t>& constant_id_to_input_index_map,
+    absl::flat_hash_map<OperandId, uint32_t>& constant_id_to_input_index_map,
     OperandId& next_operand_id) {
   const std::string& label = lstm.label;
   IdToOperandMap& id_to_operand_map = graph_info->id_to_operand_map;
@@ -5014,9 +5025,11 @@
     const ContextProperties& context_properties,
     const IdToOperandMap& id_to_operand_map,
     const Operation* operation,
-    const std::map<const Operation*, raw_ptr<const Operation, CtnExperimental>>&
+    const absl::flat_hash_map<const Operation*,
+                              raw_ptr<const Operation, CtnExperimental>>&
         operation_to_fusible_standalone_activation_map,
-    const std::map<OperandId, raw_ptr<const Operation, CtnExperimental>>&
+    const absl::flat_hash_map<OperandId,
+                              raw_ptr<const Operation, CtnExperimental>>&
         output_id_to_fusible_transpose_map,
     GraphBuilderDml& graph_builder,
     IdToNodeOutputMap& id_to_node_output_map) {
@@ -5457,7 +5470,7 @@
         constant_operands,
     GraphBuilderDml& graph_builder,
     IdToNodeOutputMap& id_to_node_output_map,
-    std::unordered_map<OperandId, uint32_t>& constant_id_to_input_index_map,
+    absl::flat_hash_map<OperandId, uint32_t>& constant_id_to_input_index_map,
     OperandId& next_operand_id) {
   const NodeOutput* input = GetNodeOutputForOperand(
       id_to_node_output_map, triangular->input_operand_id);
@@ -6015,7 +6028,7 @@
     scoped_refptr<Adapter> adapter,
     base::WeakPtr<ContextImplDml> context,
     WebNNContextImpl::CreateGraphImplCallback callback,
-    std::unordered_map<OperandId, uint32_t> constant_id_to_input_index_map,
+    absl::flat_hash_map<OperandId, uint32_t> constant_id_to_input_index_map,
     GraphBufferBindingInfo graph_buffer_binding_info,
     ComputeResourceInfo compute_resource_info,
     base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
@@ -6143,7 +6156,8 @@
     }
 
     ASSIGN_OR_RETURN(
-        (std::map<OperandId, DML_BUFFER_BINDING> constant_buffer_binding),
+        (absl::flat_hash_map<OperandId, DML_BUFFER_BINDING>
+             constant_buffer_binding),
         UploadAndCreateConstantBufferBinding(
             initialization_command_recorder.get(), constant_operands,
             aligned_byte_length_of_constants.value(),
@@ -6362,7 +6376,7 @@
     base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>&
         constant_operands,
     GraphBuilderDml& graph_builder,
-    std::unordered_map<OperandId, uint32_t>& constant_id_to_input_index_map,
+    absl::flat_hash_map<OperandId, uint32_t>& constant_id_to_input_index_map,
     GraphBufferBindingInfo& graph_buffer_binding_info) {
   IdToNodeOutputMap id_to_node_output_map;
   const IdToOperandMap& id_to_operand_map = graph_info->id_to_operand_map;
@@ -6845,7 +6859,7 @@
   TRACE_EVENT0("gpu", "dml::GraphImplDml::CreateAndBuild");
 
   GraphBuilderDml graph_builder(adapter->dml_device());
-  std::unordered_map<OperandId, uint32_t> constant_id_to_input_index_map;
+  absl::flat_hash_map<OperandId, uint32_t> constant_id_to_input_index_map;
   GraphBufferBindingInfo graph_buffer_binding_info;
   base::expected<void, mojom::ErrorPtr> create_operator_result =
       GraphImplDml::CreateAndBuildInternal(
diff --git a/services/webnn/dml/graph_impl_dml.h b/services/webnn/dml/graph_impl_dml.h
index 660e90e..5636a45 100644
--- a/services/webnn/dml/graph_impl_dml.h
+++ b/services/webnn/dml/graph_impl_dml.h
@@ -5,10 +5,8 @@
 #ifndef SERVICES_WEBNN_DML_GRAPH_IMPL_DML_H_
 #define SERVICES_WEBNN_DML_GRAPH_IMPL_DML_H_
 
-#include <map>
 #include <memory>
 #include <string>
-#include <unordered_map>
 #include <vector>
 
 #include "base/containers/flat_map.h"
@@ -23,6 +21,7 @@
 #include "services/webnn/webnn_constant_operand.h"
 #include "services/webnn/webnn_context_impl.h"
 #include "services/webnn/webnn_graph_impl.h"
+#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
 #include "third_party/microsoft_dxheaders/include/directml.h"
 
 // Windows SDK headers should be included after DirectX headers.
@@ -40,7 +39,7 @@
 template <typename Key>
 struct AlignedByteLength {
   size_t total_byte_length = 0;
-  std::map<Key, D3D12_RANGE> key_to_d3d12_range_map;
+  absl::flat_hash_map<Key, D3D12_RANGE> key_to_d3d12_range_map;
 };
 
 // GraphImplDml inherits WebNNGraphImpl to represent a DML graph implementation.
@@ -68,12 +67,12 @@
     // order.
     // The index is the DML_INPUT_GRAPH_EDGE_DESC::GraphInputIndex when
     // creating the DML_GRAPH_DESC.
-    std::unordered_map<std::string, uint32_t> graph_input_name_to_index_map;
+    absl::flat_hash_map<std::string, uint32_t> graph_input_name_to_index_map;
     // The map is used to bind output buffers for the graph execution in
     // order.
     // The index is the DML_OUTPUT_GRAPH_EDGE_DESC::GraphOutputIndex when
     // creating the DML_GRAPH_DESC.
-    std::unordered_map<std::string, uint32_t> graph_output_name_to_index_map;
+    absl::flat_hash_map<std::string, uint32_t> graph_output_name_to_index_map;
   };
   static base::expected<void, mojom::ErrorPtr> CreateAndBuildInternal(
       const ContextProperties& context_properties,
@@ -82,7 +81,7 @@
       base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>&
           constant_operands,
       GraphBuilderDml& graph_builder,
-      std::unordered_map<OperandId, uint32_t>& constant_id_to_input_index_map,
+      absl::flat_hash_map<OperandId, uint32_t>& constant_id_to_input_index_map,
       GraphBufferBindingInfo& graph_buffer_binding_info);
 
   // This method builds and compiles a DML graph from mojom::GraphInfo via
@@ -224,7 +223,7 @@
       scoped_refptr<Adapter> adapter,
       base::WeakPtr<ContextImplDml> context,
       WebNNContextImpl::CreateGraphImplCallback callback,
-      std::unordered_map<OperandId, uint32_t> constant_id_to_input_index_map,
+      absl::flat_hash_map<OperandId, uint32_t> constant_id_to_input_index_map,
       GraphBufferBindingInfo graph_buffer_binding_info,
       ComputeResourceInfo compute_resource_info,
       base::flat_map<OperandId, std::unique_ptr<WebNNConstantOperand>>
diff --git a/services/webnn/dml/tensor_impl_dml.cc b/services/webnn/dml/tensor_impl_dml.cc
index 05efe5d..f06c4422 100644
--- a/services/webnn/dml/tensor_impl_dml.cc
+++ b/services/webnn/dml/tensor_impl_dml.cc
@@ -5,11 +5,29 @@
 #include "services/webnn/dml/tensor_impl_dml.h"
 
 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
+#include "services/webnn/dml/command_queue.h"
 #include "services/webnn/dml/context_impl_dml.h"
+#include "services/webnn/dml/error.h"
 #include "services/webnn/public/mojom/webnn_tensor.mojom.h"
 
 namespace webnn::dml {
 
+SharedFence::SharedFence(Microsoft::WRL::ComPtr<ID3D12Fence> fence,
+                         UINT64 fence_value)
+    : fence(std::move(fence)), fence_value(fence_value) {}
+
+SharedFence::~SharedFence() = default;
+
+SharedFence::SharedFence(SharedFence&&) = default;
+
+Microsoft::WRL::ComPtr<ID3D12Fence> SharedFence::GetD3D12Fence() const {
+  return fence;
+}
+
+uint64_t SharedFence::GetFenceValue() const {
+  return fence_value;
+}
+
 TensorImplDml::TensorImplDml(
     mojo::PendingAssociatedReceiver<mojom::WebNNTensor> receiver,
     Microsoft::WRL::ComPtr<ID3D12Resource> buffer,
@@ -39,4 +57,47 @@
   return last_submission_fence_value_;
 }
 
+HRESULT TensorImplDml::WaitForExternalFenceAndReset(
+    CommandQueue* command_queue) {
+  if (wait_fence_external_) {
+    RETURN_IF_FAILED(
+        command_queue->WaitForFence(std::move(wait_fence_external_->fence),
+                                    wait_fence_external_->fence_value));
+    wait_fence_external_.reset();
+  }
+  return S_OK;
+}
+
+bool TensorImplDml::BeginAccessWebNN(
+    Microsoft::WRL::ComPtr<ID3D12Fence> wait_fence,
+    uint64_t wait_fence_value) {
+  CHECK(wait_fence);
+
+  wait_fence_external_.emplace(std::move(wait_fence), wait_fence_value);
+  return true;
+}
+
+std::unique_ptr<native::d3d12::WebNNSharedFence>
+TensorImplDml::EndAccessWebNN() {
+  CommandQueue* command_queue =
+      static_cast<ContextImplDml*>(context_.get())->GetCommandQueue();
+
+  // If WebNN executed no commands using this tensor, the caller's command queue
+  // will wait on the last wait fence provided.
+  if (wait_fence_external_) {
+    SharedFence fence = std::move(wait_fence_external_.value());
+    wait_fence_external_.reset();
+    return base::WrapUnique(new SharedFence(std::move(fence)));
+  }
+
+  // Return WebNN's submission fence and the last fence value from execution
+  // that used this tensor.
+  return base::WrapUnique(new SharedFence(
+      {command_queue->submission_fence(), last_submission_fence_value_}));
+}
+
+ID3D12Resource* TensorImplDml::GetD3D12Buffer() const {
+  return buffer();
+}
+
 }  // namespace webnn::dml
diff --git a/services/webnn/dml/tensor_impl_dml.h b/services/webnn/dml/tensor_impl_dml.h
index 335b952b..a2a7768 100644
--- a/services/webnn/dml/tensor_impl_dml.h
+++ b/services/webnn/dml/tensor_impl_dml.h
@@ -5,6 +5,7 @@
 #ifndef SERVICES_WEBNN_DML_TENSOR_IMPL_DML_H_
 #define SERVICES_WEBNN_DML_TENSOR_IMPL_DML_H_
 
+#include "services/webnn/d3d12_backend.h"
 #include "services/webnn/public/mojom/webnn_tensor.mojom-forward.h"
 #include "services/webnn/webnn_tensor_impl.h"
 #include "third_party/microsoft_dxheaders/src/include/directx/d3d12.h"
@@ -14,9 +15,25 @@
 
 namespace webnn::dml {
 
+class CommandQueue;
 class ContextImplDml;
 
-class TensorImplDml final : public WebNNTensorImpl {
+struct SharedFence final : public native::d3d12::WebNNSharedFence {
+  SharedFence(Microsoft::WRL::ComPtr<ID3D12Fence> fence, UINT64 fence_value);
+  ~SharedFence() override;
+
+  SharedFence(SharedFence&&);
+
+  // native::d3d12::WebNNSharedFence implementation
+  Microsoft::WRL::ComPtr<ID3D12Fence> GetD3D12Fence() const override;
+  uint64_t GetFenceValue() const override;
+
+  const Microsoft::WRL::ComPtr<ID3D12Fence> fence;
+  const uint64_t fence_value;
+};
+
+class TensorImplDml final : public WebNNTensorImpl,
+                            public native::d3d12::WebNNTensor {
  public:
   TensorImplDml(mojo::PendingAssociatedReceiver<mojom::WebNNTensor> receiver,
                 Microsoft::WRL::ComPtr<ID3D12Resource> buffer,
@@ -39,10 +56,18 @@
     return weak_factory_.GetWeakPtr();
   }
 
+  HRESULT WaitForExternalFenceAndReset(CommandQueue* command_queue);
+
  private:
   void ReadTensorImpl(ReadTensorCallback callback) override;
   void WriteTensorImpl(mojo_base::BigBuffer src_buffer) override;
 
+  // native::d3d12::WebNNTensor implementation
+  bool BeginAccessWebNN(Microsoft::WRL::ComPtr<ID3D12Fence> wait_fence,
+                        uint64_t wait_fence_value) override;
+  std::unique_ptr<native::d3d12::WebNNSharedFence> EndAccessWebNN() override;
+  ID3D12Resource* GetD3D12Buffer() const override;
+
   // The D3D12 resource that holds the tensor data.
   // The buffer must always remain valid after creation and could outlive
   // the scope of this `TensorImplDml` instance because it may be used
@@ -54,6 +79,11 @@
   // indicate whether commands have completed execution.
   uint64_t last_submission_fence_value_ = 0;
 
+  // Required input to `BeginAccessWebNN()` to resume WebNN execution after
+  // this fence is signaled. If no value, there is no need to wait for access to
+  // the tensor.
+  std::optional<SharedFence> wait_fence_external_;
+
   base::WeakPtrFactory<TensorImplDml> weak_factory_{this};
 };
 
diff --git a/services/webnn/error.h b/services/webnn/error.h
index 8bc5ebdbf..e82fdebf 100644
--- a/services/webnn/error.h
+++ b/services/webnn/error.h
@@ -20,6 +20,8 @@
     "Invalid tensor from renderer.";
 inline constexpr char kBadMessageOnBuiltGraphBuilder[] =
     "Invalid message on an MLGraphBuilder which has already built a graph.";
+inline constexpr char kBadMessageInvalidContext[] =
+    "Invalid context from renderer.";
 
 template <typename MojoResultType>
 mojo::StructPtr<MojoResultType> ToError(const mojom::Error::Code& error_code,
diff --git a/services/webnn/ort/model_editor.cc b/services/webnn/ort/model_editor.cc
new file mode 100644
index 0000000..236ae12
--- /dev/null
+++ b/services/webnn/ort/model_editor.cc
@@ -0,0 +1,343 @@
+// 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 "services/webnn/ort/model_editor.h"
+
+#include <numeric>
+#include <ranges>
+
+#include "base/functional/overloaded.h"
+#include "base/notreached.h"
+#include "base/numerics/checked_math.h"
+#include "base/types/fixed_array.h"
+#include "services/webnn/ort/ort_data_type.h"
+#include "services/webnn/ort/ort_status.h"
+
+namespace webnn::ort {
+
+namespace {
+
+// Domains
+constexpr char kOrtDefaultDomain[] = "";
+constexpr char kMSDomain[] = "com.microsoft";
+
+// Opset versions
+constexpr int32_t kOrtOpsetVersion = 21;
+
+// EPContext op is used for exporting the compiled model.
+// https://onnxruntime.ai/docs/execution-providers/EP-Context-Design.html#onnxruntime-ep-context-cache-feature-design
+constexpr int32_t kEPContextOpsetVersion = 1;
+
+// The minimum size (in bytes) to add the initializer as external data. An
+// initializer less than 128 bytes might be used for shape inferencing which
+// doesn't support external data.
+// https://github.com/microsoft/onnxruntime/blob/c1ef02f74b0d648cc7e8558805fa90846ea11a35/include/onnxruntime/core/session/onnxruntime_c_api.h#L5564
+constexpr size_t kMinExternalDataSize = 128;
+
+const OrtApi* GetOrtApi() {
+  return PlatformFunctions::GetInstance()->ort_api();
+}
+
+const OrtModelEditorApi* GetOrtModelEditorApi() {
+  return PlatformFunctions::GetInstance()->ort_model_editor_api();
+}
+
+std::vector<int64_t> VectorUint32ToInt64(base::span<const uint32_t> vec) {
+  return std::vector<int64_t>(vec.begin(), vec.end());
+}
+
+size_t CalculateOrtTensorSizeInBytes(base::span<const int64_t> shape,
+                                     ONNXTensorElementDataType data_type) {
+  base::CheckedNumeric<uint64_t> element_size_in_bits;
+  switch (data_type) {
+    case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64:
+    case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64: {
+      element_size_in_bits = 64;
+      break;
+    }
+    case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT:
+    case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32:
+    case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32: {
+      element_size_in_bits = 32;
+      break;
+    }
+    case ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16: {
+      element_size_in_bits = 16;
+      break;
+    }
+    case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8:
+    case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8: {
+      element_size_in_bits = 8;
+      break;
+    }
+    case ONNX_TENSOR_ELEMENT_DATA_TYPE_INT4:
+    case ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT4: {
+      element_size_in_bits = 4;
+      break;
+    }
+    default: {
+      NOTREACHED()
+          << "CalculateOrtTensorSizeInBytes() only supports WebNN data types.";
+    }
+  }
+  auto tensor_size_in_bits = std::accumulate(
+      shape.begin(), shape.end(), element_size_in_bits, std::multiplies());
+
+  return ((tensor_size_in_bits + 7) / 8).ValueOrDie<size_t>();
+}
+
+ScopedOrtValueInfo CreateOrtValueInfo(base::cstring_view name,
+                                      const OperandDescriptor& descriptor) {
+  const OrtApi* ort_api = GetOrtApi();
+  ScopedOrtTensorTypeAndShapeInfo tensor_type_and_shape_info;
+  CHECK_STATUS(ort_api->CreateTensorTypeAndShapeInfo(
+      ScopedOrtTensorTypeAndShapeInfo::Receiver(tensor_type_and_shape_info)
+          .get()));
+  CHECK_STATUS(ort_api->SetTensorElementType(
+      tensor_type_and_shape_info.get(),
+      WebnnToOnnxDataType(descriptor.data_type())));
+
+  std::vector<int64_t> int64_shape = VectorUint32ToInt64(descriptor.shape());
+  CHECK_STATUS(ort_api->SetDimensions(tensor_type_and_shape_info.get(),
+                                      int64_shape.data(), int64_shape.size()));
+
+  const OrtModelEditorApi* ort_model_editor_api = GetOrtModelEditorApi();
+  ScopedOrtTypeInfo type_info;
+  CHECK_STATUS(ort_model_editor_api->CreateTensorTypeInfo(
+      tensor_type_and_shape_info.get(),
+      ScopedOrtTypeInfo::Receiver(type_info).get()));
+
+  ScopedOrtValueInfo value_info;
+  CHECK_STATUS(ort_model_editor_api->CreateValueInfo(
+      name.c_str(), type_info.get(),
+      ScopedOrtValueInfo::Receiver(value_info).get()));
+  return value_info;
+}
+
+}  // namespace
+
+ModelEditor::ModelInfo::ModelInfo() = default;
+ModelEditor::ModelInfo::~ModelInfo() = default;
+
+ModelEditor::ModelEditor() : model_info_(std::make_unique<ModelInfo>()) {
+  const OrtApi* ort_api = GetOrtApi();
+  // Create a CPU memory info, the constants will always be created in
+  // CPU memory.
+  CHECK_STATUS(ort_api->CreateCpuMemoryInfo(
+      OrtDeviceAllocator, OrtMemTypeDefault,
+      ScopedOrtMemoryInfo::Receiver(memory_info_).get()));
+
+  const OrtModelEditorApi* ort_model_editor_api = GetOrtModelEditorApi();
+  CHECK_STATUS(ort_model_editor_api->CreateGraph(
+      ScopedOrtGraph::Receiver(graph_).get()));
+}
+
+ModelEditor::~ModelEditor() = default;
+
+void ModelEditor::AddInput(base::cstring_view name,
+                           const OperandDescriptor& descriptor) {
+  CHECK(!has_built_);
+  inputs_.push_back(CreateOrtValueInfo(name, descriptor));
+}
+
+void ModelEditor::AddOutput(base::cstring_view name,
+                            const OperandDescriptor& descriptor) {
+  CHECK(!has_built_);
+  outputs_.push_back(CreateOrtValueInfo(name, descriptor));
+}
+
+void ModelEditor::AddInitializer(base::cstring_view name,
+                                 const WebNNConstantOperand& constant_operand) {
+  CHECK(!has_built_);
+
+  const OperandDescriptor& descriptor = constant_operand.descriptor();
+  AddInitializer(name, WebnnToOnnxDataType(descriptor.data_type()),
+                 VectorUint32ToInt64(descriptor.shape()),
+                 constant_operand.ByteSpan());
+}
+
+void ModelEditor::AddInitializer(base::cstring_view name,
+                                 ONNXTensorElementDataType data_type,
+                                 base::span<const int64_t> shape,
+                                 base::span<const uint8_t> data) {
+  CHECK(!has_built_);
+
+  bool use_external_data = data.size() >= kMinExternalDataSize;
+  if (use_external_data) {
+    AddInitializerAsExternalData(name, data_type, shape, data);
+  } else {
+    AddInitializerAsRawData(name, data_type, shape, data);
+  }
+}
+
+void ModelEditor::AddInitializerAsRawData(base::cstring_view name,
+                                          ONNXTensorElementDataType data_type,
+                                          base::span<const int64_t> shape,
+                                          base::span<const uint8_t> data) {
+  const OrtApi* ort_api = GetOrtApi();
+  // Get the default CPU allocator, as the initializers will be used during
+  // graph optimization which will happen on CPU.
+  OrtAllocator* allocator = nullptr;
+  CHECK_STATUS(ort_api->GetAllocatorWithDefaultOptions(&allocator));
+  CHECK(allocator);
+
+  ScopedOrtValue initializer;
+  CHECK_STATUS(ort_api->CreateTensorAsOrtValue(
+      allocator, shape.data(), shape.size(), data_type,
+      ScopedOrtValue::Receiver(initializer).get()));
+
+  void* mutable_data = nullptr;
+  CHECK_STATUS(ort_api->GetTensorMutableData(initializer.get(), &mutable_data));
+  // `mutable_data` can be nullptr when there is zero dimension in `shape`
+  // a.k.a. empty tensors. While WebNN doesn't support zero dimensions the ORT
+  // backend may need this feature in some cases, e.g., the ONNX Reshape op's
+  // "new shape" can be an empty tensor when reshaping the input tensor to a
+  // scalar.
+
+  // SAFETY: `mutable_data` was created to hold a tensor of `shape` and
+  // `data_type`.
+  UNSAFE_BUFFERS(base::span(static_cast<uint8_t*>(mutable_data),
+                            CalculateOrtTensorSizeInBytes(shape, data_type)))
+      .copy_from(data);
+
+  const OrtModelEditorApi* ort_model_editor_api = GetOrtModelEditorApi();
+  // Graph will own the initializer.
+  CHECK_STATUS(ort_model_editor_api->AddInitializerToGraph(
+      graph_.get(), name.c_str(), initializer.release(),
+      /*data_is_external=*/false));
+}
+
+void ModelEditor::AddInitializerAsExternalData(
+    base::cstring_view name,
+    ONNXTensorElementDataType data_type,
+    base::span<const int64_t> shape,
+    base::span<const uint8_t> data) {
+  // The data will not be copied into the graph, so it must be stored outside.
+  auto weight = base::HeapArray<uint8_t>::CopiedFrom(data);
+  model_info_->external_data.push_back(std::move(weight));
+
+  CHECK_EQ(data.size(), CalculateOrtTensorSizeInBytes(shape, data_type));
+
+  const OrtApi* ort_api = GetOrtApi();
+  ScopedOrtValue initializer;
+  // TODO(crbug.com/411465403): Use `CreateTensorWithDataAndDeleterAsOrtValue()`
+  // to let ORT take the ownership of the tensor and free it when no longer in
+  // use.
+  CHECK_STATUS(ort_api->CreateTensorWithDataAsOrtValue(
+      memory_info_.get(), model_info_->external_data.back().data(),
+      model_info_->external_data.back().size(), shape.data(), shape.size(),
+      data_type, ScopedOrtValue::Receiver(initializer).get()));
+
+  const OrtModelEditorApi* ort_model_editor_api = GetOrtModelEditorApi();
+  // Graph will own the initializer.
+  CHECK_STATUS(ort_model_editor_api->AddInitializerToGraph(
+      graph_.get(), name.c_str(), initializer.release(),
+      /*data_is_external=*/true));
+}
+
+ScopedOrtOpAttr ModelEditor::CreateAttribute(base::cstring_view name,
+                                             OrtOpAttrData data) {
+  CHECK(!has_built_);
+
+  const OrtApi* ort_api = GetOrtApi();
+  ScopedOrtOpAttr attribute;
+  std::visit(base::Overloaded{
+                 [&](int64_t int_data) {
+                   CHECK_STATUS(ort_api->CreateOpAttr(
+                       name.c_str(), &int_data,
+                       /*len=*/1, OrtOpAttrType::ORT_OP_ATTR_INT,
+                       ScopedOrtOpAttr::Receiver(attribute).get()));
+                 },
+                 [&](float float_data) {
+                   CHECK_STATUS(ort_api->CreateOpAttr(
+                       name.c_str(), &float_data,
+                       /*len=*/1, OrtOpAttrType::ORT_OP_ATTR_FLOAT,
+                       ScopedOrtOpAttr::Receiver(attribute).get()));
+                 },
+                 [&](base::cstring_view string_data) {
+                   CHECK_STATUS(ort_api->CreateOpAttr(
+                       name.c_str(), string_data.data(), string_data.size(),
+                       OrtOpAttrType::ORT_OP_ATTR_STRING,
+                       ScopedOrtOpAttr::Receiver(attribute).get()));
+                 },
+                 [&](base::span<const int64_t> ints_data) {
+                   CHECK_STATUS(ort_api->CreateOpAttr(
+                       name.c_str(), ints_data.data(), ints_data.size(),
+                       OrtOpAttrType::ORT_OP_ATTR_INTS,
+                       ScopedOrtOpAttr::Receiver(attribute).get()));
+                 },
+                 [&](base::span<const float> floats_data) {
+                   CHECK_STATUS(ort_api->CreateOpAttr(
+                       name.c_str(), floats_data.data(), floats_data.size(),
+                       OrtOpAttrType::ORT_OP_ATTR_FLOATS,
+                       ScopedOrtOpAttr::Receiver(attribute).get()));
+                 },
+                 [&](base::span<const char*> strings_data) {
+                   CHECK_STATUS(ort_api->CreateOpAttr(
+                       name.c_str(), strings_data.data(), strings_data.size(),
+                       OrtOpAttrType::ORT_OP_ATTR_STRINGS,
+                       ScopedOrtOpAttr::Receiver(attribute).get()));
+                 }},
+             data);
+
+  return attribute;
+}
+
+void ModelEditor::AddNode(base::cstring_view op_type,
+                          base::cstring_view node_name,
+                          base::span<const char*> inputs,
+                          base::span<const char*> outputs,
+                          base::span<ScopedOrtOpAttr> attributes) {
+  CHECK(!has_built_);
+
+  base::FixedArray<OrtOpAttr*> node_attrs(attributes.size());
+  std::ranges::transform(attributes, node_attrs.begin(),
+                         [](auto& attr) { return attr.release(); });
+
+  const OrtModelEditorApi* ort_model_editor_api = GetOrtModelEditorApi();
+  // Node will own the attributes.
+  ScopedOrtNode node;
+  CHECK_STATUS(ort_model_editor_api->CreateNode(
+      op_type.c_str(), kOrtDefaultDomain, node_name.c_str(), inputs.data(),
+      inputs.size(), outputs.data(), outputs.size(), node_attrs.data(),
+      node_attrs.size(), ScopedOrtNode::Receiver(node).get()));
+  // Graph will own the node.
+  CHECK_STATUS(
+      ort_model_editor_api->AddNodeToGraph(graph_.get(), node.release()));
+}
+
+std::unique_ptr<ModelEditor::ModelInfo> ModelEditor::BuildAndTakeModelInfo() {
+  CHECK(!has_built_);
+
+  const OrtModelEditorApi* ort_model_editor_api = GetOrtModelEditorApi();
+  // Graph will own the inputs and outputs.
+  base::FixedArray<OrtValueInfo*> graph_inputs(inputs_.size());
+  std::ranges::transform(inputs_, graph_inputs.begin(),
+                         [](auto& input) { return input.release(); });
+  CHECK_STATUS(ort_model_editor_api->SetGraphInputs(
+      graph_.get(), graph_inputs.data(), graph_inputs.size()));
+
+  base::FixedArray<OrtValueInfo*> graph_outputs(outputs_.size());
+  std::ranges::transform(outputs_, graph_outputs.begin(),
+                         [](auto& output) { return output.release(); });
+  CHECK_STATUS(ort_model_editor_api->SetGraphOutputs(
+      graph_.get(), graph_outputs.data(), graph_outputs.size()));
+
+  std::array<const char*, 2> domains = {kOrtDefaultDomain, kMSDomain};
+  std::array<int32_t, 2> opset_versions = {kOrtOpsetVersion,
+                                           kEPContextOpsetVersion};
+  CHECK_STATUS(ort_model_editor_api->CreateModel(
+      domains.data(), opset_versions.data(), domains.size(),
+      ScopedOrtModel::Receiver(model_info_->model).get()));
+
+  // Model will own the graph.
+  CHECK_STATUS(ort_model_editor_api->AddGraphToModel(model_info_->model.get(),
+                                                     graph_.release()));
+
+  has_built_ = true;
+
+  return std::move(model_info_);
+}
+
+}  // namespace webnn::ort
diff --git a/services/webnn/ort/model_editor.h b/services/webnn/ort/model_editor.h
new file mode 100644
index 0000000..7c8016f
--- /dev/null
+++ b/services/webnn/ort/model_editor.h
@@ -0,0 +1,106 @@
+// 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 SERVICES_WEBNN_ORT_MODEL_EDITOR_H_
+#define SERVICES_WEBNN_ORT_MODEL_EDITOR_H_
+
+#include <memory>
+#include <variant>
+#include <vector>
+
+#include "base/component_export.h"
+#include "base/containers/heap_array.h"
+#include "base/containers/span.h"
+#include "base/strings/cstring_view.h"
+#include "services/webnn/ort/scoped_ort_types.h"
+#include "services/webnn/public/cpp/operand_descriptor.h"
+#include "services/webnn/webnn_constant_operand.h"
+
+namespace webnn::ort {
+
+class COMPONENT_EXPORT(WEBNN_SERVICE) ModelEditor {
+ public:
+  struct COMPONENT_EXPORT(WEBNN_SERVICE) ModelInfo {
+    ModelInfo();
+    ModelInfo(const ModelInfo&) = delete;
+    ModelInfo& operator=(const ModelInfo&) = delete;
+    ~ModelInfo();
+
+    ScopedOrtModel model;
+    // The external data should be kept alive during graph inferencing.
+    std::vector<base::HeapArray<uint8_t>> external_data;
+  };
+
+  ModelEditor();
+  ~ModelEditor();
+  ModelEditor(const ModelEditor&) = delete;
+  ModelEditor& operator=(const ModelEditor&) = delete;
+
+  void AddInput(base::cstring_view name, const OperandDescriptor& descriptor);
+
+  void AddOutput(base::cstring_view name, const OperandDescriptor& descriptor);
+
+  void AddInitializer(base::cstring_view name,
+                      const WebNNConstantOperand& constant_operand);
+
+  // Add an initializer directly into the ONNX model.
+  // This method could be useful for converting a WebNN operator's attribute to
+  // an ONNX model initializer. For example, the reshape operator's new shape is
+  // a tensor/initializer input in ONNX spec.
+  void AddInitializer(base::cstring_view name,
+                      ONNXTensorElementDataType data_type,
+                      base::span<const int64_t> shape,
+                      base::span<const uint8_t> data);
+
+  using OrtOpAttrData = std::variant<int64_t,
+                                     float,
+                                     base::cstring_view,
+                                     base::span<const int64_t>,
+                                     base::span<const float>,
+                                     base::span<const char*>>;
+  ScopedOrtOpAttr CreateAttribute(base::cstring_view name, OrtOpAttrData data);
+
+  // The ownership of `attributes` will be transferred and should not be used
+  // after this call.
+  void AddNode(base::cstring_view op_type,
+               base::cstring_view node_name,
+               base::span<const char*> inputs,
+               base::span<const char*> outputs,
+               base::span<ScopedOrtOpAttr> attributes = {});
+
+  // No further methods should be called on this class after calling this
+  // method.
+  std::unique_ptr<ModelInfo> BuildAndTakeModelInfo();
+
+ private:
+  // Add an initializer and copy the data into the graph.
+  void AddInitializerAsRawData(base::cstring_view name,
+                               ONNXTensorElementDataType data_type,
+                               base::span<const int64_t> shape,
+                               base::span<const uint8_t> data);
+
+  // Add an initializer and copy the data into `ModelInfo::external_data`.
+  // TODO(crbug.com/411452041): Consider transferring the constant data instead
+  // of copying.
+  void AddInitializerAsExternalData(base::cstring_view name,
+                                    ONNXTensorElementDataType data_type,
+                                    base::span<const int64_t> shape,
+                                    base::span<const uint8_t> data);
+
+  // Describes where the constant buffer resides in memory.
+  ScopedOrtMemoryInfo memory_info_;
+
+  std::vector<ScopedOrtValueInfo> inputs_;
+  std::vector<ScopedOrtValueInfo> outputs_;
+
+  ScopedOrtGraph graph_;
+
+  std::unique_ptr<ModelInfo> model_info_;
+
+  bool has_built_ = false;
+};
+
+}  // namespace webnn::ort
+
+#endif  // SERVICES_WEBNN_ORT_MODEL_EDITOR_H_
diff --git a/services/webnn/ort/model_editor_test.cc b/services/webnn/ort/model_editor_test.cc
new file mode 100644
index 0000000..48a3c89
--- /dev/null
+++ b/services/webnn/ort/model_editor_test.cc
@@ -0,0 +1,136 @@
+// 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 "services/webnn/ort/model_editor.h"
+
+#include <array>
+#include <memory>
+#include <vector>
+
+#include "base/containers/heap_array.h"
+#include "base/strings/cstring_view.h"
+#include "services/webnn/ort/scoped_ort_types.h"
+#include "services/webnn/ort/test_base_ort.h"
+#include "services/webnn/public/cpp/operand_descriptor.h"
+#include "services/webnn/webnn_constant_operand.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace webnn::ort {
+
+class WebNNOrtModelEditorTest : public TestBaseOrt {};
+
+TEST_F(WebNNOrtModelEditorTest, AddAndGather) {
+  ModelEditor model_editor;
+
+  // Add an input.
+  constexpr base::cstring_view input = "input";
+  auto input_desc = OperandDescriptor::CreateForDeserialization(
+      OperandDataType::kUint32, {4, 2, 4});
+  ASSERT_TRUE(input_desc.has_value());
+  model_editor.AddInput(input, input_desc.value());
+
+  // Add two initializers.
+  constexpr base::cstring_view add_initializer = "add_initializer";
+  std::array<uint32_t, 32> add_initializer_data;  // 128 bytes
+  add_initializer_data.fill(1);
+  auto add_initializer_desc = OperandDescriptor::CreateForDeserialization(
+      OperandDataType::kUint32, {4, 2, 4});
+  ASSERT_TRUE(add_initializer_desc.has_value());
+  WebNNConstantOperand add_initializer_operand(
+      std::move(add_initializer_desc.value()),
+      base::HeapArray<uint8_t>::CopiedFrom(
+          base::as_byte_span(add_initializer_data)));
+  model_editor.AddInitializer(add_initializer, add_initializer_operand);
+
+  constexpr base::cstring_view gather_indices_initializer =
+      "gather_indices_initializer";
+  std::array<int64_t, 4> gather_indices_initializer_data = {0, 1, 0,
+                                                            1};  // 32 bytes
+  auto gather_indices_initializer_desc =
+      OperandDescriptor::CreateForDeserialization(OperandDataType::kInt64, {4});
+  ASSERT_TRUE(gather_indices_initializer_desc.has_value());
+  WebNNConstantOperand gather_indices_initializer_operand(
+      std::move(gather_indices_initializer_desc.value()),
+      base::HeapArray<uint8_t>::CopiedFrom(
+          base::as_byte_span(gather_indices_initializer_data)));
+  model_editor.AddInitializer(gather_indices_initializer,
+                              gather_indices_initializer_operand);
+
+  // Add Add node.
+  constexpr base::cstring_view add_output = "add_output";
+  std::array<const char*, 2> add_inputs = {input.c_str(),
+                                           add_initializer.c_str()};
+  std::array<const char*, 1> add_outputs = {add_output.c_str()};
+  model_editor.AddNode(
+      /*op_type=*/"Add", /*node_name=*/"add", add_inputs, add_outputs);
+
+  // Add Gather node.
+  int64_t axis = 1;
+  std::array<ScopedOrtOpAttr, 1> gather_attrs = {
+      model_editor.CreateAttribute(/*name=*/"axis", axis)};
+
+  constexpr base::cstring_view output = "output";
+  std::array<const char*, 2> gather_inputs = {
+      add_output.c_str(), gather_indices_initializer.c_str()};
+  std::array<const char*, 1> gather_outputs = {output.c_str()};
+  model_editor.AddNode(
+      /*op_type=*/"Gather", /*node_name=*/"gather", gather_inputs,
+      gather_outputs, gather_attrs);
+
+  // Add an output.
+  model_editor.AddOutput(output, OperandDescriptor::CreateForDeserialization(
+                                     OperandDataType::kUint32, {4, 4, 4})
+                                     .value());
+
+  std::unique_ptr<ModelEditor::ModelInfo> model_info =
+      model_editor.BuildAndTakeModelInfo();
+  ASSERT_NE(model_info, nullptr);
+
+  // `add_initializer` data should be saved into `external_data` since it
+  // reaches `kMinExternalDataSize`.
+  EXPECT_EQ(model_info->external_data.size(), 1u);
+  EXPECT_TRUE(model_info->model.is_valid());
+}
+
+TEST_F(WebNNOrtModelEditorTest, ReshapeToScalar) {
+  ModelEditor model_editor;
+
+  // Add an input.
+  constexpr base::cstring_view input = "input";
+  auto input_desc = OperandDescriptor::CreateForDeserialization(
+      OperandDataType::kInt32, {1, 1, 1, 1});
+  ASSERT_TRUE(input_desc.has_value());
+  model_editor.AddInput(input, input_desc.value());
+
+  // Add an initializer which is an empty tensor that represents an empty shape
+  // of a scalar.
+  constexpr base::cstring_view new_shape_initializer = "new_shape_initializer";
+  model_editor.AddInitializer(new_shape_initializer,
+                              ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32,
+                              /*shape=*/{0}, /*data=*/{});
+
+  // Add Reshape node.
+  constexpr base::cstring_view output = "output";
+  std::array<const char*, 2> reshape_inputs = {input.c_str(),
+                                               new_shape_initializer.c_str()};
+  std::array<const char*, 1> reshape_outputs = {output.c_str()};
+  model_editor.AddNode(
+      /*op_type=*/"Reshape", /*node_name=*/"reshape", reshape_inputs,
+      reshape_outputs);
+
+  // Add an output.
+  auto output_desc =
+      OperandDescriptor::CreateForDeserialization(OperandDataType::kInt32, {});
+  ASSERT_TRUE(output_desc.has_value());
+  model_editor.AddOutput(output, output_desc.value());
+
+  std::unique_ptr<ModelEditor::ModelInfo> model_info =
+      model_editor.BuildAndTakeModelInfo();
+  ASSERT_NE(model_info, nullptr);
+
+  EXPECT_EQ(model_info->external_data.size(), 0u);
+  EXPECT_TRUE(model_info->model.is_valid());
+}
+
+}  // namespace webnn::ort
diff --git a/services/webnn/ort/ort_data_type.cc b/services/webnn/ort/ort_data_type.cc
new file mode 100644
index 0000000..238b705c
--- /dev/null
+++ b/services/webnn/ort/ort_data_type.cc
@@ -0,0 +1,34 @@
+// 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 "services/webnn/ort/ort_data_type.h"
+
+namespace webnn::ort {
+
+ONNXTensorElementDataType WebnnToOnnxDataType(OperandDataType data_type) {
+  switch (data_type) {
+    case OperandDataType::kFloat32:
+      return ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT;
+    case OperandDataType::kFloat16:
+      return ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT16;
+    case OperandDataType::kInt32:
+      return ONNX_TENSOR_ELEMENT_DATA_TYPE_INT32;
+    case OperandDataType::kUint32:
+      return ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT32;
+    case OperandDataType::kInt64:
+      return ONNX_TENSOR_ELEMENT_DATA_TYPE_INT64;
+    case OperandDataType::kUint64:
+      return ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT64;
+    case OperandDataType::kInt8:
+      return ONNX_TENSOR_ELEMENT_DATA_TYPE_INT8;
+    case OperandDataType::kUint8:
+      return ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT8;
+    case OperandDataType::kInt4:
+      return ONNX_TENSOR_ELEMENT_DATA_TYPE_INT4;
+    case OperandDataType::kUint4:
+      return ONNX_TENSOR_ELEMENT_DATA_TYPE_UINT4;
+  }
+}
+
+}  // namespace webnn::ort
diff --git a/services/webnn/ort/ort_data_type.h b/services/webnn/ort/ort_data_type.h
new file mode 100644
index 0000000..3946df89
--- /dev/null
+++ b/services/webnn/ort/ort_data_type.h
@@ -0,0 +1,17 @@
+// 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 SERVICES_WEBNN_ORT_ORT_DATA_TYPE_H_
+#define SERVICES_WEBNN_ORT_ORT_DATA_TYPE_H_
+
+#include "services/webnn/public/cpp/operand_descriptor.h"
+#include "third_party/onnxruntime_headers/src/include/onnxruntime/core/session/onnxruntime_c_api.h"
+
+namespace webnn::ort {
+
+ONNXTensorElementDataType WebnnToOnnxDataType(OperandDataType data_type);
+
+}  // namespace webnn::ort
+
+#endif  // SERVICES_WEBNN_ORT_ORT_DATA_TYPE_H_
diff --git a/services/webnn/ort/ort_status.cc b/services/webnn/ort/ort_status.cc
new file mode 100644
index 0000000..2182761
--- /dev/null
+++ b/services/webnn/ort/ort_status.cc
@@ -0,0 +1,30 @@
+// 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 "services/webnn/ort/ort_status.h"
+
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "services/webnn/ort/platform_functions_ort.h"
+#include "third_party/onnxruntime_headers/src/include/onnxruntime/core/session/onnxruntime_c_api.h"
+
+namespace webnn::ort {
+
+namespace internal {
+
+std::string OrtStatusFatalMessage(OrtStatus* status) {
+  CHECK(status);
+
+  constexpr char kOrtErrorCode[] = "[WebNN] ORT status error code: ";
+  constexpr char kOrtErrorMessage[] = " error message: ";
+  const OrtApi* ort_api = PlatformFunctions::GetInstance()->ort_api();
+  return base::StrCat(
+      {kOrtErrorCode,
+       base::NumberToString(static_cast<int>(ort_api->GetErrorCode(status))),
+       kOrtErrorMessage, ort_api->GetErrorMessage(status)});
+}
+
+}  // namespace internal
+
+}  // namespace webnn::ort
diff --git a/services/webnn/ort/ort_status.h b/services/webnn/ort/ort_status.h
new file mode 100644
index 0000000..ce27817
--- /dev/null
+++ b/services/webnn/ort/ort_status.h
@@ -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.
+
+#ifndef SERVICES_WEBNN_ORT_ORT_STATUS_H_
+#define SERVICES_WEBNN_ORT_ORT_STATUS_H_
+
+#include <string>
+
+#include "base/logging.h"
+
+struct OrtStatus;
+
+namespace webnn::ort {
+
+namespace internal {
+
+std::string OrtStatusFatalMessage(OrtStatus* status);
+
+}  // namespace internal
+
+#define CHECK_STATUS(expr)                                 \
+  if (OrtStatus* status = (expr)) {                        \
+    LOG(FATAL) << internal::OrtStatusFatalMessage(status); \
+  }
+
+}  // namespace webnn::ort
+
+#endif  // SERVICES_WEBNN_ORT_ORT_STATUS_H_
diff --git a/services/webnn/ort/platform_functions_ort_test.cc b/services/webnn/ort/platform_functions_ort_test.cc
index 9dff93e6..4fa4004 100644
--- a/services/webnn/ort/platform_functions_ort_test.cc
+++ b/services/webnn/ort/platform_functions_ort_test.cc
@@ -9,18 +9,7 @@
 
 namespace webnn::ort {
 
-class WebNNOrtPlatformFunctionsTest : public testing::Test {
- public:
-  void SetUp() override;
-};
-
-void WebNNOrtPlatformFunctionsTest::SetUp() {
-  // Skip tests if the loading platform functions fail.
-  // In order to be able to run this test suite successfully, the developer
-  // needs to place a copy of onnxruntime.dll which supports ORT_API_VERSION
-  // defined in onnxruntime_c_api.h into Chromium module folder before.
-  SKIP_TEST_IF(!PlatformFunctions::GetInstance());
-}
+class WebNNOrtPlatformFunctionsTest : public TestBaseOrt {};
 
 TEST_F(WebNNOrtPlatformFunctionsTest, AllFunctionsLoaded) {
   PlatformFunctions* platformFunctions = PlatformFunctions::GetInstance();
diff --git a/services/webnn/ort/scoped_ort_types.h b/services/webnn/ort/scoped_ort_types.h
new file mode 100644
index 0000000..3b75221b
--- /dev/null
+++ b/services/webnn/ort/scoped_ort_types.h
@@ -0,0 +1,111 @@
+// 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 SERVICES_WEBNN_ORT_SCOPED_ORT_TYPES_H_
+#define SERVICES_WEBNN_ORT_SCOPED_ORT_TYPES_H_
+
+#include <type_traits>
+
+#include "base/scoped_generic.h"
+#include "services/webnn/ort/platform_functions_ort.h"
+#include "third_party/onnxruntime_headers/src/include/onnxruntime/core/session/onnxruntime_c_api.h"
+
+namespace webnn::ort {
+
+namespace internal {
+
+template <typename T>
+  requires std::is_pointer<T>::value
+struct ScopedOrtTypeTraitsHelper;
+
+template <typename T>
+  requires std::is_pointer<T>::value
+struct ScopedOrtTypeTraits {
+  static T InvalidValue() { return nullptr; }
+  static void Free(T value) { ScopedOrtTypeTraitsHelper<T>::Free(value); }
+};
+
+template <>
+struct ScopedOrtTypeTraitsHelper<OrtValue*> {
+  static void Free(OrtValue* value) {
+    PlatformFunctions::GetInstance()->ort_api()->ReleaseValue(value);
+  }
+};
+
+template <>
+struct ScopedOrtTypeTraitsHelper<OrtMemoryInfo*> {
+  static void Free(OrtMemoryInfo* value) {
+    PlatformFunctions::GetInstance()->ort_api()->ReleaseMemoryInfo(value);
+  }
+};
+
+template <>
+struct ScopedOrtTypeTraitsHelper<OrtOpAttr*> {
+  static void Free(OrtOpAttr* value) {
+    PlatformFunctions::GetInstance()->ort_api()->ReleaseOpAttr(value);
+  }
+};
+
+template <>
+struct ScopedOrtTypeTraitsHelper<OrtTypeInfo*> {
+  static void Free(OrtTypeInfo* value) {
+    PlatformFunctions::GetInstance()->ort_api()->ReleaseTypeInfo(value);
+  }
+};
+
+template <>
+struct ScopedOrtTypeTraitsHelper<OrtTensorTypeAndShapeInfo*> {
+  static void Free(OrtTensorTypeAndShapeInfo* value) {
+    PlatformFunctions::GetInstance()->ort_api()->ReleaseTensorTypeAndShapeInfo(
+        value);
+  }
+};
+
+template <>
+struct ScopedOrtTypeTraitsHelper<OrtValueInfo*> {
+  static void Free(OrtValueInfo* value) {
+    PlatformFunctions::GetInstance()->ort_api()->ReleaseValueInfo(value);
+  }
+};
+
+template <>
+struct ScopedOrtTypeTraitsHelper<OrtNode*> {
+  static void Free(OrtNode* value) {
+    PlatformFunctions::GetInstance()->ort_api()->ReleaseNode(value);
+  }
+};
+
+template <>
+struct ScopedOrtTypeTraitsHelper<OrtGraph*> {
+  static void Free(OrtGraph* value) {
+    PlatformFunctions::GetInstance()->ort_api()->ReleaseGraph(value);
+  }
+};
+
+template <>
+struct ScopedOrtTypeTraitsHelper<OrtModel*> {
+  static void Free(OrtModel* value) {
+    PlatformFunctions::GetInstance()->ort_api()->ReleaseModel(value);
+  }
+};
+
+template <typename T>
+using ScopedOrtType = base::ScopedGeneric<T*, ScopedOrtTypeTraits<T*>>;
+
+}  // namespace internal
+
+using ScopedOrtValue = internal::ScopedOrtType<OrtValue>;
+using ScopedOrtMemoryInfo = internal::ScopedOrtType<OrtMemoryInfo>;
+using ScopedOrtOpAttr = internal::ScopedOrtType<OrtOpAttr>;
+using ScopedOrtTypeInfo = internal::ScopedOrtType<OrtTypeInfo>;
+using ScopedOrtTensorTypeAndShapeInfo =
+    internal::ScopedOrtType<OrtTensorTypeAndShapeInfo>;
+using ScopedOrtValueInfo = internal::ScopedOrtType<OrtValueInfo>;
+using ScopedOrtNode = internal::ScopedOrtType<OrtNode>;
+using ScopedOrtGraph = internal::ScopedOrtType<OrtGraph>;
+using ScopedOrtModel = internal::ScopedOrtType<OrtModel>;
+
+}  // namespace webnn::ort
+
+#endif  // SERVICES_WEBNN_ORT_SCOPED_ORT_TYPES_H_
diff --git a/services/webnn/ort/test_base_ort.cc b/services/webnn/ort/test_base_ort.cc
new file mode 100644
index 0000000..5fa6276
--- /dev/null
+++ b/services/webnn/ort/test_base_ort.cc
@@ -0,0 +1,21 @@
+// 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 "services/webnn/ort/test_base_ort.h"
+
+#include "services/webnn/ort/platform_functions_ort.h"
+
+namespace webnn::ort {
+
+void TestBaseOrt::SetUp() {
+  // Skip tests if the loading platform functions fail.
+  // In order to be able to run this test suite successfully, the developer
+  // needs to place a copy of onnxruntime.dll which supports ORT_API_VERSION
+  // defined in onnxruntime_c_api.h into Chromium module folder before.
+  if (!PlatformFunctions::GetInstance()) {
+    GTEST_SKIP() << "!PlatformFunctions::GetInstance()";
+  }
+}
+
+}  // namespace webnn::ort
diff --git a/services/webnn/ort/test_base_ort.h b/services/webnn/ort/test_base_ort.h
index 854d64af..6adcabe1 100644
--- a/services/webnn/ort/test_base_ort.h
+++ b/services/webnn/ort/test_base_ort.h
@@ -7,11 +7,13 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 
-// GTEST_SKIP() will let method return directly.
-#define SKIP_TEST_IF(condition)   \
-  do {                            \
-    if (condition)                \
-      GTEST_SKIP() << #condition; \
-  } while (0)
+namespace webnn::ort {
+
+class TestBaseOrt : public testing::Test {
+ public:
+  void SetUp() override;
+};
+
+}  // namespace webnn::ort
 
 #endif  // SERVICES_WEBNN_ORT_TEST_BASE_ORT_H_
diff --git a/services/webnn/public/cpp/webnn_trace_unittest.cc b/services/webnn/public/cpp/webnn_trace_unittest.cc
index b7250ae..b77bf63 100644
--- a/services/webnn/public/cpp/webnn_trace_unittest.cc
+++ b/services/webnn/public/cpp/webnn_trace_unittest.cc
@@ -20,6 +20,7 @@
 #include "base/trace_event/trace_event.h"
 #include "base/trace_event/trace_log.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
 
 namespace webnn {
 
@@ -59,8 +60,8 @@
 
   // End tracing, return tracing data in a map of event
   // name->(begin_event_counts, end_event_counts)
-  std::map<std::string, std::pair<int, int>> EndTracing() {
-    std::map<std::string, std::pair<int, int>> event_counts;
+  absl::flat_hash_map<std::string, std::pair<int, int>> EndTracing() {
+    absl::flat_hash_map<std::string, std::pair<int, int>> event_counts;
     base::trace_event::TraceResultBuffer::SimpleOutput json_data;
     base::trace_event::TraceLog::GetInstance()->SetDisabled();
     base::RunLoop run_loop;
diff --git a/services/webnn/tflite/graph_builder_tflite.h b/services/webnn/tflite/graph_builder_tflite.h
index bf39351..f986301 100644
--- a/services/webnn/tflite/graph_builder_tflite.h
+++ b/services/webnn/tflite/graph_builder_tflite.h
@@ -24,6 +24,7 @@
 #include "services/webnn/public/cpp/webnn_types.h"
 #include "services/webnn/public/mojom/webnn_context_provider.mojom-forward.h"
 #include "services/webnn/public/mojom/webnn_graph.mojom-forward.h"
+#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
 #include "third_party/flatbuffers/src/include/flatbuffers/flatbuffers.h"
 #include "third_party/tflite/src/tensorflow/compiler/mlir/lite/schema/schema_generated.h"
 
@@ -825,7 +826,7 @@
   //              |                                      Relu
   //           [output]                                   |
   //                                                   [output]
-  std::map<OperandId, TensorInfo> operand_to_tensor_info_map_;
+  absl::flat_hash_map<OperandId, TensorInfo> operand_to_tensor_info_map_;
 
   // The following std::vector<Offset<tflite:XXX>>> stores the weights of model
   // and the tensor information (shape, data type).
diff --git a/services/webnn/webnn_context_provider_impl.cc b/services/webnn/webnn_context_provider_impl.cc
index fe7c12b..a3b30e81 100644
--- a/services/webnn/webnn_context_provider_impl.cc
+++ b/services/webnn/webnn_context_provider_impl.cc
@@ -98,7 +98,8 @@
 }
 
 // static
-void WebNNContextProviderImpl::CreateForTesting(
+base::optional_ref<WebNNContextProviderImpl>
+WebNNContextProviderImpl::CreateForTesting(
     mojo::PendingReceiver<mojom::WebNNContextProvider> receiver,
     WebNNStatus status,
     LoseAllContextsCallback lose_all_contexts_callback) {
@@ -123,11 +124,14 @@
         DISABLE_WEBNN_FOR_NPU);
   }
 
-  mojo::MakeSelfOwnedReceiver<WebNNContextProvider>(
-      base::WrapUnique(new WebNNContextProviderImpl(
-          /*shared_context_state=*/nullptr, std::move(gpu_feature_info),
-          std::move(gpu_info), std::move(lose_all_contexts_callback))),
-      std::move(receiver));
+  // Cast is safe because only a WebNNContextProviderImpl can be created.
+  return static_cast<WebNNContextProviderImpl*>(
+      mojo::MakeSelfOwnedReceiver<WebNNContextProvider>(
+          base::WrapUnique(new WebNNContextProviderImpl(
+              /*shared_context_state=*/nullptr, std::move(gpu_feature_info),
+              std::move(gpu_info), std::move(lose_all_contexts_callback))),
+          std::move(receiver))
+          ->impl());
 }
 
 void WebNNContextProviderImpl::OnConnectionError(WebNNContextImpl* impl) {
@@ -220,4 +224,16 @@
       mojom::CreateContextResult::NewSuccess(std::move(success)));
 }
 
+base::optional_ref<WebNNContextImpl>
+WebNNContextProviderImpl::GetWebNNContextImplForTesting(
+    const blink::WebNNContextToken& handle) {
+  CHECK_IS_TEST();
+  const auto it = impls_.find(handle);
+  if (it == impls_.end()) {
+    mojo::ReportBadMessage(kBadMessageInvalidContext);
+    return std::nullopt;
+  }
+  return it->get();
+}
+
 }  // namespace webnn
diff --git a/services/webnn/webnn_context_provider_impl.h b/services/webnn/webnn_context_provider_impl.h
index 3c1d4fd9..492404f 100644
--- a/services/webnn/webnn_context_provider_impl.h
+++ b/services/webnn/webnn_context_provider_impl.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "base/component_export.h"
+#include "base/types/optional_ref.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
 #include "gpu/config/gpu_feature_info.h"
 #include "gpu/config/gpu_info.h"
@@ -54,7 +55,10 @@
     kWebNNEnabled = 3,
   };
 
-  static void CreateForTesting(
+  // Called to create a WebNNContextProviderImpl as a self-owned receiver.
+  // Optionally returns a reference to the WebNNContextProviderImpl to test
+  // interop.
+  static base::optional_ref<WebNNContextProviderImpl> CreateForTesting(
       mojo::PendingReceiver<mojom::WebNNContextProvider> receiver,
       WebNNStatus status = WebNNStatus::kWebNNEnabled,
       LoseAllContextsCallback lose_all_contexts_callback = base::BindOnce([]() {
@@ -69,6 +73,12 @@
   // process to destroy all contexts.
   void DestroyContextsAndKillGpuProcess(std::string_view reason);
 #endif  // BUILDFLAG(IS_WIN)
+
+  // Retrieves a `WebNNContextImpl` instance created from this provider.
+  // Emits a bad message if a context with the given handle does not exist.
+  base::optional_ref<WebNNContextImpl> GetWebNNContextImplForTesting(
+      const blink::WebNNContextToken& handle);
+
   using WebNNContextImplSet = base::flat_set<
       std::unique_ptr<WebNNContextImpl>,
       WebNNObjectImpl<blink::WebNNContextToken>::Comparator<WebNNContextImpl>>;
diff --git a/services/webnn/webnn_tensor_impl_backend_test.cc b/services/webnn/webnn_tensor_impl_backend_test.cc
index dbc07d3..871fb39 100644
--- a/services/webnn/webnn_tensor_impl_backend_test.cc
+++ b/services/webnn/webnn_tensor_impl_backend_test.cc
@@ -23,12 +23,18 @@
 #include "services/webnn/public/mojom/webnn_context.mojom.h"
 #include "services/webnn/public/mojom/webnn_context_provider.mojom.h"
 #include "services/webnn/public/mojom/webnn_tensor.mojom.h"
+#include "services/webnn/webnn_context_impl.h"
 #include "services/webnn/webnn_context_provider_impl.h"
+#include "services/webnn/webnn_tensor_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "services/webnn/dml/adapter.h"
+#include "services/webnn/dml/command_queue.h"
+#include "services/webnn/dml/command_recorder.h"
+#include "services/webnn/dml/tensor_impl_dml.h"
 #include "services/webnn/dml/test_base.h"
+#include "services/webnn/dml/utils.h"
 #endif  // BUILDFLAG(IS_WIN)
 
 #if BUILDFLAG(IS_MAC)
@@ -89,6 +95,7 @@
 
   base::test::ScopedFeatureList scoped_feature_list_;
   scoped_refptr<dml::Adapter> adapter_;
+  raw_ptr<WebNNContextProviderImpl, DanglingUntriaged> provider_impl_ = nullptr;
   mojo::Remote<mojom::WebNNContextProvider> webnn_provider_remote_;
 };
 
@@ -105,9 +112,12 @@
   // DirectML version 1.2 or DML_FEATURE_LEVEL_2_1, so skip the tests if the
   // DirectML version doesn't support this feature.
   SKIP_TEST_IF(!adapter_->IsDMLDeviceCompileGraphSupportedForTesting());
-
-  WebNNContextProviderImpl::CreateForTesting(
-      webnn_provider_remote_.BindNewPipeAndPassReceiver());
+  // Testing WebGpuInterop usage relies on the WebNNContextProvider
+  // interface implementation in order to lookup WebNNTensorImpls from
+  // non-WebNNContextProviderImpls.
+  provider_impl_ = WebNNContextProviderImpl::CreateForTesting(
+                       webnn_provider_remote_.BindNewPipeAndPassReceiver())
+                       .as_ptr();
 }
 #elif BUILDFLAG(IS_MAC)
 class WebNNTensorImplBackendTest : public testing::Test {
@@ -424,6 +434,392 @@
   EXPECT_FALSE(bad_message_helper.GetLastBadMessage().has_value());
 }
 
+// Testing for WebGPUInterop requires backend-specific APIs to
+// synchronize contents and simulate usage from another command queue.
+#if BUILDFLAG(IS_WIN)
+
+class WebNNTensorImplDmlBackendTest : public WebNNTensorImplBackendTest {
+ public:
+  void SetUp() override {
+    WebNNTensorImplBackendTest::SetUp();
+
+    if (!webnn_provider_remote_.is_bound()) {
+      GTEST_SKIP() << "WebNN not supported on this platform.";
+    }
+
+    base::expected<CreateContextSuccess, webnn::mojom::Error::Code>
+        context_result = CreateWebNNContext();
+    if (!context_result.has_value() &&
+        context_result.error() == mojom::Error::Code::kNotSupportedError) {
+      GTEST_SKIP() << "WebNN not supported on this platform.";
+    } else {
+      webnn_context_remote_ =
+          std::move(context_result.value().webnn_context_remote);
+      webnn_context_handle_ =
+          std::move(context_result.value().webnn_context_handle);
+    }
+
+    ASSERT_TRUE(webnn_context_remote_.is_bound());
+  }
+
+  base::WeakPtr<native::d3d12::WebNNTensor> GetWebNNTensor(
+      const blink::WebNNTensorToken& webnn_tensor_handle) const {
+    base::optional_ref<WebNNContextImpl> context_impl =
+        provider_impl_->GetWebNNContextImplForTesting(webnn_context_handle_);
+    return static_cast<dml::TensorImplDml*>(
+               context_impl->GetWebNNTensorImpl(webnn_tensor_handle).as_ptr())
+        ->AsWeakPtr();
+  }
+
+ protected:
+  mojo::Remote<mojom::WebNNContext> webnn_context_remote_;
+  blink::WebNNContextToken webnn_context_handle_;
+};
+
+void WriteTensorData(base::span<const uint8_t> src_data,
+                     ID3D12Resource* dst_buffer) {
+  void* mapped_upload_data = nullptr;
+  ASSERT_HRESULT_SUCCEEDED(dst_buffer->Map(0, nullptr, &mapped_upload_data));
+  // SAFETY: `dst_buffer` was constructed with size `src_data.size()`.
+  UNSAFE_BUFFERS(
+      base::span(static_cast<uint8_t*>(mapped_upload_data), src_data.size()))
+      .copy_from(src_data);
+  dst_buffer->Unmap(0, nullptr);
+}
+
+bool IsFenceCompleted(ID3D12Fence* fence, uint64_t fence_value) {
+  return fence->GetCompletedValue() >= fence_value;
+}
+
+// Verify calling end access twice outputs the same fence and resource.
+TEST_F(WebNNTensorImplDmlBackendTest, EndAccessWebNNTwiceTest) {
+  BadMessageTestHelper bad_message_helper;
+
+  mojo::AssociatedRemote<mojom::WebNNTensor> webnn_tensor_remote;
+  blink::WebNNTensorToken webnn_tensor_handle;
+  base::expected<CreateTensorSuccess, webnn::mojom::Error::Code>
+      create_tensor_result = CreateWebNNTensor(
+          webnn_context_remote_,
+          mojom::TensorInfo::New(
+              OperandDescriptor::UnsafeCreateForTesting(
+                  OperandDataType::kUint8, std::array<uint32_t, 2>{2, 2}),
+              MLTensorUsage{MLTensorUsageFlags::kWebGpuInterop}));
+  if (create_tensor_result.has_value()) {
+    webnn_tensor_remote =
+        std::move(create_tensor_result.value().webnn_tensor_remote);
+    webnn_tensor_handle =
+        std::move(create_tensor_result.value().webnn_tensor_handle);
+  }
+
+  ASSERT_TRUE(webnn_tensor_remote.is_bound());
+
+  webnn_context_remote_.FlushForTesting();
+
+  base::WeakPtr<native::d3d12::WebNNTensor> webnn_tensor =
+      GetWebNNTensor(webnn_tensor_handle);
+  ASSERT_TRUE(webnn_tensor);
+
+  std::unique_ptr<native::d3d12::WebNNSharedFence> webnn_fence_to_wait_for_1 =
+      webnn_tensor->EndAccessWebNN();
+  ASSERT_TRUE(webnn_fence_to_wait_for_1);
+
+  // Ensure nothing to wait for if no WebNN work prior to EndAccessWebNN().
+  EXPECT_TRUE(IsFenceCompleted(webnn_fence_to_wait_for_1->GetD3D12Fence().Get(),
+                               webnn_fence_to_wait_for_1->GetFenceValue()));
+
+  EXPECT_TRUE(webnn_tensor->BeginAccessWebNN(
+      webnn_fence_to_wait_for_1->GetD3D12Fence(),
+      webnn_fence_to_wait_for_1->GetFenceValue()));
+
+  std::unique_ptr<native::d3d12::WebNNSharedFence> webnn_fence_to_wait_for_2 =
+      webnn_tensor->EndAccessWebNN();
+  ASSERT_TRUE(webnn_fence_to_wait_for_2);
+
+  // End access again on the same tensor should return the same fence.
+  EXPECT_EQ(webnn_fence_to_wait_for_2->GetD3D12Fence().Get(),
+            webnn_fence_to_wait_for_1->GetD3D12Fence().Get());
+
+  EXPECT_FALSE(bad_message_helper.GetLastBadMessage().has_value());
+}
+
+// Verify tensor cannot be used before end access.
+TEST_F(WebNNTensorImplDmlBackendTest, UsageAfterBeginAccessWebNNTest) {
+  BadMessageTestHelper bad_message_helper;
+
+  mojo::AssociatedRemote<mojom::WebNNTensor> webnn_tensor_remote;
+  blink::WebNNTensorToken webnn_tensor_handle;
+  base::expected<CreateTensorSuccess, webnn::mojom::Error::Code>
+      create_tensor_result = CreateWebNNTensor(
+          webnn_context_remote_,
+          mojom::TensorInfo::New(
+              OperandDescriptor::UnsafeCreateForTesting(
+                  OperandDataType::kUint8, std::array<uint32_t, 2>{2, 2}),
+              MLTensorUsage{MLTensorUsageFlags::kWebGpuInterop,
+                            MLTensorUsageFlags::kWrite,
+                            MLTensorUsageFlags::kRead}));
+  if (create_tensor_result.has_value()) {
+    webnn_tensor_remote =
+        std::move(create_tensor_result.value().webnn_tensor_remote);
+    webnn_tensor_handle =
+        std::move(create_tensor_result.value().webnn_tensor_handle);
+  }
+
+  ASSERT_TRUE(webnn_tensor_remote.is_bound());
+
+  webnn_context_remote_.FlushForTesting();
+
+  base::WeakPtr<native::d3d12::WebNNTensor> webnn_tensor =
+      GetWebNNTensor(webnn_tensor_handle);
+  ASSERT_TRUE(webnn_tensor);
+
+  // Ensure WebNN can use the tensor before access begins.
+  constexpr uint64_t kTensorSize = 4ull;
+  const std::array<const uint8_t, kTensorSize> input_data{0xAA, 0xAA, 0xAA,
+                                                          0xAA};
+  webnn_tensor_remote->WriteTensor(mojo_base::BigBuffer(input_data));
+  webnn_tensor_remote.FlushForTesting();
+
+  std::unique_ptr<native::d3d12::WebNNSharedFence> webnn_fence_to_wait_for =
+      webnn_tensor->EndAccessWebNN();
+  ASSERT_TRUE(webnn_fence_to_wait_for);
+
+  EXPECT_TRUE(
+      webnn_tensor->BeginAccessWebNN(webnn_fence_to_wait_for->GetD3D12Fence(),
+                                     webnn_fence_to_wait_for->GetFenceValue()));
+
+  // Ensure the WebNN can still use the tensor after begin access.
+  {
+    base::test::TestFuture<mojom::ReadTensorResultPtr> read_tensor_future;
+    webnn_tensor_remote->ReadTensor(read_tensor_future.GetCallback());
+    mojom::ReadTensorResultPtr read_create_tensor_result =
+        read_tensor_future.Take();
+    ASSERT_FALSE(read_create_tensor_result->is_error());
+    EXPECT_TRUE(
+        IsBufferDataEqual(mojo_base::BigBuffer(input_data),
+                          std::move(read_create_tensor_result->get_buffer())));
+  }
+
+  EXPECT_FALSE(bad_message_helper.GetLastBadMessage().has_value());
+}
+
+// Verify access between queues: WebNN and an external one.
+TEST_F(WebNNTensorImplDmlBackendTest, AccessOnDifferentQueueTest) {
+  BadMessageTestHelper bad_message_helper;
+
+  mojo::AssociatedRemote<mojom::WebNNTensor> webnn_tensor_remote;
+  blink::WebNNTensorToken webnn_tensor_handle;
+  base::expected<CreateTensorSuccess, webnn::mojom::Error::Code>
+      create_tensor_result = CreateWebNNTensor(
+          webnn_context_remote_,
+          mojom::TensorInfo::New(
+              OperandDescriptor::UnsafeCreateForTesting(
+                  OperandDataType::kUint8, std::array<uint32_t, 2>{2, 2}),
+              MLTensorUsage{MLTensorUsageFlags::kWebGpuInterop,
+                            MLTensorUsageFlags::kRead}));
+  if (create_tensor_result.has_value()) {
+    webnn_tensor_remote =
+        std::move(create_tensor_result.value().webnn_tensor_remote);
+    webnn_tensor_handle =
+        std::move(create_tensor_result.value().webnn_tensor_handle);
+  }
+
+  ASSERT_TRUE(webnn_tensor_remote.is_bound());
+
+  webnn_context_remote_.FlushForTesting();
+
+  // Simulate access by creating an external queue, recorder, and a
+  // buffer.
+  scoped_refptr<dml::CommandQueue> command_queue =
+      dml::CommandQueue::Create(adapter_->d3d12_device());
+  ASSERT_NE(command_queue, nullptr);
+
+  auto create_recorder_result =
+      dml::CommandRecorder::Create(command_queue, adapter_->dml_device());
+  ASSERT_TRUE(create_recorder_result.has_value());
+  std::unique_ptr<dml::CommandRecorder> command_recorder =
+      std::move(create_recorder_result.value());
+
+  constexpr uint64_t kTensorSize = 4ull;
+  const std::array<const uint8_t, kTensorSize> input_data = {0xAA, 0xAA, 0xAA,
+                                                             0xAA};
+  Microsoft::WRL::ComPtr<ID3D12Resource> upload_buffer;
+  ASSERT_HRESULT_SUCCEEDED(
+      dml::CreateUploadBuffer(adapter_->d3d12_device(), input_data.size(),
+                              L"Upload_Buffer", upload_buffer));
+  ASSERT_NE(upload_buffer, nullptr);
+
+  // SAFETY: `upload_buffer` was constructed with size `input_data.size()`.
+  UNSAFE_BUFFERS(WriteTensorData(
+      base::span(input_data.data(), input_data.size()), upload_buffer.Get()));
+
+  base::WeakPtr<native::d3d12::WebNNTensor> webnn_tensor =
+      GetWebNNTensor(webnn_tensor_handle);
+  ASSERT_TRUE(webnn_tensor);
+
+  // Simulate multi-queue usage via GPU copy.
+  //
+  // Step | WebNN queue   |  Other queue
+  // -----------------------------------
+  // 1.     Signal
+  // 2.          |---------> Wait
+  // 3.                      GPU copy
+  // 4.                      Signal
+  // 5.     Wait <-----------|
+  // 6.     GPU copy
+  // 7.     Signal
+  // 8.          |---------> Wait
+  // 9.                      GPU copy
+  // 10.                     Signal
+  // 11.    Wait <-----------|
+  // 12.    GPU copy
+  // 13.    Signal
+  // 14.         |----------> Wait
+
+  std::unique_ptr<native::d3d12::WebNNSharedFence> webnn_fence_to_wait_for_1 =
+      webnn_tensor->EndAccessWebNN();
+  ASSERT_TRUE(webnn_fence_to_wait_for_1);
+
+  // Step 1. End access with no WebNN work should not require a wait.
+  ASSERT_TRUE(IsFenceCompleted(webnn_fence_to_wait_for_1->GetD3D12Fence().Get(),
+                               webnn_fence_to_wait_for_1->GetFenceValue()));
+
+  {
+    ASSERT_HRESULT_SUCCEEDED(command_recorder->Open());
+    UploadBufferWithBarrier(command_recorder.get(),
+                            webnn_tensor->GetD3D12Buffer(), upload_buffer,
+                            kTensorSize);
+    ASSERT_HRESULT_SUCCEEDED(command_recorder->CloseAndExecute());
+  }
+
+  ASSERT_TRUE(webnn_tensor->BeginAccessWebNN(
+      command_queue->submission_fence(), command_queue->GetLastFenceValue()));
+
+  // Step 5. Ensure WebNN can use the tensor after begin access.
+  {
+    base::test::TestFuture<mojom::ReadTensorResultPtr> read_tensor_future;
+    webnn_tensor_remote->ReadTensor(read_tensor_future.GetCallback());
+    mojom::ReadTensorResultPtr read_create_tensor_result =
+        read_tensor_future.Take();
+    ASSERT_FALSE(read_create_tensor_result->is_error());
+    EXPECT_TRUE(
+        IsBufferDataEqual(mojo_base::BigBuffer(input_data),
+                          std::move(read_create_tensor_result->get_buffer())));
+  }
+
+  // Ensure WebNN waited until the other queue completed.
+  ASSERT_TRUE(IsFenceCompleted(command_queue->submission_fence(),
+                               command_queue->GetLastFenceValue()));
+
+  // Step 8. Simulate more external queue use with new data.
+  std::unique_ptr<native::d3d12::WebNNSharedFence> webnn_fence_to_wait_for_2 =
+      webnn_tensor->EndAccessWebNN();
+  ASSERT_TRUE(webnn_fence_to_wait_for_2);
+
+  const std::array<const uint8_t, kTensorSize> new_input_data = {0xBB, 0xBB,
+                                                                 0xBB, 0xBB};
+  {
+    // SAFETY: `upload_buffer` was constructed with size
+    // `new_input_data.size()`.
+    UNSAFE_BUFFERS(WriteTensorData(
+        base::span(new_input_data.data(), new_input_data.size()),
+        upload_buffer.Get()));
+
+    ASSERT_HRESULT_SUCCEEDED(command_queue->WaitForFence(
+        webnn_fence_to_wait_for_2->GetD3D12Fence(),
+        webnn_fence_to_wait_for_2->GetFenceValue()));
+    ASSERT_HRESULT_SUCCEEDED(command_recorder->Open());
+    UploadBufferWithBarrier(command_recorder.get(),
+                            webnn_tensor->GetD3D12Buffer(), upload_buffer,
+                            kTensorSize);
+    ASSERT_HRESULT_SUCCEEDED(command_recorder->CloseAndExecute());
+  }
+
+  ASSERT_TRUE(webnn_tensor->BeginAccessWebNN(
+      command_queue->submission_fence(), command_queue->GetLastFenceValue()));
+
+  // Step 11. WebNN should be able to use the tensor after begin access.
+  {
+    base::test::TestFuture<mojom::ReadTensorResultPtr> read_tensor_future;
+    webnn_tensor_remote->ReadTensor(read_tensor_future.GetCallback());
+    mojom::ReadTensorResultPtr read_create_tensor_result =
+        read_tensor_future.Take();
+    ASSERT_FALSE(read_create_tensor_result->is_error());
+    EXPECT_TRUE(
+        IsBufferDataEqual(mojo_base::BigBuffer(new_input_data),
+                          std::move(read_create_tensor_result->get_buffer())));
+  }
+
+  EXPECT_FALSE(bad_message_helper.GetLastBadMessage().has_value());
+}
+
+// Verify end access with no WebNN work in-between returns the last fence
+// without WebNN calling wait.
+TEST_F(WebNNTensorImplDmlBackendTest, NoWebNNQueueAccessInBetweenTest) {
+  BadMessageTestHelper bad_message_helper;
+
+  mojo::AssociatedRemote<mojom::WebNNTensor> webnn_tensor_remote;
+  blink::WebNNTensorToken webnn_tensor_handle;
+  base::expected<CreateTensorSuccess, webnn::mojom::Error::Code>
+      create_tensor_result = CreateWebNNTensor(
+          webnn_context_remote_,
+          mojom::TensorInfo::New(
+              OperandDescriptor::UnsafeCreateForTesting(
+                  OperandDataType::kUint8, std::array<uint32_t, 2>{2, 2}),
+              MLTensorUsage{MLTensorUsageFlags::kWebGpuInterop}));
+  if (create_tensor_result.has_value()) {
+    webnn_tensor_remote =
+        std::move(create_tensor_result.value().webnn_tensor_remote);
+    webnn_tensor_handle =
+        std::move(create_tensor_result.value().webnn_tensor_handle);
+  }
+
+  ASSERT_TRUE(webnn_tensor_remote.is_bound());
+
+  webnn_context_remote_.FlushForTesting();
+
+  // Simulate access by creating an external queue.
+  scoped_refptr<dml::CommandQueue> command_queue =
+      dml::CommandQueue::Create(adapter_->d3d12_device());
+  ASSERT_NE(command_queue, nullptr);
+
+  base::WeakPtr<native::d3d12::WebNNTensor> webnn_tensor =
+      GetWebNNTensor(webnn_tensor_handle);
+  ASSERT_TRUE(webnn_tensor);
+
+  // End access without any WebNN work prior returns WebNN's submission
+  // fence which should be completed.
+  std::unique_ptr<native::d3d12::WebNNSharedFence> webnn_fence_to_wait_for_1 =
+      webnn_tensor->EndAccessWebNN();
+  ASSERT_TRUE(webnn_fence_to_wait_for_1);
+
+  ASSERT_TRUE(IsFenceCompleted(webnn_fence_to_wait_for_1->GetD3D12Fence().Get(),
+                               webnn_fence_to_wait_for_1->GetFenceValue()));
+
+  // Initialize the external queue's submission fence to a non-zero value to
+  // ensure it has not been signaled by WebNN's queue.
+  const uint64_t initialValue = 0xFF;
+  command_queue->submission_fence()->Signal(initialValue);
+
+  ASSERT_TRUE(webnn_tensor->BeginAccessWebNN(command_queue->submission_fence(),
+                                             initialValue + 1));
+
+  // Calling end access again, with no WebNN work, should
+  // return the last fence without WebNN calling wait on it.
+  std::unique_ptr<native::d3d12::WebNNSharedFence> webnn_fence_to_wait_for_2 =
+      webnn_tensor->EndAccessWebNN();
+  ASSERT_TRUE(webnn_fence_to_wait_for_2);
+
+  EXPECT_EQ(command_queue->submission_fence(),
+            webnn_fence_to_wait_for_2->GetD3D12Fence().Get());
+
+  EXPECT_FALSE(
+      IsFenceCompleted(command_queue->submission_fence(), initialValue + 1));
+
+  EXPECT_FALSE(bad_message_helper.GetLastBadMessage().has_value());
+}
+
+#endif  // BUILDFLAG(IS_WIN)
+
 }  // namespace
 
 }  // namespace webnn::test
diff --git a/testing/buildbot/buildbot_json_magic_substitutions.py b/testing/buildbot/buildbot_json_magic_substitutions.py
index a196ab9..702f257 100644
--- a/testing/buildbot/buildbot_json_magic_substitutions.py
+++ b/testing/buildbot/buildbot_json_magic_substitutions.py
@@ -20,6 +20,9 @@
 MAGIC_SUBSTITUTION_PREFIX = '$$MAGIC_SUBSTITUTION_'
 
 GpuDevice = collections.namedtuple('GpuDevice', ['vendor', 'device'])
+ANDROID_DESKTOP_BOARD_GPUS = {
+    'brya': GpuDevice('8086', '46a8'),
+}
 CROS_BOARD_GPUS = {
     'volteer': GpuDevice('8086', '9a49'),
 }
@@ -47,6 +50,18 @@
     'oriole': GpuDevice('13b5', '92020010,92020000'),
 }
 
+
+def AndroidDesktopTelemetryRemote(test_config, _, tester_config):
+  """Substitutes the correct Android Desktop remote Telemetry arguments."""
+  assert _IsAndroid(tester_config)
+  if not _GetAndroidDesktopBoardName(test_config):
+    return []
+  return [
+      '--device=variable_lab_dut_hostname',
+      '--connect-to-device-over-network',
+  ]
+
+
 def ChromeOSTelemetryRemote(test_config, _, tester_config):
   """Substitutes the correct CrOS remote Telemetry arguments.
 
@@ -93,6 +108,13 @@
   ]
 
 
+def _GetAndroidDesktopBoardName(test_config):
+  """Helper function to determine what Android Desktop board is being used."""
+  dimensions = test_config.get('swarming', {}).get('dimensions')
+  assert dimensions is not None
+  return dimensions.get('label-board')
+
+
 def _GetChromeOSBoardName(test_config):
   """Helper function to determine what ChromeOS board is being used."""
 
@@ -120,6 +142,11 @@
   return dimensions.get('device_type', 'amd64-generic')
 
 
+def _IsAndroidDesktopBot(test_config, tester_config):
+  """Helper function to determine if a bot is an Android Desktop bot."""
+  return _IsAndroid(tester_config) and _GetAndroidDesktopBoardName(test_config)
+
+
 def _IsSkylabBot(tester_config):
   """Helper function to determine if a bot is a Skylab ChromeOS bot."""
   return (tester_config.get('browser_config') == 'cros-chrome'
@@ -142,6 +169,8 @@
     tester_config: A dict containing the configuration for the builder
         that |test_config| is for.
   """
+  if _IsAndroidDesktopBot(test_config, tester_config):
+    return _GPUExpectedVendorIdAndroidDesktop(test_config)
   if _IsSkylabBot(tester_config):
     return _GPUExpectedVendorIdSkylab(test_config)
   dimensions = test_config.get('swarming', {}).get('dimensions')
@@ -173,6 +202,13 @@
   return ['--expected-vendor-id', vendor_ids.pop()]
 
 
+def _GPUExpectedVendorIdAndroidDesktop(test_config):
+  board = _GetAndroidDesktopBoardName(test_config)
+  assert board is not None
+  gpu_device = ANDROID_DESKTOP_BOARD_GPUS.get(board, GpuDevice('0', '0'))
+  return ['--expected-vendor-id', gpu_device.vendor]
+
+
 def _GPUExpectedVendorIdSkylab(test_config):
   cros_board = test_config.get('cros_board')
   assert cros_board is not None
@@ -192,6 +228,8 @@
     tester_config: A dict containing the configuration for the builder
         that |test_config| is for.
   """
+  if _IsAndroidDesktopBot(test_config, tester_config):
+    return _GPUExpectedDeviceIdAndroidDesktop(test_config)
   if _IsSkylabBot(tester_config):
     return _GPUExpectedDeviceIdSkylab(test_config)
   dimensions = test_config.get('swarming', {}).get('dimensions')
@@ -229,6 +267,13 @@
   return retval
 
 
+def _GPUExpectedDeviceIdAndroidDesktop(test_config):
+  board = _GetAndroidDesktopBoardName(test_config)
+  assert board is not None
+  gpu_device = ANDROID_DESKTOP_BOARD_GPUS.get(board, GpuDevice('0', '0'))
+  return ['--expected-device-id', gpu_device.device]
+
+
 def _GPUExpectedDeviceIdSkylab(test_config):
   cros_board = test_config.get('cros_board')
   assert cros_board is not None
diff --git a/testing/buildbot/buildbot_json_magic_substitutions_unittest.py b/testing/buildbot/buildbot_json_magic_substitutions_unittest.py
index 1d93c7c..76c3004 100755
--- a/testing/buildbot/buildbot_json_magic_substitutions_unittest.py
+++ b/testing/buildbot/buildbot_json_magic_substitutions_unittest.py
@@ -9,7 +9,7 @@
 import buildbot_json_magic_substitutions as magic_substitutions
 
 
-def CreateConfigWithPool(pool, device_type=None):
+def CreateConfigWithPool(pool, device_type=None, board=None):
   dims = {
       'name': 'test_name',
       'swarming': {
@@ -20,9 +20,35 @@
   }
   if device_type:
     dims['swarming']['dimensions']['device_type'] = device_type
+  if board:
+    dims['swarming']['dimensions']['label-board'] = board
   return dims
 
 
+class AndroidDesktopTelemetryRemoteTest(unittest.TestCase):
+
+  def testNonAndroid(self):
+    test_config = CreateConfigWithPool('chromium.tests', board='brya')
+    with self.assertRaises(AssertionError):
+      magic_substitutions.AndroidDesktopTelemetryRemote(test_config, None,
+                                                        {'os_type': 'linux'})
+
+  def testNoBoard(self):
+    test_config = CreateConfigWithPool('chromium.tests')
+    self.assertEqual(
+        magic_substitutions.AndroidDesktopTelemetryRemote(
+            test_config, None, {'os_type': 'android'}), [])
+
+  def testSuccess(self):
+    test_config = CreateConfigWithPool('chromium.tests', board='brya')
+    self.assertEqual(
+        magic_substitutions.AndroidDesktopTelemetryRemote(
+            test_config, None, {'os_type': 'android'}), [
+                '--device=variable_lab_dut_hostname',
+                '--connect-to-device-over-network',
+            ])
+
+
 class ChromeOSTelemetryRemoteTest(unittest.TestCase):
 
   def testVirtualMachineSubstitutions(self):
@@ -160,6 +186,40 @@
     with self.assertRaises(AssertionError):
       magic_substitutions.GPUExpectedVendorId({}, None, {})
 
+  def testAndroidDesktopKnownBoard(self):
+    test_config = {
+        'name': 'test_name',
+        'swarming': {
+            'dimensions': {
+                'label-board': 'brya',
+            },
+        },
+    }
+    tester_config = {
+        'os_type': 'android',
+    }
+    self.assertEqual(
+        magic_substitutions.GPUExpectedVendorId(test_config, None,
+                                                tester_config),
+        ['--expected-vendor-id', '8086'])
+
+  def testAndroidDesktopUnknownBoard(self):
+    test_config = {
+        'name': 'test_name',
+        'swarming': {
+            'dimensions': {
+                'label-board': 'fake_board',
+            },
+        },
+    }
+    tester_config = {
+        'os_type': 'android',
+    }
+    self.assertEqual(
+        magic_substitutions.GPUExpectedVendorId(test_config, None,
+                                                tester_config),
+        ['--expected-vendor-id', '0'])
+
   def testSkylabKnownBoard(self):
     test_config = {
         'name': 'test_name',
@@ -226,6 +286,38 @@
     with self.assertRaises(AssertionError):
       magic_substitutions.GPUExpectedDeviceId({}, None, {})
 
+  def testAndroidDesktopKnownBoard(self):
+    test_config = {
+        'name': 'test_name',
+        'swarming': {
+            'dimensions': {
+                'label-board': 'brya',
+            },
+        },
+    }
+    tester_config = {
+        'os_type': 'android',
+    }
+    self.assertDeviceIdCorrectness(
+        magic_substitutions.GPUExpectedDeviceId(test_config, None,
+                                                tester_config), ['46a8'])
+
+  def testAndroidDesktopUnknownBoard(self):
+    test_config = {
+        'name': 'test_name',
+        'swarming': {
+            'dimensions': {
+                'label-board': 'fake_board',
+            },
+        },
+    }
+    tester_config = {
+        'os_type': 'android',
+    }
+    self.assertDeviceIdCorrectness(
+        magic_substitutions.GPUExpectedDeviceId(test_config, None,
+                                                tester_config), ['0'])
+
   def testSkylabKnownBoard(self):
     test_config = {
         'name': 'test_name',
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 5c525bf..98be8ea 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -106,7 +106,7 @@
           "can_use_on_swarming_builders": true,
           "dimensions": {
             "dut_state": "ready",
-            "label-board": "byra",
+            "label-board": "brya",
             "label-pool": "chrome.tests.perf",
             "os": "Android",
             "pool": "chrome"
diff --git a/testing/buildbot/chromium.perf.pinpoint.json b/testing/buildbot/chromium.perf.pinpoint.json
index 306f3368..70348c8 100644
--- a/testing/buildbot/chromium.perf.pinpoint.json
+++ b/testing/buildbot/chromium.perf.pinpoint.json
@@ -25,7 +25,7 @@
           "can_use_on_swarming_builders": true,
           "dimensions": {
             "dut_state": "ready",
-            "label-board": "byra",
+            "label-board": "brya",
             "label-pool": "chrome.tests.perf",
             "os": "Android",
             "pool": "chrome"
diff --git a/testing/buildbot/filters/BUILD.gn b/testing/buildbot/filters/BUILD.gn
index 7f1a3141..1ea8e6c 100644
--- a/testing/buildbot/filters/BUILD.gn
+++ b/testing/buildbot/filters/BUILD.gn
@@ -385,6 +385,7 @@
     "//testing/buildbot/filters/android.emulator_12.gl_unittests.filter",
     "//testing/buildbot/filters/win.win_arm64.gl_unittests.filter",
     "//testing/buildbot/filters/win.amd.5500xt.gl_unittests.filter",
+    "//testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter",
   ]
 }
 
diff --git a/testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter b/testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter
index 5a757f4..9074ff4 100644
--- a/testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter
+++ b/testing/buildbot/filters/ozone-linux.browser_tests_mutter.filter
@@ -1,4 +1,5 @@
-# TODO(crbug.com/395595608) Failing due to per-surface scaling.
+# TODO(crbug.com/348590032) Failing as test relies on window.devicePixelRatio
+# which doesn't work with per-window scaling.
 -All/GetDisplayMediaHiDpiBrowserTest.Capture/1
 
 # Very flaky/failing on mutter (less flaky/passing on weston)
diff --git a/testing/buildbot/filters/pixel_tests.filter b/testing/buildbot/filters/pixel_tests.filter
index 7c00fe5..881a6875 100644
--- a/testing/buildbot/filters/pixel_tests.filter
+++ b/testing/buildbot/filters/pixel_tests.filter
@@ -19,6 +19,7 @@
 BrowserViewScrimPixelTest.*
 BubbleFrameViewBrowserTest.*
 BubbleSignInPromoInteractiveUITest.*
+CaptionBubbleBrowserTest.*
 ChromeLabs*UiTest.*
 ChromeSignoutConfirmationPromptPixelTest.*
 ChromeSignoutConfirmationPromptWithExtensionsPixelTest.*
diff --git a/testing/buildbot/filters/trees_in_viz.content_browsertests.filter b/testing/buildbot/filters/trees_in_viz.content_browsertests.filter
index 5eb7e30..cb89cf51 100644
--- a/testing/buildbot/filters/trees_in_viz.content_browsertests.filter
+++ b/testing/buildbot/filters/trees_in_viz.content_browsertests.filter
@@ -9,7 +9,6 @@
 -All/CompositedScrollingMetricTest.RecordCorrectScrollingThread/NonComposited_RasterInducingScroll
 -All/NavigationBrowserTestPaintHoldingSubframe.BasicInProcessIframe/0
 -All/NavigationBrowserTestPaintHoldingSubframe.BasicInProcessIframe/1
--All/RenderWidgetHostItemSequenceNumberInRenderFrameMetadataTest.ItemSequenceNumberExpectedNoContentChange/SameDoc
 -PrerenderBrowserDeathTest.PrerenderCannotHaveInnerContents
 -RenderFrameHostImplDeathTest.ReloadInPendingDeletionOrBFCache
 -SecurityExploitBrowserTest.AllowBindingsForNonWebUIProcess
diff --git a/testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter b/testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter
new file mode 100644
index 0000000..cf557d76
--- /dev/null
+++ b/testing/buildbot/filters/win.nvidia.gtx.1660.gl_unittests.filter
@@ -0,0 +1,2 @@
+# crbug.com/415086846
+-*DCompPresenter*
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl
index dd9fc79..159ef3a 100644
--- a/testing/buildbot/mixins.pyl
+++ b/testing/buildbot/mixins.pyl
@@ -261,6 +261,7 @@
     ],
     'android_args': [
       '$$MAGIC_SUBSTITUTION_GPUTelemetryNoRootForUnrootedDevices',
+      '$$MAGIC_SUBSTITUTION_AndroidDesktopTelemetryRemote',
       '--initial-find-device-attempts=3',
     ],
     'chromeos_args': [
@@ -866,8 +867,8 @@
     'swarming': {
       'dimensions': {
         'display_attached': '1',
-        'gpu': '10de:2184-31.0.15.4601',
-        'os': 'Windows-10-19045',
+        'gpu': '10de:2184-32.0.15.7602',
+        'os': 'Windows-11-26100',
         'pool': 'chromium.tests.gpu',
       },
     },
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 9a285d856..5a8c902 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -8834,6 +8834,24 @@
             ]
         }
     ],
+    "EnterpriseFileSystemAccessDeepScan": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "EnterpriseFileSystemAccessDeepScan"
+                    ]
+                }
+            ]
+        }
+    ],
     "EnterpriseUpdatedProfileCreationScreen": [
         {
             "platforms": [
@@ -10224,7 +10242,7 @@
             ]
         }
     ],
-    "GwpAsanAndroid2024": [
+    "GwpAsan2024Android": [
         {
             "platforms": [
                 "android",
@@ -10232,11 +10250,20 @@
             ],
             "experiments": [
                 {
-                    "name": "TripleBrowserReservation",
+                    "name": "CombinedGreedyArmForVariationsPresubmit",
                     "params": {
+                        "BrowserAllocationSamplingMultiplier": "1500",
+                        "BrowserAllocationSamplingRange": "16",
                         "BrowserMaxAllocations": "210",
                         "BrowserMaxMetadata": "765",
-                        "BrowserTotalPages": "1536"
+                        "BrowserTotalPages": "1536",
+                        "GpuAllocationSamplingMultiplier": "1500",
+                        "GpuAllocationSamplingRange": "16",
+                        "GpuMaxAllocations": "140",
+                        "GpuMaxMetadata": "510",
+                        "GpuTotalPages": "1024",
+                        "RendererAllocationSamplingMultiplier": "1500",
+                        "RendererAllocationSamplingRange": "12"
                     },
                     "enable_features": [
                         "GwpAsanMalloc",
@@ -10246,20 +10273,59 @@
             ]
         }
     ],
-    "GwpAsanDesktop2024": [
+    "GwpAsan2024LinuxCros": [
         {
             "platforms": [
                 "chromeos",
-                "linux",
+                "linux"
+            ],
+            "experiments": [
+                {
+                    "name": "CombinedGreedyArmForVariationsPresubmit",
+                    "params": {
+                        "BrowserAllocationSamplingMultiplier": "1200",
+                        "BrowserAllocationSamplingRange": "10",
+                        "BrowserMaxAllocations": "150",
+                        "BrowserMaxMetadata": "630",
+                        "BrowserTotalPages": "6144",
+                        "GpuAllocationSamplingMultiplier": "1200",
+                        "GpuAllocationSamplingRange": "10",
+                        "GpuMaxAllocations": "100",
+                        "GpuMaxMetadata": "420",
+                        "GpuTotalPages": "4096",
+                        "RendererAllocationSamplingMultiplier": "800",
+                        "RendererAllocationSamplingRange": "10"
+                    },
+                    "enable_features": [
+                        "GwpAsanMalloc",
+                        "GwpAsanPartitionAlloc"
+                    ]
+                }
+            ]
+        }
+    ],
+    "GwpAsan2024WinMac": [
+        {
+            "platforms": [
+                "windows",
                 "mac"
             ],
             "experiments": [
                 {
-                    "name": "TripleBrowserReservation",
+                    "name": "CombinedGreedyArmForVariationsPresubmit",
                     "params": {
+                        "BrowserAllocationSamplingMultiplier": "800",
+                        "BrowserAllocationSamplingRange": "10",
                         "BrowserMaxAllocations": "210",
                         "BrowserMaxMetadata": "765",
-                        "BrowserTotalPages": "6144"
+                        "BrowserTotalPages": "6144",
+                        "GpuAllocationSamplingMultiplier": "800",
+                        "GpuAllocationSamplingRange": "10",
+                        "GpuMaxAllocations": "140",
+                        "GpuMaxMetadata": "510",
+                        "GpuTotalPages": "4096",
+                        "RendererAllocationSamplingMultiplier": "600",
+                        "RendererAllocationSamplingRange": "10"
                     },
                     "enable_features": [
                         "GwpAsanMalloc",
@@ -11177,6 +11243,21 @@
             ]
         }
     ],
+    "IOSAutofillRefillForForms": [
+        {
+            "platforms": [
+                "ios"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AutofillRefillForFormsIos"
+                    ]
+                }
+            ]
+        }
+    ],
     "IOSAutofillThrottleDocumentFormScan": [
         {
             "platforms": [
@@ -13185,35 +13266,6 @@
             ]
         }
     ],
-    "LightweightUAFDetector": [
-        {
-            "platforms": [
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled_20240130",
-                    "params": {
-                        "AllocationSamplingMultiplier": "100",
-                        "EvictionTaskIntervalMs": "5000",
-                        "MaxAllocations": "1400",
-                        "MaxMetadata": "5100",
-                        "MaxTotalSize": "131072",
-                        "Mode": "Random",
-                        "TotalSizeHighWaterMark": "104857",
-                        "TotalSizeLowWaterMark": "91750"
-                    },
-                    "enable_features": [
-                        "LightweightUafDetector"
-                    ],
-                    "disable_features": [
-                        "GwpAsanMalloc",
-                        "GwpAsanPartitionAlloc"
-                    ]
-                }
-            ]
-        }
-    ],
     "LinkCrossDeviceDogFoodFeedback": [
         {
             "platforms": [
@@ -14618,6 +14670,21 @@
             ]
         }
     ],
+    "NonModalSignInPromo": [
+        {
+            "platforms": [
+                "ios"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "NonModalSignInPromo"
+                    ]
+                }
+            ]
+        }
+    ],
     "NonStandardAppearanceValueSliderVertical": [
         {
             "platforms": [
@@ -17335,6 +17402,7 @@
         {
             "platforms": [
                 "android",
+                "android_webview",
                 "chromeos",
                 "chromeos_lacros",
                 "linux",
@@ -21295,6 +21363,32 @@
             ]
         }
     ],
+    "SearchEnginePreconnect2": [
+        {
+            "platforms": [
+                "android",
+                "chromeos",
+                "chromeos_lacros",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "EnabledWithbase_60_30_30_30__20250507",
+                    "params": {
+                        "IdleTimeoutInSeconds": "60",
+                        "MaxPreconnectRetryInterval": "30",
+                        "MaxShortSessionThreshold": "30",
+                        "PingIntervalInSeconds": "30"
+                    },
+                    "enable_features": [
+                        "SearchEnginePreconnect2"
+                    ]
+                }
+            ]
+        }
+    ],
     "SearchEnginePreconnectInterval": [
         {
             "platforms": [
diff --git a/third_party/abseil-cpp/README.chromium b/third_party/abseil-cpp/README.chromium
index 3f47276..044587a 100644
--- a/third_party/abseil-cpp/README.chromium
+++ b/third_party/abseil-cpp/README.chromium
@@ -4,7 +4,7 @@
 License: Apache-2.0
 License File: LICENSE
 Version: N/A
-Revision: 2bbec17a3f20da4d908e5627aa72b46f48e064ac
+Revision: 1b52dcb350289b262a105471a75ef6c001beecae
 Security Critical: yes
 Shipped: yes
 
diff --git a/third_party/abseil-cpp/absl/base/config.h b/third_party/abseil-cpp/absl/base/config.h
index 7514b86..f3cafbd 100644
--- a/third_party/abseil-cpp/absl/base/config.h
+++ b/third_party/abseil-cpp/absl/base/config.h
@@ -530,13 +530,12 @@
 
 // ABSL_HAVE_STD_STRING_VIEW
 //
-// Checks whether C++17 std::string_view is available.
+// Deprecated: always defined to 1.
+// std::string_view was added in C++17, which means all versions of C++
+// supported by Abseil have it.
 #ifdef ABSL_HAVE_STD_STRING_VIEW
 #error "ABSL_HAVE_STD_STRING_VIEW cannot be directly set."
-#elif defined(__cpp_lib_string_view) && __cpp_lib_string_view >= 201606L
-#define ABSL_HAVE_STD_STRING_VIEW 1
-#elif defined(ABSL_INTERNAL_CPLUSPLUS_LANG) && \
-    ABSL_INTERNAL_CPLUSPLUS_LANG >= 201703L
+#else
 #define ABSL_HAVE_STD_STRING_VIEW 1
 #endif
 
@@ -561,13 +560,10 @@
 // Indicates whether absl::string_view is an alias for std::string_view.
 #if !defined(ABSL_OPTION_USE_STD_STRING_VIEW)
 #error options.h is misconfigured.
-#elif ABSL_OPTION_USE_STD_STRING_VIEW == 0 || \
-    (ABSL_OPTION_USE_STD_STRING_VIEW == 2 &&  \
-     !defined(ABSL_HAVE_STD_STRING_VIEW))
+#elif ABSL_OPTION_USE_STD_STRING_VIEW == 0
 #undef ABSL_USES_STD_STRING_VIEW
 #elif ABSL_OPTION_USE_STD_STRING_VIEW == 1 || \
-    (ABSL_OPTION_USE_STD_STRING_VIEW == 2 &&  \
-     defined(ABSL_HAVE_STD_STRING_VIEW))
+    ABSL_OPTION_USE_STD_STRING_VIEW == 2
 #define ABSL_USES_STD_STRING_VIEW 1
 #else
 #error options.h is misconfigured.
diff --git a/third_party/abseil-cpp/absl/container/internal/hash_function_defaults.h b/third_party/abseil-cpp/absl/container/internal/hash_function_defaults.h
index 0f07bcf..c2a757b 100644
--- a/third_party/abseil-cpp/absl/container/internal/hash_function_defaults.h
+++ b/third_party/abseil-cpp/absl/container/internal/hash_function_defaults.h
@@ -49,6 +49,7 @@
 #include <functional>
 #include <memory>
 #include <string>
+#include <string_view>
 #include <type_traits>
 
 #include "absl/base/config.h"
@@ -58,10 +59,6 @@
 #include "absl/strings/cord.h"
 #include "absl/strings/string_view.h"
 
-#ifdef ABSL_HAVE_STD_STRING_VIEW
-#include <string_view>
-#endif
-
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
@@ -113,8 +110,6 @@
 template <>
 struct HashEq<absl::Cord> : StringHashEq {};
 
-#ifdef ABSL_HAVE_STD_STRING_VIEW
-
 template <typename TChar>
 struct BasicStringHash {
   using is_transparent = void;
@@ -153,8 +148,6 @@
 template <>
 struct HashEq<std::u32string_view> : BasicStringHashEq<char32_t> {};
 
-#endif  // ABSL_HAVE_STD_STRING_VIEW
-
 // Supports heterogeneous lookup for pointers and smart pointers.
 template <class T>
 struct HashEq<T*> {
diff --git a/third_party/abseil-cpp/absl/container/internal/hash_function_defaults_test.cc b/third_party/abseil-cpp/absl/container/internal/hash_function_defaults_test.cc
index 912d119..9a39b07 100644
--- a/third_party/abseil-cpp/absl/container/internal/hash_function_defaults_test.cc
+++ b/third_party/abseil-cpp/absl/container/internal/hash_function_defaults_test.cc
@@ -16,6 +16,7 @@
 
 #include <cstddef>
 #include <functional>
+#include <string_view>
 #include <type_traits>
 #include <utility>
 
@@ -28,10 +29,6 @@
 #include "absl/strings/cord_test_helpers.h"
 #include "absl/strings/string_view.h"
 
-#ifdef ABSL_HAVE_STD_STRING_VIEW
-#include <string_view>
-#endif
-
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace container_internal {
@@ -118,9 +115,6 @@
 }
 
 TEST(BasicStringViewTest, WStringEqWorks) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   hash_default_eq<std::wstring> eq;
   EXPECT_TRUE(eq(L"a", L"a"));
   EXPECT_TRUE(eq(L"a", std::wstring_view(L"a")));
@@ -128,13 +122,9 @@
   EXPECT_FALSE(eq(L"a", L"b"));
   EXPECT_FALSE(eq(L"a", std::wstring_view(L"b")));
   EXPECT_FALSE(eq(L"a", std::wstring(L"b")));
-#endif
 }
 
 TEST(BasicStringViewTest, WStringViewEqWorks) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   hash_default_eq<std::wstring_view> eq;
   EXPECT_TRUE(eq(L"a", L"a"));
   EXPECT_TRUE(eq(L"a", std::wstring_view(L"a")));
@@ -142,13 +132,9 @@
   EXPECT_FALSE(eq(L"a", L"b"));
   EXPECT_FALSE(eq(L"a", std::wstring_view(L"b")));
   EXPECT_FALSE(eq(L"a", std::wstring(L"b")));
-#endif
 }
 
 TEST(BasicStringViewTest, U16StringEqWorks) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   hash_default_eq<std::u16string> eq;
   EXPECT_TRUE(eq(u"a", u"a"));
   EXPECT_TRUE(eq(u"a", std::u16string_view(u"a")));
@@ -156,13 +142,9 @@
   EXPECT_FALSE(eq(u"a", u"b"));
   EXPECT_FALSE(eq(u"a", std::u16string_view(u"b")));
   EXPECT_FALSE(eq(u"a", std::u16string(u"b")));
-#endif
 }
 
 TEST(BasicStringViewTest, U16StringViewEqWorks) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   hash_default_eq<std::u16string_view> eq;
   EXPECT_TRUE(eq(u"a", u"a"));
   EXPECT_TRUE(eq(u"a", std::u16string_view(u"a")));
@@ -170,13 +152,9 @@
   EXPECT_FALSE(eq(u"a", u"b"));
   EXPECT_FALSE(eq(u"a", std::u16string_view(u"b")));
   EXPECT_FALSE(eq(u"a", std::u16string(u"b")));
-#endif
 }
 
 TEST(BasicStringViewTest, U32StringEqWorks) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   hash_default_eq<std::u32string> eq;
   EXPECT_TRUE(eq(U"a", U"a"));
   EXPECT_TRUE(eq(U"a", std::u32string_view(U"a")));
@@ -184,13 +162,9 @@
   EXPECT_FALSE(eq(U"a", U"b"));
   EXPECT_FALSE(eq(U"a", std::u32string_view(U"b")));
   EXPECT_FALSE(eq(U"a", std::u32string(U"b")));
-#endif
 }
 
 TEST(BasicStringViewTest, U32StringViewEqWorks) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   hash_default_eq<std::u32string_view> eq;
   EXPECT_TRUE(eq(U"a", U"a"));
   EXPECT_TRUE(eq(U"a", std::u32string_view(U"a")));
@@ -198,85 +172,60 @@
   EXPECT_FALSE(eq(U"a", U"b"));
   EXPECT_FALSE(eq(U"a", std::u32string_view(U"b")));
   EXPECT_FALSE(eq(U"a", std::u32string(U"b")));
-#endif
 }
 
 TEST(BasicStringViewTest, WStringHashWorks) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   hash_default_hash<std::wstring> hash;
   auto h = hash(L"a");
   EXPECT_EQ(h, hash(std::wstring_view(L"a")));
   EXPECT_EQ(h, hash(std::wstring(L"a")));
   EXPECT_NE(h, hash(std::wstring_view(L"b")));
   EXPECT_NE(h, hash(std::wstring(L"b")));
-#endif
 }
 
 TEST(BasicStringViewTest, WStringViewHashWorks) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   hash_default_hash<std::wstring_view> hash;
   auto h = hash(L"a");
   EXPECT_EQ(h, hash(std::wstring_view(L"a")));
   EXPECT_EQ(h, hash(std::wstring(L"a")));
   EXPECT_NE(h, hash(std::wstring_view(L"b")));
   EXPECT_NE(h, hash(std::wstring(L"b")));
-#endif
 }
 
 TEST(BasicStringViewTest, U16StringHashWorks) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   hash_default_hash<std::u16string> hash;
   auto h = hash(u"a");
   EXPECT_EQ(h, hash(std::u16string_view(u"a")));
   EXPECT_EQ(h, hash(std::u16string(u"a")));
   EXPECT_NE(h, hash(std::u16string_view(u"b")));
   EXPECT_NE(h, hash(std::u16string(u"b")));
-#endif
 }
 
 TEST(BasicStringViewTest, U16StringViewHashWorks) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   hash_default_hash<std::u16string_view> hash;
   auto h = hash(u"a");
   EXPECT_EQ(h, hash(std::u16string_view(u"a")));
   EXPECT_EQ(h, hash(std::u16string(u"a")));
   EXPECT_NE(h, hash(std::u16string_view(u"b")));
   EXPECT_NE(h, hash(std::u16string(u"b")));
-#endif
 }
 
 TEST(BasicStringViewTest, U32StringHashWorks) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   hash_default_hash<std::u32string> hash;
   auto h = hash(U"a");
   EXPECT_EQ(h, hash(std::u32string_view(U"a")));
   EXPECT_EQ(h, hash(std::u32string(U"a")));
   EXPECT_NE(h, hash(std::u32string_view(U"b")));
   EXPECT_NE(h, hash(std::u32string(U"b")));
-#endif
 }
 
 TEST(BasicStringViewTest, U32StringViewHashWorks) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   hash_default_hash<std::u32string_view> hash;
   auto h = hash(U"a");
   EXPECT_EQ(h, hash(std::u32string_view(U"a")));
   EXPECT_EQ(h, hash(std::u32string(U"a")));
   EXPECT_NE(h, hash(std::u32string_view(U"b")));
   EXPECT_NE(h, hash(std::u32string(U"b")));
-#endif
 }
 
 struct NoDeleter {
diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc
index f19e87b..339e662 100644
--- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc
+++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.cc
@@ -295,9 +295,14 @@
   ABSL_UNREACHABLE();
 }
 
-size_t DropDeletesWithoutResizeAndPrepareInsert(CommonFields& common,
-                                                const PolicyFunctions& policy,
-                                                size_t new_hash) {
+void PrepareInsertCommon(CommonFields& common) {
+  common.increment_size();
+  common.maybe_increment_generation_on_insert();
+}
+
+size_t DropDeletesWithoutResizeAndPrepareInsert(
+    CommonFields& common, const PolicyFunctions& __restrict policy,
+    size_t new_hash) {
   void* set = &common;
   void* slot_array = common.slot_array();
   const size_t capacity = common.capacity();
@@ -399,7 +404,7 @@
   PrepareInsertCommon(common);
   ResetGrowthLeft(common);
   FindInfo find_info = find_first_non_full(common, new_hash);
-  SetCtrlInLargeTable(common, find_info.offset, H2(new_hash), policy.slot_size);
+  SetCtrlInLargeTable(common, find_info.offset, H2(new_hash), slot_size);
   common.infoz().RecordInsert(new_hash, find_info.probe_length);
   common.infoz().RecordRehash(total_probe_length);
   return find_info.offset;
@@ -555,8 +560,9 @@
   SetCtrlInLargeTable(c, index, ctrl_t::kDeleted, slot_size);
 }
 
-void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
-                       void* alloc, bool reuse, bool soo_enabled) {
+void ClearBackingArray(CommonFields& c,
+                       const PolicyFunctions& __restrict policy, void* alloc,
+                       bool reuse, bool soo_enabled) {
   if (reuse) {
     c.set_size_to_zero();
     ABSL_SWISSTABLE_ASSERT(!soo_enabled || c.capacity() > SooCapacity());
@@ -587,10 +593,9 @@
 // This function is used for reserving or rehashing non-empty tables.
 // This use case is rare so the function is type erased.
 // Returns the total probe length.
-size_t FindNewPositionsAndTransferSlots(CommonFields& common,
-                                        const PolicyFunctions& policy,
-                                        ctrl_t* old_ctrl, void* old_slots,
-                                        size_t old_capacity) {
+size_t FindNewPositionsAndTransferSlots(
+    CommonFields& common, const PolicyFunctions& __restrict policy,
+    ctrl_t* old_ctrl, void* old_slots, size_t old_capacity) {
   void* new_slots = common.slot_array();
   const void* hash_fn = policy.hash_fn(common);
   const size_t slot_size = policy.slot_size;
@@ -614,7 +619,8 @@
 }
 
 template <ResizeNonSooMode kMode>
-void ResizeNonSooImpl(CommonFields& common, const PolicyFunctions& policy,
+void ResizeNonSooImpl(CommonFields& common,
+                      const PolicyFunctions& __restrict policy,
                       size_t new_capacity, HashtablezInfoHandle infoz) {
   ABSL_SWISSTABLE_ASSERT(IsValidCapacity(new_capacity));
   ABSL_SWISSTABLE_ASSERT(new_capacity > policy.soo_capacity());
@@ -667,7 +673,7 @@
 }
 
 void ResizeEmptyNonAllocatedTableImpl(CommonFields& common,
-                                      const PolicyFunctions& policy,
+                                      const PolicyFunctions& __restrict policy,
                                       size_t new_capacity, bool force_infoz) {
   ABSL_SWISSTABLE_ASSERT(IsValidCapacity(new_capacity));
   ABSL_SWISSTABLE_ASSERT(new_capacity > policy.soo_capacity());
@@ -690,10 +696,9 @@
 // After transferring the slot, sets control and slots in CommonFields.
 // It is rare to resize an SOO table with one element to a large size.
 // Requires: `c` contains SOO data.
-void InsertOldSooSlotAndInitializeControlBytes(CommonFields& c,
-                                               const PolicyFunctions& policy,
-                                               size_t hash, ctrl_t* new_ctrl,
-                                               void* new_slots) {
+void InsertOldSooSlotAndInitializeControlBytes(
+    CommonFields& c, const PolicyFunctions& __restrict policy, size_t hash,
+    ctrl_t* new_ctrl, void* new_slots) {
   ABSL_SWISSTABLE_ASSERT(c.size() == policy.soo_capacity());
   ABSL_SWISSTABLE_ASSERT(policy.soo_enabled);
   size_t new_capacity = c.capacity();
@@ -728,7 +733,8 @@
   ABSL_SWISSTABLE_ASSERT(common.size() == policy.soo_capacity());
 }
 
-void ResizeFullSooTable(CommonFields& common, const PolicyFunctions& policy,
+void ResizeFullSooTable(CommonFields& common,
+                        const PolicyFunctions& __restrict policy,
                         size_t new_capacity,
                         ResizeFullSooTableSamplingMode sampling_mode) {
   AssertFullSoo(common, policy);
@@ -912,8 +918,8 @@
 // Returns the total probe length.
 template <typename ProbedItem>
 ABSL_ATTRIBUTE_NOINLINE size_t DecodeAndInsertImpl(
-    CommonFields& c, const PolicyFunctions& policy, const ProbedItem* start,
-    const ProbedItem* end, void* old_slots) {
+    CommonFields& c, const PolicyFunctions& __restrict policy,
+    const ProbedItem* start, const ProbedItem* end, void* old_slots) {
   const size_t new_capacity = c.capacity();
 
   void* new_slots = c.slot_array();
@@ -949,9 +955,9 @@
 // We marked them in control bytes as kSentinel.
 // Hash recomputation and full probing is done here.
 // This use case should be extremely rare.
-ABSL_ATTRIBUTE_NOINLINE size_t
-ProcessProbedMarkedElements(CommonFields& c, const PolicyFunctions& policy,
-                            ctrl_t* old_ctrl, void* old_slots, size_t start) {
+ABSL_ATTRIBUTE_NOINLINE size_t ProcessProbedMarkedElements(
+    CommonFields& c, const PolicyFunctions& __restrict policy, ctrl_t* old_ctrl,
+    void* old_slots, size_t start) {
   size_t old_capacity = PreviousCapacity(c.capacity());
   const size_t slot_size = policy.slot_size;
   void* new_slots = c.slot_array();
@@ -1029,7 +1035,7 @@
   // Finds new position for each element and transfers it to the new slots.
   // Returns the total probe length.
   size_t DecodeAndInsertToTable(CommonFields& common,
-                                const PolicyFunctions& policy,
+                                const PolicyFunctions& __restrict policy,
                                 void* old_slots) const {
     if (pos_ == buffer_) {
       return 0;
@@ -1103,7 +1109,7 @@
   // Finds new position for each element and transfers it to the new slots.
   // Returns the total probe length.
   ABSL_ATTRIBUTE_NOINLINE size_t DecodeAndInsertToTableOverflow(
-      CommonFields& common, const PolicyFunctions& policy,
+      CommonFields& common, const PolicyFunctions& __restrict policy,
       void* old_slots) const {
     ABSL_SWISSTABLE_ASSERT(local_buffer_full_ &&
                            "must not be called when local buffer is not full");
@@ -1136,7 +1142,8 @@
 // Different encoder is used depending on the capacity of the table.
 // Returns total probe length.
 template <typename Encoder>
-size_t GrowToNextCapacity(CommonFields& common, const PolicyFunctions& policy,
+size_t GrowToNextCapacity(CommonFields& common,
+                          const PolicyFunctions& __restrict policy,
                           ctrl_t* old_ctrl, void* old_slots) {
   using ProbedItem = typename Encoder::ProbedItem;
   ABSL_SWISSTABLE_ASSERT(common.capacity() <= ProbedItem::kMaxNewCapacity);
@@ -1154,10 +1161,9 @@
 // Grows to next capacity for relatively small tables so that even if all
 // elements are probed, we don't need to overflow the local buffer.
 // Returns total probe length.
-size_t GrowToNextCapacityThatFitsInLocalBuffer(CommonFields& common,
-                                               const PolicyFunctions& policy,
-                                               ctrl_t* old_ctrl,
-                                               void* old_slots) {
+size_t GrowToNextCapacityThatFitsInLocalBuffer(
+    CommonFields& common, const PolicyFunctions& __restrict policy,
+    ctrl_t* old_ctrl, void* old_slots) {
   ABSL_SWISSTABLE_ASSERT(common.capacity() <= kMaxLocalBufferNewCapacity);
   return GrowToNextCapacity<
       ProbedItemEncoder<ProbedItem4Bytes, /*kGuaranteedFitToBuffer=*/true>>(
@@ -1167,20 +1173,20 @@
 // Grows to next capacity with different encodings. Returns total probe length.
 // These functions are useful to simplify profile analysis.
 size_t GrowToNextCapacity4BytesEncoder(CommonFields& common,
-                                       const PolicyFunctions& policy,
+                                       const PolicyFunctions& __restrict policy,
                                        ctrl_t* old_ctrl, void* old_slots) {
   return GrowToNextCapacity<ProbedItemEncoder<ProbedItem4Bytes>>(
       common, policy, old_ctrl, old_slots);
 }
 size_t GrowToNextCapacity8BytesEncoder(CommonFields& common,
-                                       const PolicyFunctions& policy,
+                                       const PolicyFunctions& __restrict policy,
                                        ctrl_t* old_ctrl, void* old_slots) {
   return GrowToNextCapacity<ProbedItemEncoder<ProbedItem8Bytes>>(
       common, policy, old_ctrl, old_slots);
 }
-size_t GrowToNextCapacity16BytesEncoder(CommonFields& common,
-                                        const PolicyFunctions& policy,
-                                        ctrl_t* old_ctrl, void* old_slots) {
+size_t GrowToNextCapacity16BytesEncoder(
+    CommonFields& common, const PolicyFunctions& __restrict policy,
+    ctrl_t* old_ctrl, void* old_slots) {
   return GrowToNextCapacity<ProbedItemEncoder<ProbedItem16Bytes>>(
       common, policy, old_ctrl, old_slots);
 }
@@ -1188,10 +1194,9 @@
 // Grows to next capacity for tables with relatively large capacity so that we
 // can't guarantee that all probed elements fit in the local buffer. Returns
 // total probe length.
-size_t GrowToNextCapacityOverflowLocalBuffer(CommonFields& common,
-                                             const PolicyFunctions& policy,
-                                             ctrl_t* old_ctrl,
-                                             void* old_slots) {
+size_t GrowToNextCapacityOverflowLocalBuffer(
+    CommonFields& common, const PolicyFunctions& __restrict policy,
+    ctrl_t* old_ctrl, void* old_slots) {
   const size_t new_capacity = common.capacity();
   if (ABSL_PREDICT_TRUE(new_capacity <= ProbedItem4Bytes::kMaxNewCapacity)) {
     return GrowToNextCapacity4BytesEncoder(common, policy, old_ctrl, old_slots);
@@ -1207,7 +1212,7 @@
 // capacity of the table. Returns total probe length.
 ABSL_ATTRIBUTE_NOINLINE
 size_t GrowToNextCapacityDispatch(CommonFields& common,
-                                  const PolicyFunctions& policy,
+                                  const PolicyFunctions& __restrict policy,
                                   ctrl_t* old_ctrl, void* old_slots) {
   const size_t new_capacity = common.capacity();
   if (ABSL_PREDICT_TRUE(new_capacity <= kMaxLocalBufferNewCapacity)) {
@@ -1221,9 +1226,9 @@
 
 // Grows to next capacity and prepares insert for the given new_hash.
 // Returns the offset of the new element.
-size_t GrowToNextCapacityAndPrepareInsert(CommonFields& common,
-                                          const PolicyFunctions& policy,
-                                          size_t new_hash) {
+size_t GrowToNextCapacityAndPrepareInsert(
+    CommonFields& common, const PolicyFunctions& __restrict policy,
+    size_t new_hash) {
   ABSL_SWISSTABLE_ASSERT(common.growth_left() == 0);
   const size_t old_capacity = common.capacity();
   ABSL_SWISSTABLE_ASSERT(old_capacity == 0 ||
@@ -1321,9 +1326,9 @@
 // Called whenever the table needs to vacate empty slots either by removing
 // tombstones via rehash or growth to next capacity.
 ABSL_ATTRIBUTE_NOINLINE
-size_t RehashOrGrowToNextCapacityAndPrepareInsert(CommonFields& common,
-                                                  const PolicyFunctions& policy,
-                                                  size_t new_hash) {
+size_t RehashOrGrowToNextCapacityAndPrepareInsert(
+    CommonFields& common, const PolicyFunctions& __restrict policy,
+    size_t new_hash) {
   const size_t cap = common.capacity();
   ABSL_ASSUME(cap > 0);
   if (cap > Group::kWidth &&
@@ -1380,7 +1385,8 @@
 // Slow path for PrepareInsertNonSoo that is called when the table has deleted
 // slots or need to be resized or rehashed.
 size_t PrepareInsertNonSooSlow(CommonFields& common,
-                               const PolicyFunctions& policy, size_t hash) {
+                               const PolicyFunctions& __restrict policy,
+                               size_t hash) {
   const GrowthInfo growth_info = common.growth_info();
   ABSL_SWISSTABLE_ASSERT(!growth_info.HasNoDeletedAndGrowthLeft());
   if (ABSL_PREDICT_TRUE(growth_info.HasNoGrowthLeftAndNoDeleted())) {
@@ -1402,7 +1408,6 @@
   return target.offset;
 }
 
-
 // Resizes empty non-allocated SOO table to NextCapacity(SooCapacity()),
 // forces the table to be sampled and prepares the insert.
 // SOO tables need to switch from SOO to heap in order to store the infoz.
@@ -1411,7 +1416,8 @@
 //   2. `c.empty()`.
 ABSL_ATTRIBUTE_NOINLINE size_t
 GrowEmptySooTableToNextCapacityForceSamplingAndPrepareInsert(
-    CommonFields& common, const PolicyFunctions& policy, size_t new_hash) {
+    CommonFields& common, const PolicyFunctions& __restrict policy,
+    size_t new_hash) {
   ResizeEmptyNonAllocatedTableImpl(common, policy, NextCapacity(SooCapacity()),
                                    /*force_infoz=*/true);
   PrepareInsertCommon(common);
@@ -1428,9 +1434,9 @@
 //   2. `c.empty()`.
 //   3. `new_size > policy.soo_capacity()`.
 // The table will be attempted to be sampled.
-void ReserveEmptyNonAllocatedTableToFitNewSize(CommonFields& common,
-                                               const PolicyFunctions& policy,
-                                               size_t new_size) {
+void ReserveEmptyNonAllocatedTableToFitNewSize(
+    CommonFields& common, const PolicyFunctions& __restrict policy,
+    size_t new_size) {
   ValidateMaxSize(new_size, policy.slot_size);
   ABSL_ASSUME(new_size > 0);
   ResizeEmptyNonAllocatedTableImpl(common, policy, SizeToCapacity(new_size),
@@ -1447,7 +1453,8 @@
 //   1. `c.capacity() > policy.soo_capacity()` OR `!c.empty()`.
 // Reserving already allocated tables is considered to be a rare case.
 ABSL_ATTRIBUTE_NOINLINE void ReserveAllocatedTable(
-    CommonFields& common, const PolicyFunctions& policy, size_t new_size) {
+    CommonFields& common, const PolicyFunctions& __restrict policy,
+    size_t new_size) {
   const size_t cap = common.capacity();
   ValidateMaxSize(new_size, policy.slot_size);
   ABSL_ASSUME(new_size > 0);
@@ -1474,15 +1481,16 @@
   return &common;
 }
 
-void ResizeAllocatedTableWithSeedChange(CommonFields& common,
-                                        const PolicyFunctions& policy,
-                                        size_t new_capacity) {
+void ResizeAllocatedTableWithSeedChange(
+    CommonFields& common, const PolicyFunctions& __restrict policy,
+    size_t new_capacity) {
   ResizeNonSooImpl<ResizeNonSooMode::kGuaranteedAllocated>(
       common, policy, new_capacity, common.infoz());
 }
 
 void ReserveEmptyNonAllocatedTableToFitBucketCount(
-    CommonFields& common, const PolicyFunctions& policy, size_t bucket_count) {
+    CommonFields& common, const PolicyFunctions& __restrict policy,
+    size_t bucket_count) {
   size_t new_capacity = NormalizeCapacity(bucket_count);
   ValidateMaxSize(CapacityToGrowth(new_capacity), policy.slot_size);
   ResizeEmptyNonAllocatedTableImpl(common, policy, new_capacity,
@@ -1491,10 +1499,9 @@
 
 // Resizes a full SOO table to the NextCapacity(SooCapacity()).
 template <size_t SooSlotMemcpySize, bool TransferUsesMemcpy>
-size_t GrowSooTableToNextCapacityAndPrepareInsert(CommonFields& common,
-                                                  const PolicyFunctions& policy,
-                                                  size_t new_hash,
-                                                  ctrl_t soo_slot_ctrl) {
+size_t GrowSooTableToNextCapacityAndPrepareInsert(
+    CommonFields& common, const PolicyFunctions& __restrict policy,
+    size_t new_hash, ctrl_t soo_slot_ctrl) {
   AssertSoo(common, policy);
   if (ABSL_PREDICT_FALSE(soo_slot_ctrl == ctrl_t::kEmpty)) {
     // The table is empty, it is only used for forced sampling of SOO tables.
@@ -1566,14 +1573,15 @@
 }
 
 void GrowFullSooTableToNextCapacityForceSampling(
-    CommonFields& common, const PolicyFunctions& policy) {
+    CommonFields& common, const PolicyFunctions& __restrict policy) {
   AssertFullSoo(common, policy);
   ResizeFullSooTable(
       common, policy, NextCapacity(SooCapacity()),
       ResizeFullSooTableSamplingMode::kForceSampleNoResizeIfUnsampled);
 }
 
-void Rehash(CommonFields& common, const PolicyFunctions& policy, size_t n) {
+void Rehash(CommonFields& common, const PolicyFunctions& __restrict policy,
+            size_t n) {
   const size_t cap = common.capacity();
 
   auto clear_backing_array = [&]() {
@@ -1640,7 +1648,7 @@
   }
 }
 
-void Copy(CommonFields& common, const PolicyFunctions& policy,
+void Copy(CommonFields& common, const PolicyFunctions& __restrict policy,
           const CommonFields& other,
           absl::FunctionRef<void(void*, const void*)> copy_fn) {
   const size_t size = other.size();
@@ -1716,7 +1724,8 @@
 }
 
 void ReserveTableToFitNewSize(CommonFields& common,
-                              const PolicyFunctions& policy, size_t new_size) {
+                              const PolicyFunctions& __restrict policy,
+                              size_t new_size) {
   common.reset_reserved_growth(new_size);
   common.set_reservation_size(new_size);
   ABSL_SWISSTABLE_ASSERT(new_size > policy.soo_capacity());
@@ -1736,7 +1745,8 @@
   ReserveAllocatedTable(common, policy, new_size);
 }
 
-size_t PrepareInsertNonSoo(CommonFields& common, const PolicyFunctions& policy,
+size_t PrepareInsertNonSoo(CommonFields& common,
+                           const PolicyFunctions& __restrict policy,
                            size_t hash, FindInfo target) {
   const bool rehash_for_bug_detection =
       common.should_rehash_for_bug_detection_on_insert() &&
diff --git a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h
index 512c946..3bc86d1 100644
--- a/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h
+++ b/third_party/abseil-cpp/absl/container/internal/raw_hash_set.h
@@ -1827,11 +1827,6 @@
                                         const PolicyFunctions& policy,
                                         size_t new_capacity);
 
-inline void PrepareInsertCommon(CommonFields& common) {
-  common.increment_size();
-  common.maybe_increment_generation_on_insert();
-}
-
 // ClearBackingArray clears the backing array, either modifying it in place,
 // or creating a new one based on the value of "reuse".
 // REQUIRES: c.capacity > 0
diff --git a/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake b/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake
index cc0f4bb..7d8af92 100644
--- a/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake
+++ b/third_party/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake
@@ -23,6 +23,7 @@
     "-Wno-implicit-int-conversion"
     "-Wno-missing-prototypes"
     "-Wno-missing-variable-declarations"
+    "-Wno-nullability-completeness"
     "-Wno-shadow"
     "-Wno-shorten-64-to-32"
     "-Wno-sign-compare"
@@ -139,7 +140,6 @@
     "-Winvalid-constexpr"
     "-Wliteral-conversion"
     "-Wmissing-declarations"
-    "-Wnullability-completeness"
     "-Woverlength-strings"
     "-Wpointer-arith"
     "-Wself-assign"
@@ -165,6 +165,7 @@
     "-Wno-implicit-int-conversion"
     "-Wno-missing-prototypes"
     "-Wno-missing-variable-declarations"
+    "-Wno-nullability-completeness"
     "-Wno-shadow"
     "-Wno-shorten-64-to-32"
     "-Wno-sign-compare"
diff --git a/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl b/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl
index 35319f0..23896e9 100644
--- a/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl
+++ b/third_party/abseil-cpp/absl/copts/GENERATED_copts.bzl
@@ -24,6 +24,7 @@
     "-Wno-implicit-int-conversion",
     "-Wno-missing-prototypes",
     "-Wno-missing-variable-declarations",
+    "-Wno-nullability-completeness",
     "-Wno-shadow",
     "-Wno-shorten-64-to-32",
     "-Wno-sign-compare",
@@ -140,7 +141,6 @@
     "-Winvalid-constexpr",
     "-Wliteral-conversion",
     "-Wmissing-declarations",
-    "-Wnullability-completeness",
     "-Woverlength-strings",
     "-Wpointer-arith",
     "-Wself-assign",
@@ -166,6 +166,7 @@
     "-Wno-implicit-int-conversion",
     "-Wno-missing-prototypes",
     "-Wno-missing-variable-declarations",
+    "-Wno-nullability-completeness",
     "-Wno-shadow",
     "-Wno-shorten-64-to-32",
     "-Wno-sign-compare",
diff --git a/third_party/abseil-cpp/absl/copts/copts.py b/third_party/abseil-cpp/absl/copts/copts.py
index 941528e..8cf8f31 100644
--- a/third_party/abseil-cpp/absl/copts/copts.py
+++ b/third_party/abseil-cpp/absl/copts/copts.py
@@ -93,6 +93,7 @@
     "-Wno-implicit-int-conversion",
     "-Wno-missing-prototypes",
     "-Wno-missing-variable-declarations",
+    "-Wno-nullability-completeness",
     "-Wno-shadow",
     "-Wno-shorten-64-to-32",
     "-Wno-sign-compare",
diff --git a/third_party/abseil-cpp/absl/debugging/internal/decode_rust_punycode.h b/third_party/abseil-cpp/absl/debugging/internal/decode_rust_punycode.h
index b1b1c97..44aad8a 100644
--- a/third_party/abseil-cpp/absl/debugging/internal/decode_rust_punycode.h
+++ b/third_party/abseil-cpp/absl/debugging/internal/decode_rust_punycode.h
@@ -23,10 +23,10 @@
 namespace debugging_internal {
 
 struct DecodeRustPunycodeOptions {
-  const char* punycode_begin;
-  const char* punycode_end;
-  char* out_begin;
-  char* out_end;
+  const char* absl_nonnull punycode_begin;
+  const char* absl_nonnull punycode_end;
+  char* absl_nonnull out_begin;
+  char* absl_nonnull out_end;
 };
 
 // Given Rust Punycode in `punycode_begin .. punycode_end`, writes the
diff --git a/third_party/abseil-cpp/absl/debugging/internal/demangle.cc b/third_party/abseil-cpp/absl/debugging/internal/demangle.cc
index dc15b8e..5f62ebb 100644
--- a/third_party/abseil-cpp/absl/debugging/internal/demangle.cc
+++ b/third_party/abseil-cpp/absl/debugging/internal/demangle.cc
@@ -484,36 +484,6 @@
 
 static bool IsDigit(char c) { return c >= '0' && c <= '9'; }
 
-// Returns true if "str" is a function clone suffix.  These suffixes are used
-// by GCC 4.5.x and later versions (and our locally-modified version of GCC
-// 4.4.x) to indicate functions which have been cloned during optimization.
-// We treat any sequence (.<alpha>+.<digit>+)+ as a function clone suffix.
-// Additionally, '_' is allowed along with the alphanumeric sequence.
-static bool IsFunctionCloneSuffix(const char *str) {
-  size_t i = 0;
-  while (str[i] != '\0') {
-    bool parsed = false;
-    // Consume a single [.<alpha> | _]*[.<digit>]* sequence.
-    if (str[i] == '.' && (IsAlpha(str[i + 1]) || str[i + 1] == '_')) {
-      parsed = true;
-      i += 2;
-      while (IsAlpha(str[i]) || str[i] == '_') {
-        ++i;
-      }
-    }
-    if (str[i] == '.' && IsDigit(str[i + 1])) {
-      parsed = true;
-      i += 2;
-      while (IsDigit(str[i])) {
-        ++i;
-      }
-    }
-    if (!parsed)
-      return false;
-  }
-  return true;  // Consumed everything in "str".
-}
-
 static bool EndsWith(State *state, const char chr) {
   return state->parse_state.out_cur_idx > 0 &&
          state->parse_state.out_cur_idx < state->out_end_idx &&
@@ -2932,7 +2902,7 @@
   if (ParseMangledName(state)) {
     if (RemainingInput(state)[0] != '\0') {
       // Drop trailing function clone suffix, if any.
-      if (IsFunctionCloneSuffix(RemainingInput(state))) {
+      if (RemainingInput(state)[0] == '.') {
         return true;
       }
       // Append trailing version suffix if any.
diff --git a/third_party/abseil-cpp/absl/debugging/internal/demangle_test.cc b/third_party/abseil-cpp/absl/debugging/internal/demangle_test.cc
index 9c8225a..2012184 100644
--- a/third_party/abseil-cpp/absl/debugging/internal/demangle_test.cc
+++ b/third_party/abseil-cpp/absl/debugging/internal/demangle_test.cc
@@ -556,14 +556,15 @@
   EXPECT_TRUE(Demangle("_ZL3Foov.part.9.165493.constprop.775.31805", tmp,
                        sizeof(tmp)));
   EXPECT_STREQ("Foo()", tmp);
-  // Invalid (. without anything else), should not demangle.
-  EXPECT_FALSE(Demangle("_ZL3Foov.", tmp, sizeof(tmp)));
-  // Invalid (. with mix of alpha and digits), should not demangle.
-  EXPECT_FALSE(Demangle("_ZL3Foov.abc123", tmp, sizeof(tmp)));
-  // Invalid (.clone. not followed by number), should not demangle.
-  EXPECT_FALSE(Demangle("_ZL3Foov.clone.", tmp, sizeof(tmp)));
-  // Invalid (.constprop. not followed by number), should not demangle.
-  EXPECT_FALSE(Demangle("_ZL3Foov.isra.2.constprop.", tmp, sizeof(tmp)));
+  // Other suffixes should demangle too.
+  EXPECT_TRUE(Demangle("_ZL3Foov.", tmp, sizeof(tmp)));
+  EXPECT_STREQ("Foo()", tmp);
+  EXPECT_TRUE(Demangle("_ZL3Foov.abc123", tmp, sizeof(tmp)));
+  EXPECT_STREQ("Foo()", tmp);
+  EXPECT_TRUE(Demangle("_ZL3Foov.clone.", tmp, sizeof(tmp)));
+  EXPECT_STREQ("Foo()", tmp);
+  EXPECT_TRUE(Demangle("_ZL3Foov.isra.2.constprop.", tmp, sizeof(tmp)));
+  EXPECT_STREQ("Foo()", tmp);
 }
 
 TEST(Demangle, Discriminators) {
diff --git a/third_party/abseil-cpp/absl/hash/hash_test.cc b/third_party/abseil-cpp/absl/hash/hash_test.cc
index c3182f1..7582f54 100644
--- a/third_party/abseil-cpp/absl/hash/hash_test.cc
+++ b/third_party/abseil-cpp/absl/hash/hash_test.cc
@@ -29,6 +29,7 @@
 #include <ostream>
 #include <set>
 #include <string>
+#include <string_view>
 #include <tuple>
 #include <type_traits>
 #include <unordered_map>
@@ -55,10 +56,6 @@
 #include <filesystem>  // NOLINT
 #endif
 
-#ifdef ABSL_HAVE_STD_STRING_VIEW
-#include <string_view>
-#endif
-
 namespace {
 
 using ::absl::hash_test_internal::is_hashable;
@@ -495,22 +492,15 @@
 }
 
 TEST(HashValueTest, WStringView) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   EXPECT_TRUE((is_hashable<std::wstring_view>::value));
 
   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(std::make_tuple(
       std::wstring_view(), std::wstring_view(L"ABC"), std::wstring_view(L"ABC"),
       std::wstring_view(L"Some other different string_view"),
       std::wstring_view(L"Iñtërnâtiônàlizætiøn"))));
-#endif
 }
 
 TEST(HashValueTest, U16StringView) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   EXPECT_TRUE((is_hashable<std::u16string_view>::value));
 
   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
@@ -518,13 +508,9 @@
                       std::u16string_view(u"ABC"),
                       std::u16string_view(u"Some other different string_view"),
                       std::u16string_view(u"Iñtërnâtiônàlizætiøn"))));
-#endif
 }
 
 TEST(HashValueTest, U32StringView) {
-#ifndef ABSL_HAVE_STD_STRING_VIEW
-  GTEST_SKIP();
-#else
   EXPECT_TRUE((is_hashable<std::u32string_view>::value));
 
   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
@@ -532,7 +518,6 @@
                       std::u32string_view(U"ABC"),
                       std::u32string_view(U"Some other different string_view"),
                       std::u32string_view(U"Iñtërnâtiônàlizætiøn"))));
-#endif
 }
 
 TEST(HashValueTest, StdFilesystemPath) {
diff --git a/third_party/abseil-cpp/absl/hash/internal/hash.h b/third_party/abseil-cpp/absl/hash/internal/hash.h
index c7916b5..63b35490 100644
--- a/third_party/abseil-cpp/absl/hash/internal/hash.h
+++ b/third_party/abseil-cpp/absl/hash/internal/hash.h
@@ -65,6 +65,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <string_view>
 #include <tuple>
 #include <type_traits>
 #include <unordered_map>
@@ -92,10 +93,6 @@
 #include <filesystem>  // NOLINT
 #endif
 
-#ifdef ABSL_HAVE_STD_STRING_VIEW
-#include <string_view>
-#endif
-
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
@@ -640,8 +637,6 @@
       WeaklyMixedInteger{str.size()});
 }
 
-#ifdef ABSL_HAVE_STD_STRING_VIEW
-
 // Support std::wstring_view, std::u16string_view and std::u32string_view.
 template <typename Char, typename H,
           typename = absl::enable_if_t<std::is_same<Char, wchar_t>::value ||
@@ -653,8 +648,6 @@
       WeaklyMixedInteger{str.size()});
 }
 
-#endif  // ABSL_HAVE_STD_STRING_VIEW
-
 #if defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703L && \
     (!defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__) ||        \
      __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 130000) &&       \
diff --git a/third_party/abseil-cpp/absl/log/CMakeLists.txt b/third_party/abseil-cpp/absl/log/CMakeLists.txt
index 6aae05d..130897f 100644
--- a/third_party/abseil-cpp/absl/log/CMakeLists.txt
+++ b/third_party/abseil-cpp/absl/log/CMakeLists.txt
@@ -218,6 +218,7 @@
     absl::span
     absl::strerror
     absl::strings
+    absl::strings_internal
     absl::time
 )
 
@@ -395,6 +396,7 @@
   DEPS
     absl::config
     absl::strings
+    absl::strings_internal
     absl::span
 )
 
diff --git a/third_party/abseil-cpp/absl/log/internal/BUILD.bazel b/third_party/abseil-cpp/absl/log/internal/BUILD.bazel
index 44ec71b..953b690 100644
--- a/third_party/abseil-cpp/absl/log/internal/BUILD.bazel
+++ b/third_party/abseil-cpp/absl/log/internal/BUILD.bazel
@@ -205,6 +205,7 @@
         "//absl/log:log_sink_registry",
         "//absl/memory",
         "//absl/strings",
+        "//absl/strings:internal",
         "//absl/time",
         "//absl/types:span",
     ],
@@ -218,6 +219,7 @@
     deps = [
         "//absl/base:config",
         "//absl/strings",
+        "//absl/strings:internal",
         "//absl/types:span",
     ],
 )
diff --git a/third_party/abseil-cpp/absl/log/internal/BUILD.gn b/third_party/abseil-cpp/absl/log/internal/BUILD.gn
index 54fe654..fda8e48d 100644
--- a/third_party/abseil-cpp/absl/log/internal/BUILD.gn
+++ b/third_party/abseil-cpp/absl/log/internal/BUILD.gn
@@ -124,6 +124,7 @@
     "//third_party/abseil-cpp/absl/log:log_sink_registry",
     "//third_party/abseil-cpp/absl/memory",
     "//third_party/abseil-cpp/absl/strings",
+    "//third_party/abseil-cpp/absl/strings:internal",
     "//third_party/abseil-cpp/absl/strings:string_view",
     "//third_party/abseil-cpp/absl/time",
     "//third_party/abseil-cpp/absl/types:span",
@@ -134,6 +135,7 @@
   public = [ "append_truncated.h" ]
   deps = [
     "//third_party/abseil-cpp/absl/base:config",
+    "//third_party/abseil-cpp/absl/strings:internal",
     "//third_party/abseil-cpp/absl/strings:string_view",
     "//third_party/abseil-cpp/absl/types:span",
   ]
diff --git a/third_party/abseil-cpp/absl/log/internal/append_truncated.h b/third_party/abseil-cpp/absl/log/internal/append_truncated.h
index f0e7912..d420a8b 100644
--- a/third_party/abseil-cpp/absl/log/internal/append_truncated.h
+++ b/third_party/abseil-cpp/absl/log/internal/append_truncated.h
@@ -17,8 +17,10 @@
 
 #include <cstddef>
 #include <cstring>
+#include <string_view>
 
 #include "absl/base/config.h"
+#include "absl/strings/internal/utf8.h"
 #include "absl/strings/string_view.h"
 #include "absl/types/span.h"
 
@@ -33,6 +35,32 @@
   dst.remove_prefix(src.size());
   return src.size();
 }
+// Likewise, but it also takes a wide character string and transforms it into a
+// UTF-8 encoded byte string regardless of the current locale.
+// - On platforms where `wchar_t` is 2 bytes (e.g., Windows), the input is
+//   treated as UTF-16.
+// - On platforms where `wchar_t` is 4 bytes (e.g., Linux, macOS), the input
+//   is treated as UTF-32.
+inline size_t AppendTruncated(std::wstring_view src, absl::Span<char> &dst) {
+  absl::strings_internal::ShiftState state;
+  size_t total_bytes_written = 0;
+  for (const wchar_t wc : src) {
+    // If the destination buffer might not be large enough to write the next
+    // character, stop.
+    if (dst.size() < absl::strings_internal::kMaxEncodedUTF8Size) break;
+    size_t bytes_written =
+        absl::strings_internal::WideToUtf8(wc, dst.data(), state);
+    if (bytes_written == static_cast<size_t>(-1)) {
+      // Invalid character. Encode REPLACEMENT CHARACTER (U+FFFD) instead.
+      constexpr wchar_t kReplacementCharacter = L'\uFFFD';
+      bytes_written = absl::strings_internal::WideToUtf8(kReplacementCharacter,
+                                                         dst.data(), state);
+    }
+    dst.remove_prefix(bytes_written);
+    total_bytes_written += bytes_written;
+  }
+  return total_bytes_written;
+}
 // Likewise, but `n` copies of `c`.
 inline size_t AppendTruncated(char c, size_t n, absl::Span<char> &dst) {
   if (n > dst.size()) n = dst.size();
diff --git a/third_party/abseil-cpp/absl/log/internal/check_op.h b/third_party/abseil-cpp/absl/log/internal/check_op.h
index dc7d19e..72534028 100644
--- a/third_party/abseil-cpp/absl/log/internal/check_op.h
+++ b/third_party/abseil-cpp/absl/log/internal/check_op.h
@@ -224,7 +224,7 @@
 void MakeCheckOpValueString(std::ostream& os, char v);
 void MakeCheckOpValueString(std::ostream& os, signed char v);
 void MakeCheckOpValueString(std::ostream& os, unsigned char v);
-void MakeCheckOpValueString(std::ostream& os, const void* p);
+void MakeCheckOpValueString(std::ostream& os, const void* absl_nullable p);
 
 namespace detect_specialization {
 
@@ -266,8 +266,9 @@
 double operator<<(std::ostream&, double value);
 long double operator<<(std::ostream&, long double value);
 bool operator<<(std::ostream&, bool value);
-const void* operator<<(std::ostream&, const void* value);
-const void* operator<<(std::ostream&, std::nullptr_t);
+const void* absl_nullable operator<<(std::ostream&,
+                                     const void* absl_nullable value);
+const void* absl_nullable operator<<(std::ostream&, std::nullptr_t);
 
 // These `char` overloads are specified like this in the standard, so we have to
 // write them exactly the same to ensure the call is ambiguous.
@@ -281,13 +282,14 @@
 template <typename Traits>
 unsigned char operator<<(std::basic_ostream<char, Traits>&, unsigned char);
 template <typename Traits>
-const char* operator<<(std::basic_ostream<char, Traits>&, const char*);
+const char* absl_nonnull operator<<(std::basic_ostream<char, Traits>&,
+                                    const char* absl_nonnull);
 template <typename Traits>
-const signed char* operator<<(std::basic_ostream<char, Traits>&,
-                              const signed char*);
+const signed char* absl_nonnull operator<<(std::basic_ostream<char, Traits>&,
+                                           const signed char* absl_nonnull);
 template <typename Traits>
-const unsigned char* operator<<(std::basic_ostream<char, Traits>&,
-                                const unsigned char*);
+const unsigned char* absl_nonnull operator<<(std::basic_ostream<char, Traits>&,
+                                             const unsigned char* absl_nonnull);
 
 // This overload triggers when the call is not ambiguous.
 // It means that T is being printed with some overload not on this list.
@@ -312,7 +314,8 @@
 
   void Append(absl::string_view text);
   void Append(size_t length, char ch);
-  friend void AbslFormatFlush(StringifySink* sink, absl::string_view text);
+  friend void AbslFormatFlush(StringifySink* absl_nonnull sink,
+                              absl::string_view text);
 
  private:
   std::ostream& os_;
@@ -376,10 +379,12 @@
 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(unsigned char);
 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const std::string&);
 ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const absl::string_view&);
-ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const char*);
-ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const signed char*);
-ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const unsigned char*);
-ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const void*);
+ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const char* absl_nonnull);
+ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(
+    const signed char* absl_nonnull);
+ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(
+    const unsigned char* absl_nonnull);
+ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN(const void* absl_nonnull);
 #undef ABSL_LOG_INTERNAL_DEFINE_MAKE_CHECK_OP_STRING_EXTERN
 
 // `ABSL_LOG_INTERNAL_CHECK_OP_IMPL_RESULT` skips formatting the Check_OP result
diff --git a/third_party/abseil-cpp/absl/log/internal/log_message.cc b/third_party/abseil-cpp/absl/log/internal/log_message.cc
index aaaaf03..07d17a02 100644
--- a/third_party/abseil-cpp/absl/log/internal/log_message.cc
+++ b/third_party/abseil-cpp/absl/log/internal/log_message.cc
@@ -27,10 +27,12 @@
 #include <algorithm>
 #include <array>
 #include <atomic>
+#include <cwchar>
 #include <ios>
 #include <memory>
 #include <ostream>
 #include <string>
+#include <string_view>
 #include <tuple>
 
 #include "absl/base/attributes.h"
@@ -47,12 +49,14 @@
 #include "absl/log/internal/globals.h"
 #include "absl/log/internal/log_format.h"
 #include "absl/log/internal/log_sink_set.h"
+#include "absl/log/internal/nullguard.h"
 #include "absl/log/internal/proto.h"
 #include "absl/log/internal/structured_proto.h"
 #include "absl/log/log_entry.h"
 #include "absl/log/log_sink.h"
 #include "absl/log/log_sink_registry.h"
 #include "absl/memory/memory.h"
+#include "absl/strings/internal/utf8.h"
 #include "absl/strings/string_view.h"
 #include "absl/time/clock.h"
 #include "absl/time/time.h"
@@ -403,6 +407,35 @@
   CopyToEncodedBuffer<StringType::kNotLiteral>(v);
   return *this;
 }
+
+LogMessage& LogMessage::operator<<(const std::wstring& v) {
+  CopyToEncodedBuffer<StringType::kNotLiteral>(v);
+  return *this;
+}
+
+LogMessage& LogMessage::operator<<(std::wstring_view v) {
+  CopyToEncodedBuffer<StringType::kNotLiteral>(v);
+  return *this;
+}
+
+template <>
+LogMessage& LogMessage::operator<< <const wchar_t*>(
+    const wchar_t* absl_nullable const& v) {
+  if (v == nullptr) {
+    CopyToEncodedBuffer<StringType::kNotLiteral>(
+        absl::string_view(kCharNull.data(), kCharNull.size() - 1));
+  } else {
+    CopyToEncodedBuffer<StringType::kNotLiteral>(
+        std::wstring_view(v, wcsnlen(v, data_->encoded_remaining().size())));
+  }
+  return *this;
+}
+
+LogMessage& LogMessage::operator<<(wchar_t v) {
+  CopyToEncodedBuffer<StringType::kNotLiteral>(std::wstring_view(&v, 1));
+  return *this;
+}
+
 LogMessage& LogMessage::operator<<(std::ostream& (*m)(std::ostream& os)) {
   OstreamView view(*data_);
   data_->manipulated << m;
@@ -625,6 +658,37 @@
 template void LogMessage::CopyToEncodedBuffer<
     LogMessage::StringType::kNotLiteral>(char ch, size_t num);
 
+template <LogMessage::StringType str_type>
+void LogMessage::CopyToEncodedBuffer(std::wstring_view str) {
+  auto encoded_remaining_copy = data_->encoded_remaining();
+  constexpr uint8_t tag_value = str_type == StringType::kLiteral
+                                    ? ValueTag::kStringLiteral
+                                    : ValueTag::kString;
+  size_t max_str_byte_length =
+      absl::strings_internal::kMaxEncodedUTF8Size * str.length();
+  auto value_start =
+      EncodeMessageStart(EventTag::kValue,
+                         BufferSizeFor(tag_value, WireType::kLengthDelimited) +
+                             max_str_byte_length,
+                         &encoded_remaining_copy);
+  auto str_start = EncodeMessageStart(tag_value, max_str_byte_length,
+                                      &encoded_remaining_copy);
+  if (str_start.data()) {
+    log_internal::AppendTruncated(str, encoded_remaining_copy);
+    EncodeMessageLength(str_start, &encoded_remaining_copy);
+    EncodeMessageLength(value_start, &encoded_remaining_copy);
+    data_->encoded_remaining() = encoded_remaining_copy;
+  } else {
+    // The field header(s) did not fit; zero `encoded_remaining()` so we don't
+    // write anything else later.
+    data_->encoded_remaining().remove_suffix(data_->encoded_remaining().size());
+  }
+}
+template void LogMessage::CopyToEncodedBuffer<LogMessage::StringType::kLiteral>(
+    std::wstring_view str);
+template void LogMessage::CopyToEncodedBuffer<
+    LogMessage::StringType::kNotLiteral>(std::wstring_view str);
+
 template void LogMessage::CopyToEncodedBufferWithStructuredProtoField<
     LogMessage::StringType::kLiteral>(StructuredProtoField field,
                                       absl::string_view str);
@@ -682,17 +746,13 @@
   *this << "Check failed: " << failure_msg << " ";
 }
 
-LogMessageFatal::~LogMessageFatal() {
-  FailWithoutStackTrace();
-}
+LogMessageFatal::~LogMessageFatal() { FailWithoutStackTrace(); }
 
 LogMessageDebugFatal::LogMessageDebugFatal(const char* absl_nonnull file,
                                            int line)
     : LogMessage(file, line, absl::LogSeverity::kFatal) {}
 
-LogMessageDebugFatal::~LogMessageDebugFatal() {
-  FailWithoutStackTrace();
-}
+LogMessageDebugFatal::~LogMessageDebugFatal() { FailWithoutStackTrace(); }
 
 LogMessageQuietlyDebugFatal::LogMessageQuietlyDebugFatal(
     const char* absl_nonnull file, int line)
@@ -700,9 +760,7 @@
   SetFailQuietly();
 }
 
-LogMessageQuietlyDebugFatal::~LogMessageQuietlyDebugFatal() {
-  FailQuietly();
-}
+LogMessageQuietlyDebugFatal::~LogMessageQuietlyDebugFatal() { FailQuietly(); }
 
 LogMessageQuietlyFatal::LogMessageQuietlyFatal(const char* absl_nonnull file,
                                                int line)
@@ -717,9 +775,7 @@
   *this << "Check failed: " << failure_msg << " ";
 }
 
-LogMessageQuietlyFatal::~LogMessageQuietlyFatal() {
-  FailQuietly();
-}
+LogMessageQuietlyFatal::~LogMessageQuietlyFatal() { FailQuietly(); }
 #if defined(_MSC_VER) && !defined(__clang__)
 #pragma warning(pop)
 #endif
diff --git a/third_party/abseil-cpp/absl/log/internal/log_message.h b/third_party/abseil-cpp/absl/log/internal/log_message.h
index e7eff47b..1aaf05e 100644
--- a/third_party/abseil-cpp/absl/log/internal/log_message.h
+++ b/third_party/abseil-cpp/absl/log/internal/log_message.h
@@ -27,12 +27,15 @@
 #ifndef ABSL_LOG_INTERNAL_LOG_MESSAGE_H_
 #define ABSL_LOG_INTERNAL_LOG_MESSAGE_H_
 
+#include <wchar.h>
+
 #include <cstddef>
 #include <ios>
 #include <memory>
 #include <ostream>
 #include <streambuf>
 #include <string>
+#include <string_view>
 #include <type_traits>
 
 #include "absl/base/attributes.h"
@@ -158,6 +161,13 @@
   LogMessage& operator<<(const std::string& v);
   LogMessage& operator<<(absl::string_view v);
 
+  // Wide string overloads (since std::ostream does not provide them).
+  LogMessage& operator<<(const std::wstring& v);
+  LogMessage& operator<<(std::wstring_view v);
+  // `const wchar_t*` is handled by `operator<< <const wchar_t*>`.
+  LogMessage& operator<<(wchar_t* absl_nullable v);
+  LogMessage& operator<<(wchar_t v);
+
   // Handle stream manipulators e.g. std::endl.
   LogMessage& operator<<(std::ostream& (*absl_nonnull m)(std::ostream& os));
   LogMessage& operator<<(std::ios_base& (*absl_nonnull m)(std::ios_base& os));
@@ -169,17 +179,20 @@
   // this template for every value of `SIZE` encountered in each source code
   // file. That significantly increases linker input sizes. Inlining is cheap
   // because the argument to this overload is almost always a string literal so
-  // the call to `strlen` can be replaced at compile time. The overload for
-  // `char[]` below should not be inlined. The compiler typically does not have
-  // the string at compile time and cannot replace the call to `strlen` so
-  // inlining it increases the binary size. See the discussion on
+  // the call to `strlen` can be replaced at compile time. The overloads for
+  // `char[]`/`wchar_t[]` below should not be inlined. The compiler typically
+  // does not have the string at compile time and cannot replace the call to
+  // `strlen` so inlining it increases the binary size. See the discussion on
   // cl/107527369.
   template <int SIZE>
   LogMessage& operator<<(const char (&buf)[SIZE]);
+  template <int SIZE>
+  LogMessage& operator<<(const wchar_t (&buf)[SIZE]);
 
   // This prevents non-const `char[]` arrays from looking like literals.
   template <int SIZE>
   LogMessage& operator<<(char (&buf)[SIZE]) ABSL_ATTRIBUTE_NOINLINE;
+  // `wchar_t[SIZE]` is handled by `operator<< <const wchar_t*>`.
 
   // Types that support `AbslStringify()` are serialized that way.
   // Types that don't support `AbslStringify()` but do support streaming into a
@@ -243,6 +256,8 @@
   void CopyToEncodedBuffer(absl::string_view str) ABSL_ATTRIBUTE_NOINLINE;
   template <StringType str_type>
   void CopyToEncodedBuffer(char ch, size_t num) ABSL_ATTRIBUTE_NOINLINE;
+  template <StringType str_type>
+  void CopyToEncodedBuffer(std::wstring_view str) ABSL_ATTRIBUTE_NOINLINE;
 
   // Copies `field` to the encoded buffer, then appends `str` after it
   // (truncating `str` if necessary to fit).
@@ -273,6 +288,22 @@
   absl_nonnull std::unique_ptr<LogMessageData> data_;
 };
 
+// Explicitly specializes the generic operator<< for `const wchar_t*`
+// arguments.
+//
+// This method is used instead of a non-template `const wchar_t*` overload,
+// as the latter was found to take precedence over the array template
+// (`operator<<(const wchar_t(&)[SIZE])`) when handling string literals.
+// This specialization ensures the array template now correctly processes
+// literals.
+template <>
+LogMessage& LogMessage::operator<< <const wchar_t*>(
+    const wchar_t* absl_nullable const& v);
+
+inline LogMessage& LogMessage::operator<<(wchar_t* absl_nullable v) {
+  return operator<<(const_cast<const wchar_t*>(v));
+}
+
 // Helper class so that `AbslStringify()` can modify the LogMessage.
 class StringifySink final {
  public:
@@ -317,6 +348,12 @@
   return *this;
 }
 
+template <int SIZE>
+LogMessage& LogMessage::operator<<(const wchar_t (&buf)[SIZE]) {
+  CopyToEncodedBuffer<StringType::kLiteral>(buf);
+  return *this;
+}
+
 // Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
 template <int SIZE>
 LogMessage& LogMessage::operator<<(char (&buf)[SIZE]) {
@@ -358,6 +395,10 @@
                                                                   size_t num);
 extern template void LogMessage::CopyToEncodedBuffer<
     LogMessage::StringType::kNotLiteral>(char ch, size_t num);
+extern template void LogMessage::CopyToEncodedBuffer<
+    LogMessage::StringType::kLiteral>(std::wstring_view str);
+extern template void LogMessage::CopyToEncodedBuffer<
+    LogMessage::StringType::kNotLiteral>(std::wstring_view str);
 
 // `LogMessageFatal` ensures the process will exit in failure after logging this
 // message.
diff --git a/third_party/abseil-cpp/absl/log/log_format_test.cc b/third_party/abseil-cpp/absl/log/log_format_test.cc
index ecd69683..f4e33c9 100644
--- a/third_party/abseil-cpp/absl/log/log_format_test.cc
+++ b/third_party/abseil-cpp/absl/log/log_format_test.cc
@@ -15,12 +15,14 @@
 
 #include <math.h>
 
+#include <cstring>
 #include <iomanip>
 #include <ios>
 #include <limits>
 #include <ostream>
 #include <sstream>
 #include <string>
+#include <string_view>
 #include <type_traits>
 
 #ifdef __ANDROID__
@@ -28,6 +30,7 @@
 #endif
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
+#include "absl/base/config.h"
 #include "absl/log/check.h"
 #include "absl/log/internal/test_matchers.h"
 #include "absl/log/log.h"
@@ -44,6 +47,7 @@
 using ::absl::log_internal::RawEncodedMessage;
 using ::absl::log_internal::TextMessage;
 using ::absl::log_internal::TextPrefix;
+using ::testing::_;
 using ::testing::AllOf;
 using ::testing::AnyOf;
 using ::testing::Each;
@@ -124,6 +128,33 @@
   LOG(INFO) << value;
 }
 
+TEST(WideCharLogFormatTest, Printable) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  EXPECT_CALL(test_sink, Send(AllOf(TextMessage(Eq("€")),
+                                    ENCODED_MESSAGE(HasValues(
+                                        ElementsAre(ValueWithStr(Eq("€"))))))));
+
+  test_sink.StartCapturingLogs();
+  const wchar_t value = L'\u20AC';
+  LOG(INFO) << value;
+}
+
+TEST(WideCharLogFormatTest, Unprintable) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  // Using NEL (Next Line) Unicode character (U+0085).
+  // It is encoded as "\xC2\x85" in UTF-8.
+  constexpr wchar_t wide_value = L'\u0085';
+  constexpr char value[] = "\xC2\x85";
+  EXPECT_CALL(test_sink, Send(AllOf(TextMessage(Eq(value)),
+                                    ENCODED_MESSAGE(HasValues(ElementsAre(
+                                        ValueWithStr(Eq(value))))))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << wide_value;
+}
+
 template <typename T>
 class UnsignedIntLogFormatTest : public testing::Test {};
 using UnsignedIntTypes = Types<unsigned short, unsigned int,        // NOLINT
@@ -635,7 +666,7 @@
 
 template <typename T>
 class VoidPtrLogFormatTest : public testing::Test {};
-using VoidPtrTypes = Types<void *, const void *>;
+using VoidPtrTypes = Types<void*, const void*>;
 TYPED_TEST_SUITE(VoidPtrLogFormatTest, VoidPtrTypes);
 
 TYPED_TEST(VoidPtrLogFormatTest, Null) {
@@ -676,11 +707,10 @@
 
 template <typename T>
 class VolatilePtrLogFormatTest : public testing::Test {};
-using VolatilePtrTypes =
-    Types<volatile void*, const volatile void*, volatile char*,
-          const volatile char*, volatile signed char*,
-          const volatile signed char*, volatile unsigned char*,
-          const volatile unsigned char*>;
+using VolatilePtrTypes = Types<
+    volatile void*, const volatile void*, volatile char*, const volatile char*,
+    volatile signed char*, const volatile signed char*, volatile unsigned char*,
+    const volatile unsigned char*, volatile wchar_t*, const volatile wchar_t*>;
 TYPED_TEST_SUITE(VolatilePtrLogFormatTest, VolatilePtrTypes);
 
 TYPED_TEST(VolatilePtrLogFormatTest, Null) {
@@ -784,6 +814,38 @@
   LOG(INFO) << value;
 }
 
+template <typename T>
+class WideCharPtrLogFormatTest : public testing::Test {};
+using WideCharPtrTypes = Types<wchar_t, const wchar_t>;
+TYPED_TEST_SUITE(WideCharPtrLogFormatTest, WideCharPtrTypes);
+
+TYPED_TEST(WideCharPtrLogFormatTest, Null) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  TypeParam* const value = nullptr;
+
+  EXPECT_CALL(test_sink, Send(AllOf(TextMessage(Eq("(null)")),
+                                    ENCODED_MESSAGE(HasValues(ElementsAre(
+                                        ValueWithStr(Eq("(null)"))))))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << value;
+}
+
+TYPED_TEST(WideCharPtrLogFormatTest, NonNull) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  TypeParam data[] = {'v', 'a', 'l', 'u', 'e', '\0'};
+  TypeParam* const value = data;
+
+  EXPECT_CALL(test_sink, Send(AllOf(TextMessage(Eq("value")),
+                                    ENCODED_MESSAGE(HasValues(ElementsAre(
+                                        ValueWithStr(Eq("value"))))))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << value;
+}
+
 TEST(BoolLogFormatTest, True) {
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
@@ -836,6 +898,17 @@
   LOG(INFO) << "value";
 }
 
+TEST(LogFormatTest, WideStringLiteral) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  EXPECT_CALL(test_sink, Send(AllOf(TextMessage(Eq("value")),
+                                    ENCODED_MESSAGE(HasValues(ElementsAre(
+                                        ValueWithLiteral(Eq("value"))))))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << L"value";
+}
+
 TEST(LogFormatTest, CharArray) {
   absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
 
@@ -854,6 +927,125 @@
   LOG(INFO) << value;
 }
 
+TEST(LogFormatTest, WideCharArray) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  wchar_t value[] = L"value";
+
+  EXPECT_CALL(test_sink, Send(AllOf(TextMessage(Eq("value")),
+                                    ENCODED_MESSAGE(HasValues(ElementsAre(
+                                        ValueWithStr(Eq("value"))))))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << value;
+}
+
+// Comprehensive test string for validating wchar_t to UTF-8 conversion.
+// See details in absl/strings/internal/utf8_test.cc.
+//
+// clang-format off
+#define ABSL_LOG_INTERNAL_WIDE_LITERAL L"Holá €1 你好 שָׁלוֹם 👍🏻🇺🇸👩‍❤️‍💋‍👨 中"
+#define ABSL_LOG_INTERNAL_UTF8_LITERAL u8"Holá €1 你好 שָׁלוֹם 👍🏻🇺🇸👩‍❤️‍💋‍👨 中"
+// clang-format on
+
+absl::string_view GetUtf8TestString() {
+  // `u8""` forces UTF-8 encoding; MSVC will default to e.g. CP1252 (and warn)
+  // without it. However, the resulting character type differs between pre-C++20
+  // (`char`) and C++20 (`char8_t`). So we reinterpret_cast to `char*` and wrap
+  // it in a `string_view`.
+  static const absl::string_view kUtf8TestString(
+      reinterpret_cast<const char*>(ABSL_LOG_INTERNAL_UTF8_LITERAL),
+      sizeof(ABSL_LOG_INTERNAL_UTF8_LITERAL) - 1);
+  return kUtf8TestString;
+}
+
+template <typename T>
+class WideStringLogFormatTest : public testing::Test {};
+using StringTypes =
+    Types<std::wstring, const std::wstring, wchar_t[], const wchar_t*>;
+TYPED_TEST_SUITE(WideStringLogFormatTest, StringTypes);
+
+TYPED_TEST(WideStringLogFormatTest, NonLiterals) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  TypeParam value = ABSL_LOG_INTERNAL_WIDE_LITERAL;
+  absl::string_view utf8_value = GetUtf8TestString();
+
+  EXPECT_CALL(test_sink, Send(AllOf(TextMessage(Eq(utf8_value)),
+                                    ENCODED_MESSAGE(HasValues(ElementsAre(
+                                        ValueWithStr(Eq(utf8_value))))))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << value;
+}
+
+TEST(WideStringLogFormatTest, StringView) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  std::wstring_view value = ABSL_LOG_INTERNAL_WIDE_LITERAL;
+  absl::string_view utf8_value = GetUtf8TestString();
+
+  EXPECT_CALL(test_sink, Send(AllOf(TextMessage(Eq(utf8_value)),
+                                    ENCODED_MESSAGE(HasValues(ElementsAre(
+                                        ValueWithStr(Eq(utf8_value))))))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << value;
+}
+
+TEST(WideStringLogFormatTest, Literal) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  absl::string_view utf8_value = GetUtf8TestString();
+
+  EXPECT_CALL(test_sink, Send(AllOf(TextMessage(Eq(utf8_value)),
+                                    ENCODED_MESSAGE(HasValues(ElementsAre(
+                                        ValueWithLiteral(Eq(utf8_value))))))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << ABSL_LOG_INTERNAL_WIDE_LITERAL;
+}
+
+#undef ABSL_LOG_INTERNAL_WIDE_LITERAL
+#undef ABSL_LOG_INTERNAL_UTF8_LITERAL
+
+TYPED_TEST(WideStringLogFormatTest, InvalidCharactersAreReplaced) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  TypeParam value = L"AAA \xDC00 BBB";
+  // NOLINTNEXTLINE(readability/utf8)
+  absl::string_view utf8_value = "AAA � BBB";
+
+  EXPECT_CALL(test_sink, Send(AllOf(TextMessage(Eq(utf8_value)),
+                                    ENCODED_MESSAGE(HasValues(ElementsAre(
+                                        ValueWithStr(Eq(utf8_value))))))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << value;
+}
+
+TYPED_TEST(WideStringLogFormatTest, EmptyWideString) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  TypeParam value = L"";
+
+  EXPECT_CALL(test_sink, Send(AllOf(TextMessage(Eq("")),
+                                    ENCODED_MESSAGE(HasValues(
+                                        ElementsAre(ValueWithStr(Eq(""))))))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << value;
+}
+
+TEST(WideStringLogFormatTest, MixedNarrowAndWideStrings) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  EXPECT_CALL(test_sink, Log(_, _, "1234"));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << "1" << L"2" << "3" << L"4";
+}
+
 class CustomClass {};
 std::ostream& operator<<(std::ostream& os, const CustomClass&) {
   return os << "CustomClass{}";
@@ -1675,6 +1867,29 @@
   LOG(INFO) << std::string(2 * absl::log_internal::kLogMessageBufferSize, 'x');
 }
 
+TEST(StructuredLoggingOverflowTest, TruncatesWideStrings) {
+  absl::ScopedMockLog test_sink(absl::MockLogDefault::kDisallowUnexpected);
+
+  // This message is too long and should be truncated to some unspecified size
+  // no greater than the buffer size but not too much less either.  It should be
+  // truncated rather than discarded.
+  EXPECT_CALL(
+      test_sink,
+      Send(AllOf(
+          TextMessage(AllOf(
+              SizeIs(AllOf(Ge(absl::log_internal::kLogMessageBufferSize - 256),
+                           Le(absl::log_internal::kLogMessageBufferSize))),
+              Each(Eq('x')))),
+          ENCODED_MESSAGE(HasOneStrThat(AllOf(
+              SizeIs(AllOf(Ge(absl::log_internal::kLogMessageBufferSize - 256),
+                           Le(absl::log_internal::kLogMessageBufferSize))),
+              Each(Eq('x'))))))));
+
+  test_sink.StartCapturingLogs();
+  LOG(INFO) << std::wstring(2 * absl::log_internal::kLogMessageBufferSize,
+                            L'x');
+}
+
 struct StringLike {
   absl::string_view data;
 };
diff --git a/third_party/abseil-cpp/absl/meta/type_traits.h b/third_party/abseil-cpp/absl/meta/type_traits.h
index 5e57a15..ba57e52 100644
--- a/third_party/abseil-cpp/absl/meta/type_traits.h
+++ b/third_party/abseil-cpp/absl/meta/type_traits.h
@@ -38,6 +38,7 @@
 #include <cstddef>
 #include <functional>
 #include <string>
+#include <string_view>
 #include <type_traits>
 #include <vector>
 
@@ -48,10 +49,6 @@
 #include <span>  // NOLINT(build/c++20)
 #endif
 
-#ifdef ABSL_HAVE_STD_STRING_VIEW
-#include <string_view>
-#endif
-
 // Defines the default alignment. `__STDCPP_DEFAULT_NEW_ALIGNMENT__` is a C++17
 // feature.
 #if defined(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
@@ -507,10 +504,8 @@
 struct IsView : std::integral_constant<bool, std::is_pointer<T>::value ||
                                                  IsViewImpl<T>::value> {};
 
-#ifdef ABSL_HAVE_STD_STRING_VIEW
 template <typename Char, typename Traits>
 struct IsView<std::basic_string_view<Char, Traits>> : std::true_type {};
-#endif
 
 #ifdef __cpp_lib_span
 template <typename T>
diff --git a/third_party/abseil-cpp/absl/meta/type_traits_test.cc b/third_party/abseil-cpp/absl/meta/type_traits_test.cc
index bcf90d73..7c2dbbcf 100644
--- a/third_party/abseil-cpp/absl/meta/type_traits_test.cc
+++ b/third_party/abseil-cpp/absl/meta/type_traits_test.cc
@@ -16,6 +16,7 @@
 
 #include <cstdint>
 #include <string>
+#include <string_view>
 #include <type_traits>
 #include <utility>
 #include <vector>
@@ -26,10 +27,6 @@
 #include "absl/time/clock.h"
 #include "absl/time/time.h"
 
-#ifdef ABSL_HAVE_STD_STRING_VIEW
-#include <string_view>
-#endif
-
 namespace {
 
 using ::testing::StaticAssertTypeEq;
@@ -45,12 +42,10 @@
               "string is an owner, not a view");
 static_assert(IsOwnerAndNotView<std::wstring>::value,
               "wstring is an owner, not a view");
-#ifdef ABSL_HAVE_STD_STRING_VIEW
 static_assert(!IsOwnerAndNotView<std::string_view>::value,
               "string_view is a view, not an owner");
 static_assert(!IsOwnerAndNotView<std::wstring_view>::value,
               "wstring_view is a view, not an owner");
-#endif
 
 template <class T, class U>
 struct simple_pair {
diff --git a/third_party/abseil-cpp/absl/status/internal/statusor_internal.h b/third_party/abseil-cpp/absl/status/internal/statusor_internal.h
index ca7c550..e9866113 100644
--- a/third_party/abseil-cpp/absl/status/internal/statusor_internal.h
+++ b/third_party/abseil-cpp/absl/status/internal/statusor_internal.h
@@ -39,7 +39,8 @@
 struct HasConversionOperatorToStatusOr : std::false_type {};
 
 template <typename T, typename U>
-void test(char (*)[sizeof(std::declval<U>().operator absl::StatusOr<T>())]);
+void test(char (*absl_nullable)[sizeof(
+    std::declval<U>().operator absl::StatusOr<T>())]);
 
 template <typename T, typename U>
 struct HasConversionOperatorToStatusOr<T, U, decltype(test<T, U>(0))>
diff --git a/third_party/abseil-cpp/absl/status/statusor.h b/third_party/abseil-cpp/absl/status/statusor.h
index 5257af0..6142a2f 100644
--- a/third_party/abseil-cpp/absl/status/statusor.h
+++ b/third_party/abseil-cpp/absl/status/statusor.h
@@ -520,8 +520,8 @@
   // REQUIRES: `this->ok() == true`, otherwise the behavior is undefined.
   //
   // Use `this->ok()` to verify that there is a current value.
-  const T* operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
-  T* operator->() ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  const T* absl_nonnull operator->() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
+  T* absl_nonnull operator->() ABSL_ATTRIBUTE_LIFETIME_BOUND;
 
   // StatusOr<T>::value_or()
   //
diff --git a/third_party/abseil-cpp/absl/strings/cordz_test_helpers.h b/third_party/abseil-cpp/absl/strings/cordz_test_helpers.h
index 98117099..66232db 100644
--- a/third_party/abseil-cpp/absl/strings/cordz_test_helpers.h
+++ b/third_party/abseil-cpp/absl/strings/cordz_test_helpers.h
@@ -34,16 +34,15 @@
 ABSL_NAMESPACE_BEGIN
 
 // Returns the CordzInfo for the cord, or nullptr if the cord is not sampled.
-inline const cord_internal::CordzInfo* absl_nullable GetCordzInfoForTesting(
+inline const cord_internal::CordzInfo* GetCordzInfoForTesting(
     const Cord& cord) {
   if (!cord.contents_.is_tree()) return nullptr;
   return cord.contents_.cordz_info();
 }
 
 // Returns true if the provided cordz_info is in the list of sampled cords.
-inline bool CordzInfoIsListed(
-    const cord_internal::CordzInfo* absl_nonnull cordz_info,
-    cord_internal::CordzSampleToken token = {}) {
+inline bool CordzInfoIsListed(const cord_internal::CordzInfo* cordz_info,
+                              cord_internal::CordzSampleToken token = {}) {
   for (const cord_internal::CordzInfo& info : token) {
     if (cordz_info == &info) return true;
   }
@@ -121,7 +120,7 @@
 
 // Wrapper struct managing a small CordRep `rep`
 struct TestCordRep {
-  cord_internal::CordRepFlat* absl_nonnull rep;
+  cord_internal::CordRepFlat* rep;
 
   TestCordRep() {
     rep = cord_internal::CordRepFlat::New(100);
diff --git a/third_party/abseil-cpp/absl/strings/internal/cord_internal.h b/third_party/abseil-cpp/absl/strings/internal/cord_internal.h
index b55b412..cf1f703 100644
--- a/third_party/abseil-cpp/absl/strings/internal/cord_internal.h
+++ b/third_party/abseil-cpp/absl/strings/internal/cord_internal.h
@@ -635,7 +635,7 @@
     poison();
   }
 
-  void CopyInlineToString(std::string* absl_nonnull dst) const {
+  void CopyInlineToString(std::string* dst) const {
     assert(!is_tree());
     // As Cord can store only 15 bytes it is smaller than std::string's
     // small string optimization buffer size. Therefore we will always trigger
diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc
index 103c85d1..01e4e42 100644
--- a/third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc
+++ b/third_party/abseil-cpp/absl/strings/internal/str_format/arg.cc
@@ -26,6 +26,7 @@
 #include <cstring>
 #include <cwchar>
 #include <string>
+#include <string_view>
 #include <type_traits>
 
 #include "absl/base/config.h"
@@ -38,10 +39,6 @@
 #include "absl/strings/numbers.h"
 #include "absl/strings/string_view.h"
 
-#if defined(ABSL_HAVE_STD_STRING_VIEW)
-#include <string_view>
-#endif
-
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace str_format_internal {
@@ -459,13 +456,11 @@
   return {ConvertStringArg(v, conv, sink)};
 }
 
-#if defined(ABSL_HAVE_STD_STRING_VIEW)
 StringConvertResult FormatConvertImpl(std::wstring_view v,
                                       const FormatConversionSpecImpl conv,
                                       FormatSinkImpl* sink) {
   return {ConvertStringArg(v.data(), v.size(), conv, sink)};
 }
-#endif
 
 StringPtrConvertResult FormatConvertImpl(const char* v,
                                          const FormatConversionSpecImpl conv,
diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/arg.h b/third_party/abseil-cpp/absl/strings/internal/str_format/arg.h
index 309161d..021013f 100644
--- a/third_party/abseil-cpp/absl/strings/internal/str_format/arg.h
+++ b/third_party/abseil-cpp/absl/strings/internal/str_format/arg.h
@@ -26,6 +26,7 @@
 #include <memory>
 #include <sstream>
 #include <string>
+#include <string_view>
 #include <type_traits>
 #include <utility>
 
@@ -37,10 +38,6 @@
 #include "absl/strings/internal/str_format/extension.h"
 #include "absl/strings/string_view.h"
 
-#if defined(ABSL_HAVE_STD_STRING_VIEW)
-#include <string_view>
-#endif
-
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 
@@ -228,7 +225,6 @@
 StringConvertResult FormatConvertImpl(string_view v,
                                       FormatConversionSpecImpl conv,
                                       FormatSinkImpl* sink);
-#if defined(ABSL_HAVE_STD_STRING_VIEW)
 StringConvertResult FormatConvertImpl(std::wstring_view v,
                                       FormatConversionSpecImpl conv,
                                       FormatSinkImpl* sink);
@@ -239,7 +235,6 @@
   return FormatConvertImpl(absl::string_view(v.data(), v.size()), conv, sink);
 }
 #endif  // !ABSL_USES_STD_STRING_VIEW
-#endif  // ABSL_HAVE_STD_STRING_VIEW
 
 using StringPtrConvertResult = ArgConvertResult<FormatConversionCharSetUnion(
     FormatConversionCharSetInternal::s,
@@ -651,15 +646,10 @@
   ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(const wchar_t*, __VA_ARGS__);     \
   ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::wstring, __VA_ARGS__)
 
-#if defined(ABSL_HAVE_STD_STRING_VIEW)
 #define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...)       \
   ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_( \
       __VA_ARGS__);                                                \
   ABSL_INTERNAL_FORMAT_DISPATCH_INSTANTIATE_(std::wstring_view, __VA_ARGS__)
-#else
-#define ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(...) \
-  ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_NO_WSTRING_VIEW_(__VA_ARGS__)
-#endif
 
 ABSL_INTERNAL_FORMAT_DISPATCH_OVERLOADS_EXPAND_(extern);
 
diff --git a/third_party/abseil-cpp/absl/strings/internal/str_format/convert_test.cc b/third_party/abseil-cpp/absl/strings/internal/str_format/convert_test.cc
index baffe05..e4fa44e 100644
--- a/third_party/abseil-cpp/absl/strings/internal/str_format/convert_test.cc
+++ b/third_party/abseil-cpp/absl/strings/internal/str_format/convert_test.cc
@@ -27,6 +27,7 @@
 #include <set>
 #include <sstream>
 #include <string>
+#include <string_view>
 #include <thread>  // NOLINT
 #include <type_traits>
 #include <vector>
@@ -46,10 +47,6 @@
 #include "absl/types/optional.h"
 #include "absl/types/span.h"
 
-#if defined(ABSL_HAVE_STD_STRING_VIEW)
-#include <string_view>
-#endif
-
 namespace absl {
 ABSL_NAMESPACE_BEGIN
 namespace str_format_internal {
@@ -322,10 +319,8 @@
   TestStringConvert(std::string("hello"));
   TestStringConvert(std::wstring(L"hello"));
   TestStringConvert(string_view("hello"));
-#if defined(ABSL_HAVE_STD_STRING_VIEW)
   TestStringConvert(std::string_view("hello"));
   TestStringConvert(std::wstring_view(L"hello"));
-#endif  // ABSL_HAVE_STD_STRING_VIEW
 }
 
 TEST_F(FormatConvertTest, NullString) {
diff --git a/third_party/abseil-cpp/absl/strings/str_cat.h b/third_party/abseil-cpp/absl/strings/str_cat.h
index eafd8a3f..84db0f6 100644
--- a/third_party/abseil-cpp/absl/strings/str_cat.h
+++ b/third_party/abseil-cpp/absl/strings/str_cat.h
@@ -111,7 +111,7 @@
 #include "absl/strings/numbers.h"
 #include "absl/strings/string_view.h"
 
-#if defined(ABSL_HAVE_STD_STRING_VIEW) && !defined(ABSL_USES_STD_STRING_VIEW)
+#if !defined(ABSL_USES_STD_STRING_VIEW)
 #include <string_view>
 #endif
 
@@ -191,26 +191,26 @@
   template <typename Int>
   explicit Hex(
       Int v, PadSpec spec = absl::kNoPad,
-      typename std::enable_if<sizeof(Int) == 1 &&
-                              !std::is_pointer<Int>::value>::type* = nullptr)
+      std::enable_if_t<sizeof(Int) == 1 && !std::is_pointer<Int>::value, bool> =
+          true)
       : Hex(spec, static_cast<uint8_t>(v)) {}
   template <typename Int>
   explicit Hex(
       Int v, PadSpec spec = absl::kNoPad,
-      typename std::enable_if<sizeof(Int) == 2 &&
-                              !std::is_pointer<Int>::value>::type* = nullptr)
+      std::enable_if_t<sizeof(Int) == 2 && !std::is_pointer<Int>::value, bool> =
+          true)
       : Hex(spec, static_cast<uint16_t>(v)) {}
   template <typename Int>
   explicit Hex(
       Int v, PadSpec spec = absl::kNoPad,
-      typename std::enable_if<sizeof(Int) == 4 &&
-                              !std::is_pointer<Int>::value>::type* = nullptr)
+      std::enable_if_t<sizeof(Int) == 4 && !std::is_pointer<Int>::value, bool> =
+          true)
       : Hex(spec, static_cast<uint32_t>(v)) {}
   template <typename Int>
   explicit Hex(
       Int v, PadSpec spec = absl::kNoPad,
-      typename std::enable_if<sizeof(Int) == 8 &&
-                              !std::is_pointer<Int>::value>::type* = nullptr)
+      std::enable_if_t<sizeof(Int) == 8 && !std::is_pointer<Int>::value, bool> =
+          true)
       : Hex(spec, static_cast<uint64_t>(v)) {}
   template <typename Pointee>
   explicit Hex(Pointee* absl_nullable v, PadSpec spec = absl::kNoPad)
@@ -262,7 +262,7 @@
 
   template <typename Int>
   explicit Dec(Int v, PadSpec spec = absl::kNoPad,
-               typename std::enable_if<(sizeof(Int) <= 8)>::type* = nullptr)
+               std::enable_if_t<sizeof(Int) <= 8, bool> = true)
       : value(v >= 0 ? static_cast<uint64_t>(v)
                      : uint64_t{0} - static_cast<uint64_t>(v)),
         width(spec == absl::kNoPad       ? 1
@@ -366,7 +366,7 @@
                ABSL_ATTRIBUTE_LIFETIME_BOUND)
       : piece_(pc) {}
 
-#if defined(ABSL_HAVE_STD_STRING_VIEW) && !defined(ABSL_USES_STD_STRING_VIEW)
+#if !defined(ABSL_USES_STD_STRING_VIEW)
   AlphaNum(std::string_view pc  // NOLINT(runtime/explicit)
                ABSL_ATTRIBUTE_LIFETIME_BOUND)
       : piece_(pc.data(), pc.size()) {}
diff --git a/third_party/abseil-cpp/absl/strings/str_cat_test.cc b/third_party/abseil-cpp/absl/strings/str_cat_test.cc
index 4de379eb5..a3bd42c 100644
--- a/third_party/abseil-cpp/absl/strings/str_cat_test.cc
+++ b/third_party/abseil-cpp/absl/strings/str_cat_test.cc
@@ -21,6 +21,7 @@
 #include <cstdlib>
 #include <limits>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include "gtest/gtest.h"
@@ -28,10 +29,6 @@
 #include "absl/strings/str_format.h"
 #include "absl/strings/string_view.h"
 
-#if defined(ABSL_HAVE_STD_STRING_VIEW)
-#include <string_view>
-#endif
-
 #ifdef __ANDROID__
 // Android assert messages only go to system log, so death tests cannot inspect
 // the message for matching.
@@ -219,13 +216,11 @@
   EXPECT_EQ(result, "");
 }
 
-#if defined(ABSL_HAVE_STD_STRING_VIEW)
 TEST(StrCat, StdStringView) {
   std::string_view pieces[] = {"Hello", ", ", "World", "!"};
   EXPECT_EQ(absl::StrCat(pieces[0], pieces[1], pieces[2], pieces[3]),
                          "Hello, World!");
 }
-#endif  // ABSL_HAVE_STD_STRING_VIEW
 
 TEST(StrCat, NullConstCharPtr) {
   const char* null = nullptr;
diff --git a/third_party/abseil-cpp/absl/strings/string_view_test.cc b/third_party/abseil-cpp/absl/strings/string_view_test.cc
index 7064cc7..0a2a7a97 100644
--- a/third_party/abseil-cpp/absl/strings/string_view_test.cc
+++ b/third_party/abseil-cpp/absl/strings/string_view_test.cc
@@ -34,7 +34,7 @@
 #include "absl/base/config.h"
 #include "absl/meta/type_traits.h"
 
-#if defined(ABSL_HAVE_STD_STRING_VIEW) || defined(__ANDROID__)
+#if defined(ABSL_USES_STD_STRING_VIEW) || defined(__ANDROID__)
 // We don't control the death messaging when using std::string_view.
 // Android assert messages only go to system log, so death tests cannot inspect
 // the message for matching.
diff --git a/third_party/abseil-cpp/absl/strings/substitute.h b/third_party/abseil-cpp/absl/strings/substitute.h
index 08f64e9..c93b1cc 100644
--- a/third_party/abseil-cpp/absl/strings/substitute.h
+++ b/third_party/abseil-cpp/absl/strings/substitute.h
@@ -187,12 +187,13 @@
 
   // vector<bool>::reference and const_reference require special help to convert
   // to `Arg` because it requires two user defined conversions.
-  template <typename T,
-            absl::enable_if_t<
-                std::is_class<T>::value &&
-                (std::is_same<T, std::vector<bool>::reference>::value ||
-                 std::is_same<T, std::vector<bool>::const_reference>::value)>* =
-                nullptr>
+  template <
+      typename T,
+      std::enable_if_t<
+          std::is_class<T>::value &&
+              (std::is_same<T, std::vector<bool>::reference>::value ||
+               std::is_same<T, std::vector<bool>::const_reference>::value),
+          bool> = true>
   Arg(T value)  // NOLINT(google-explicit-constructor)
       : Arg(static_cast<bool>(value)) {}
 
@@ -237,7 +238,7 @@
                                           : (1 << (*format - '0'));
 }
 
-constexpr const char* SkipNumber(const char* absl_nonnull format) {
+constexpr const char* absl_nonnull SkipNumber(const char* absl_nonnull format) {
   return !*format ? format : (format + 1);
 }
 
diff --git a/third_party/abseil-cpp/absl/types/span.h b/third_party/abseil-cpp/absl/types/span.h
index 444b2ae6..39e6a8a5 100644
--- a/third_party/abseil-cpp/absl/types/span.h
+++ b/third_party/abseil-cpp/absl/types/span.h
@@ -202,10 +202,11 @@
  public:
   using element_type = T;
   using value_type = absl::remove_cv_t<T>;
-  // TODO(b/316099902) - pointer should be Nullable<T*>, but this makes it hard
-  // to recognize foreach loops as safe.
-  using pointer = T*;
-  using const_pointer = const T*;
+  // TODO(b/316099902) - pointer should be absl_nullable, but this makes it hard
+  // to recognize foreach loops as safe. absl_nullability_unknown is currently
+  // used to suppress -Wnullability-completeness warnings.
+  using pointer = T* absl_nullability_unknown;
+  using const_pointer = const T* absl_nullability_unknown;
   using reference = T&;
   using const_reference = const T&;
   using iterator = pointer;
diff --git a/third_party/abseil-cpp/ci/cmake_common.sh b/third_party/abseil-cpp/ci/cmake_common.sh
index 3e14ca3..484230cd 100644
--- a/third_party/abseil-cpp/ci/cmake_common.sh
+++ b/third_party/abseil-cpp/ci/cmake_common.sh
@@ -14,6 +14,6 @@
 
 # The commit of GoogleTest to be used in the CMake tests in this directory.
 # Keep this in sync with the commit in the MODULE.bazel file.
-readonly ABSL_GOOGLETEST_VERSION="1.16.0"
+readonly ABSL_GOOGLETEST_VERSION="1.17.0"
 
 readonly ABSL_GOOGLETEST_DOWNLOAD_URL="https://github.com/google/googletest/releases/download/v${ABSL_GOOGLETEST_VERSION}/googletest-${ABSL_GOOGLETEST_VERSION}.tar.gz"
diff --git a/third_party/abseil-cpp/ci/linux_arm_clang-latest_libcxx_bazel.sh b/third_party/abseil-cpp/ci/linux_arm_clang-latest_libcxx_bazel.sh
index d9e5992..631a8bd 100755
--- a/third_party/abseil-cpp/ci/linux_arm_clang-latest_libcxx_bazel.sh
+++ b/third_party/abseil-cpp/ci/linux_arm_clang-latest_libcxx_bazel.sh
@@ -51,12 +51,12 @@
   BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
 fi
 
-# Avoid depending on external sites like GitHub by checking --distdir for
-# external dependencies first.
-# https://docs.bazel.build/versions/master/guide.html#distdir
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
-  BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
+# Use Bazel Vendor mode to reduce reliance on external dependencies.
+# See https://bazel.build/external/vendor and the Dockerfile for
+# an explaination of how this works.
+if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f "${KOKORO_GFILE_DIR}/distdir/abseil-cpp_vendor.tar.gz" ]]; then
+  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly --env=BAZEL_VENDOR_ARCHIVE=/distdir/abseil-cpp_vendor.tar.gz ${DOCKER_EXTRA_ARGS:-}"
+  BAZEL_EXTRA_ARGS="--vendor_dir=/abseil-cpp_vendor ${BAZEL_EXTRA_ARGS:-}"
 fi
 
 for std in ${STD}; do
@@ -71,13 +71,13 @@
         --rm \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
-        /bin/sh -c "
+        /bin/bash --login -c "
           cp -r /abseil-cpp-ro/* /abseil-cpp/
           if [ -n \"${ALTERNATE_OPTIONS:-}\" ]; then
             cp ${ALTERNATE_OPTIONS:-} absl/base/options.h || exit 1
           fi
           /usr/local/bin/bazel test ... \
-            --action_env=CC=clang-18 \
+            --action_env=CC=clang-19 \
             --compilation_mode=\"${compilation_mode}\" \
             --copt=\"${exceptions_mode}\" \
             --copt=\"-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1\" \
diff --git a/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_asan_bazel.sh b/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_asan_bazel.sh
index c83f3a0..cfc5510 100755
--- a/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_asan_bazel.sh
+++ b/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_asan_bazel.sh
@@ -73,32 +73,33 @@
         --rm \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
+        /bin/bash --login -c "
         /usr/local/bin/bazel test ... \
-          --action_env="CC=/opt/llvm/clang/bin/clang" \
-          --action_env="BAZEL_CXXOPTS=-std=${std}:-nostdinc++" \
-          --action_env="BAZEL_LINKOPTS=-L/opt/llvm/libcxx/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib" \
-          --action_env="CPLUS_INCLUDE_PATH=/opt/llvm/libcxx/include/c++/v1" \
-          --compilation_mode="${compilation_mode}" \
-          --copt="${exceptions_mode}" \
-          --copt="-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1" \
-          --copt="-fsanitize=address" \
-          --copt="-fsanitize=${UBSAN_CHECKS}" \
-          --copt="-fno-sanitize-recover=${UBSAN_CHECKS}" \
-          --copt="-fno-sanitize-blacklist" \
+          --action_env=\"CC=/opt/llvm/clang/bin/clang\" \
+          --action_env=\"BAZEL_CXXOPTS=-std=${std}:-nostdinc++\" \
+          --action_env=\"BAZEL_LINKOPTS=-L/opt/llvm/libcxx/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx/lib\" \
+          --action_env=\"CPLUS_INCLUDE_PATH=/opt/llvm/libcxx/include/c++/v1\" \
+          --compilation_mode=\"${compilation_mode}\" \
+          --copt=\"${exceptions_mode}\" \
+          --copt=\"-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1\" \
+          --copt=\"-fsanitize=address\" \
+          --copt=\"-fsanitize=${UBSAN_CHECKS}\" \
+          --copt=\"-fno-sanitize-recover=${UBSAN_CHECKS}\" \
+          --copt=\"-fno-sanitize-blacklist\" \
           --copt=-Werror \
           --enable_bzlmod=true \
           --features=external_include_paths \
           --keep_going \
-          --linkopt="-fsanitize=address" \
-          --linkopt="-fsanitize-link-c++-runtime" \
+          --linkopt=\"-fsanitize=address\" \
+          --linkopt=\"-fsanitize-link-c++-runtime\" \
           --show_timestamps \
-          --test_env="ASAN_SYMBOLIZER_PATH=/opt/llvm/clang/bin/llvm-symbolizer" \
-          --test_env="TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo" \
-          --test_env="UBSAN_OPTIONS=print_stacktrace=1" \
-          --test_env="UBSAN_SYMBOLIZER_PATH=/opt/llvm/clang/bin/llvm-symbolizer" \
+          --test_env=\"ASAN_SYMBOLIZER_PATH=/opt/llvm/clang/bin/llvm-symbolizer\" \
+          --test_env=\"TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo\" \
+          --test_env=\"UBSAN_OPTIONS=print_stacktrace=1\" \
+          --test_env=\"UBSAN_SYMBOLIZER_PATH=/opt/llvm/clang/bin/llvm-symbolizer\" \
           --test_output=errors \
-          --test_tag_filters="-benchmark,-noasan" \
-          ${BAZEL_EXTRA_ARGS:-}
+          --test_tag_filters=\"-benchmark,-noasan\" \
+          ${BAZEL_EXTRA_ARGS:-}"
     done
   done
 done
diff --git a/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_bazel.sh b/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_bazel.sh
index 832a9d8b..5c51d15 100755
--- a/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_bazel.sh
+++ b/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_bazel.sh
@@ -51,12 +51,12 @@
   BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
 fi
 
-# Avoid depending on external sites like GitHub by checking --distdir for
-# external dependencies first.
-# https://docs.bazel.build/versions/master/guide.html#distdir
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
-  BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
+# Use Bazel Vendor mode to reduce reliance on external dependencies.
+# See https://bazel.build/external/vendor and the Dockerfile for
+# an explaination of how this works.
+if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f "${KOKORO_GFILE_DIR}/distdir/abseil-cpp_vendor.tar.gz" ]]; then
+  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly --env=BAZEL_VENDOR_ARCHIVE=/distdir/abseil-cpp_vendor.tar.gz ${DOCKER_EXTRA_ARGS:-}"
+  BAZEL_EXTRA_ARGS="--vendor_dir=/abseil-cpp_vendor ${BAZEL_EXTRA_ARGS:-}"
 fi
 
 for std in ${STD}; do
@@ -71,7 +71,7 @@
         --rm \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
-        /bin/sh -c "
+        /bin/bash --login -c "
           cp -r /abseil-cpp-ro/* /abseil-cpp/
           if [ -n \"${ALTERNATE_OPTIONS:-}\" ]; then
             cp ${ALTERNATE_OPTIONS:-} absl/base/options.h || exit 1
diff --git a/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_tsan_bazel.sh b/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_tsan_bazel.sh
index 82b4dd1..c9ea22d 100755
--- a/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_tsan_bazel.sh
+++ b/third_party/abseil-cpp/ci/linux_clang-latest_libcxx_tsan_bazel.sh
@@ -51,12 +51,12 @@
   BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
 fi
 
-# Avoid depending on external sites like GitHub by checking --distdir for
-# external dependencies first.
-# https://docs.bazel.build/versions/master/guide.html#distdir
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
-  BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
+# Use Bazel Vendor mode to reduce reliance on external dependencies.
+# See https://bazel.build/external/vendor and the Dockerfile for
+# an explaination of how this works.
+if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f "${KOKORO_GFILE_DIR}/distdir/abseil-cpp_vendor.tar.gz" ]]; then
+  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly --env=BAZEL_VENDOR_ARCHIVE=/distdir/abseil-cpp_vendor.tar.gz ${DOCKER_EXTRA_ARGS:-}"
+  BAZEL_EXTRA_ARGS="--vendor_dir=/abseil-cpp_vendor ${BAZEL_EXTRA_ARGS:-}"
 fi
 
 for std in ${STD}; do
@@ -70,28 +70,29 @@
         --rm \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
+        /bin/bash --login -c "
         /usr/local/bin/bazel test ... \
-          --action_env="CC=/opt/llvm/clang/bin/clang" \
-          --action_env="BAZEL_CXXOPTS=-std=${std}:-nostdinc++" \
-          --action_env="BAZEL_LINKOPTS=-L/opt/llvm/libcxx-tsan/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx-tsan/lib" \
-          --action_env="CPLUS_INCLUDE_PATH=/opt/llvm/libcxx-tsan/include/c++/v1" \
-          --build_tag_filters="-notsan" \
-          --compilation_mode="${compilation_mode}" \
-          --copt="${exceptions_mode}" \
-          --copt="-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1" \
-          --copt="-fsanitize=thread" \
-          --copt="-fno-sanitize-blacklist" \
+          --action_env=\"CC=/opt/llvm/clang/bin/clang\" \
+          --action_env=\"BAZEL_CXXOPTS=-std=${std}:-nostdinc++\" \
+          --action_env=\"BAZEL_LINKOPTS=-L/opt/llvm/libcxx-tsan/lib:-lc++:-lc++abi:-lm:-Wl,-rpath=/opt/llvm/libcxx-tsan/lib\" \
+          --action_env=\"CPLUS_INCLUDE_PATH=/opt/llvm/libcxx-tsan/include/c++/v1\" \
+          --build_tag_filters=\"-notsan\" \
+          --compilation_mode=\"${compilation_mode}\" \
+          --copt=\"${exceptions_mode}\" \
+          --copt=\"-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1\" \
+          --copt=\"-fsanitize=thread\" \
+          --copt=\"-fno-sanitize-blacklist\" \
           --copt=-Werror \
           --enable_bzlmod=true \
           --features=external_include_paths \
           --keep_going \
-          --linkopt="-fsanitize=thread" \
+          --linkopt=\"-fsanitize=thread\" \
           --show_timestamps \
-          --test_env="TSAN_SYMBOLIZER_PATH=/opt/llvm/clang/bin/llvm-symbolizer" \
-          --test_env="TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo" \
+          --test_env=\"TSAN_SYMBOLIZER_PATH=/opt/llvm/clang/bin/llvm-symbolizer\" \
+          --test_env=\"TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo\" \
           --test_output=errors \
-          --test_tag_filters="-benchmark,-notsan" \
-          ${BAZEL_EXTRA_ARGS:-}
+          --test_tag_filters=\"-benchmark,-notsan\" \
+          ${BAZEL_EXTRA_ARGS:-}"
     done
   done
 done
diff --git a/third_party/abseil-cpp/ci/linux_clang-latest_libstdcxx_bazel.sh b/third_party/abseil-cpp/ci/linux_clang-latest_libstdcxx_bazel.sh
index 06aef62..a1620e0 100755
--- a/third_party/abseil-cpp/ci/linux_clang-latest_libstdcxx_bazel.sh
+++ b/third_party/abseil-cpp/ci/linux_clang-latest_libstdcxx_bazel.sh
@@ -51,12 +51,12 @@
   BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
 fi
 
-# Avoid depending on external sites like GitHub by checking --distdir for
-# external dependencies first.
-# https://docs.bazel.build/versions/master/guide.html#distdir
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
-  BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
+# Use Bazel Vendor mode to reduce reliance on external dependencies.
+# See https://bazel.build/external/vendor and the Dockerfile for
+# an explaination of how this works.
+if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f "${KOKORO_GFILE_DIR}/distdir/abseil-cpp_vendor.tar.gz" ]]; then
+  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly --env=BAZEL_VENDOR_ARCHIVE=/distdir/abseil-cpp_vendor.tar.gz ${DOCKER_EXTRA_ARGS:-}"
+  BAZEL_EXTRA_ARGS="--vendor_dir=/abseil-cpp_vendor ${BAZEL_EXTRA_ARGS:-}"
 fi
 
 for std in ${STD}; do
@@ -70,26 +70,27 @@
         --rm \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
+        /bin/bash --login -c "
         /usr/local/bin/bazel test ... \
-          --action_env="CC=/opt/llvm/clang/bin/clang" \
-          --action_env="BAZEL_CXXOPTS=-std=${std}" \
-          --compilation_mode="${compilation_mode}" \
-          --copt="--gcc-toolchain=/usr/local" \
-          --copt="-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1" \
-          --copt="${exceptions_mode}" \
-          --copt="-march=haswell" \
+          --action_env=\"CC=/opt/llvm/clang/bin/clang\" \
+          --action_env=\"BAZEL_CXXOPTS=-std=${std}\" \
+          --compilation_mode=\"${compilation_mode}\" \
+          --copt=\"--gcc-toolchain=/usr/local\" \
+          --copt=\"-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1\" \
+          --copt=\"${exceptions_mode}\" \
+          --copt=\"-march=haswell\" \
           --copt=-Werror \
-          --define="absl=1" \
+          --define=\"absl=1\" \
           --enable_bzlmod=true \
           --features=external_include_paths \
           --keep_going \
-          --linkopt="--gcc-toolchain=/usr/local" \
+          --linkopt=\"--gcc-toolchain=/usr/local\" \
           --show_timestamps \
-          --test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" \
-          --test_env="TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo" \
+          --test_env=\"GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1\" \
+          --test_env=\"TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo\" \
           --test_output=errors \
           --test_tag_filters=-benchmark \
-          ${BAZEL_EXTRA_ARGS:-}
+          ${BAZEL_EXTRA_ARGS:-}"
     done
   done
 done
diff --git a/third_party/abseil-cpp/ci/linux_docker_containers.sh b/third_party/abseil-cpp/ci/linux_docker_containers.sh
index 3f824a8..0f454716 100644
--- a/third_party/abseil-cpp/ci/linux_docker_containers.sh
+++ b/third_party/abseil-cpp/ci/linux_docker_containers.sh
@@ -16,7 +16,7 @@
 # Test scripts should source this file to get the identifiers.
 
 readonly LINUX_ALPINE_CONTAINER="gcr.io/google.com/absl-177019/alpine:20230612"
-readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20241218"
-readonly LINUX_ARM_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_arm_hybrid-latest:20250224"
-readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20241218"
-readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20250205"
+readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20250430"
+readonly LINUX_ARM_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_arm_hybrid-latest:20250430"
+readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20250430"
+readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20250430"
diff --git a/third_party/abseil-cpp/ci/linux_gcc-floor_libstdcxx_bazel.sh b/third_party/abseil-cpp/ci/linux_gcc-floor_libstdcxx_bazel.sh
index 74d996ab5..b683b60 100755
--- a/third_party/abseil-cpp/ci/linux_gcc-floor_libstdcxx_bazel.sh
+++ b/third_party/abseil-cpp/ci/linux_gcc-floor_libstdcxx_bazel.sh
@@ -51,12 +51,12 @@
   BAZEL_EXTRA_ARGS="--remote_http_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
 fi
 
-# Avoid depending on external sites like GitHub by checking --distdir for
-# external dependencies first.
-# https://docs.bazel.build/versions/master/guide.html#distdir
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
-  DOCKER_EXTRA_ARGS="--volume=${KOKORO_GFILE_DIR}/distdir:/distdir:ro ${DOCKER_EXTRA_ARGS:-}"
-  BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
+# Use Bazel Vendor mode to reduce reliance on external dependencies.
+# See https://bazel.build/external/vendor and the Dockerfile for
+# an explaination of how this works.
+if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f "${KOKORO_GFILE_DIR}/distdir/abseil-cpp_vendor.tar.gz" ]]; then
+  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly --env=BAZEL_VENDOR_ARCHIVE=/distdir/abseil-cpp_vendor.tar.gz ${DOCKER_EXTRA_ARGS:-}"
+  BAZEL_EXTRA_ARGS="--vendor_dir=/abseil-cpp_vendor ${BAZEL_EXTRA_ARGS:-}"
 fi
 
 for std in ${STD}; do
@@ -70,22 +70,23 @@
         --rm \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
+        /bin/bash --login -c "
         /usr/local/bin/bazel test ... \
-          --action_env="CC=/usr/local/bin/gcc" \
-          --action_env="BAZEL_CXXOPTS=-std=${std}" \
-          --compilation_mode="${compilation_mode}" \
-          --copt="${exceptions_mode}" \
-          --copt="-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1" \
+          --action_env=\"CC=/usr/local/bin/gcc\" \
+          --action_env=\"BAZEL_CXXOPTS=-std=${std}\" \
+          --compilation_mode=\"${compilation_mode}\" \
+          --copt=\"${exceptions_mode}\" \
+          --copt=\"-DGTEST_REMOVE_LEGACY_TEST_CASEAPI_=1\" \
           --copt=-Werror \
-          --define="absl=1" \
+          --define=\"absl=1\" \
           --features=external_include_paths \
           --keep_going \
           --show_timestamps \
-          --test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" \
-          --test_env="TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo" \
+          --test_env=\"GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1\" \
+          --test_env=\"TZDIR=/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo\" \
           --test_output=errors \
           --test_tag_filters=-benchmark \
-          ${BAZEL_EXTRA_ARGS:-}
+          ${BAZEL_EXTRA_ARGS:-}"
     done
   done
 done
diff --git a/third_party/abseil-cpp/ci/linux_gcc-latest_libstdcxx_bazel.sh b/third_party/abseil-cpp/ci/linux_gcc-latest_libstdcxx_bazel.sh
index 2daa132..b092c1d 100755
--- a/third_party/abseil-cpp/ci/linux_gcc-latest_libstdcxx_bazel.sh
+++ b/third_party/abseil-cpp/ci/linux_gcc-latest_libstdcxx_bazel.sh
@@ -51,12 +51,12 @@
   BAZEL_EXTRA_ARGS="--remote_cache=https://storage.googleapis.com/absl-bazel-remote-cache/${container_key} --google_credentials=/keystore/73103_absl-bazel-remote-cache ${BAZEL_EXTRA_ARGS:-}"
 fi
 
-# Avoid depending on external sites like GitHub by checking --distdir for
-# external dependencies first.
-# https://docs.bazel.build/versions/master/guide.html#distdir
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
-  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly ${DOCKER_EXTRA_ARGS:-}"
-  BAZEL_EXTRA_ARGS="--distdir=/distdir ${BAZEL_EXTRA_ARGS:-}"
+# Use Bazel Vendor mode to reduce reliance on external dependencies.
+# See https://bazel.build/external/vendor and the Dockerfile for
+# an explaination of how this works.
+if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f "${KOKORO_GFILE_DIR}/distdir/abseil-cpp_vendor.tar.gz" ]]; then
+  DOCKER_EXTRA_ARGS="--mount type=bind,source=${KOKORO_GFILE_DIR}/distdir,target=/distdir,readonly --env=BAZEL_VENDOR_ARCHIVE=/distdir/abseil-cpp_vendor.tar.gz ${DOCKER_EXTRA_ARGS:-}"
+  BAZEL_EXTRA_ARGS="--vendor_dir=/abseil-cpp_vendor ${BAZEL_EXTRA_ARGS:-}"
 fi
 
 for std in ${STD}; do
@@ -71,7 +71,7 @@
         --rm \
         ${DOCKER_EXTRA_ARGS:-} \
         ${DOCKER_CONTAINER} \
-        /bin/sh -c "
+        /bin/bash --login -c "
           cp -r /abseil-cpp-ro/* /abseil-cpp/
           if [ -n \"${ALTERNATE_OPTIONS:-}\" ]; then
             cp ${ALTERNATE_OPTIONS:-} absl/base/options.h || exit 1
diff --git a/third_party/abseil-cpp/ci/macos_xcode_bazel.sh b/third_party/abseil-cpp/ci/macos_xcode_bazel.sh
index 51ffde8d..b05cfac 100755
--- a/third_party/abseil-cpp/ci/macos_xcode_bazel.sh
+++ b/third_party/abseil-cpp/ci/macos_xcode_bazel.sh
@@ -27,7 +27,7 @@
 fi
 
 # If we are running on Kokoro, check for a versioned Bazel binary.
-KOKORO_GFILE_BAZEL_BIN="bazel-8.0.0-darwin-x86_64"
+KOKORO_GFILE_BAZEL_BIN="bazel-8.2.1-darwin-x86_64"
 if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]]; then
   BAZEL_BIN="${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN}"
   chmod +x ${BAZEL_BIN}
@@ -35,11 +35,10 @@
   BAZEL_BIN="bazel"
 fi
 
-# Avoid depending on external sites like GitHub by checking --distdir for
-# external dependencies first.
-# https://docs.bazel.build/versions/master/guide.html#distdir
-if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -d "${KOKORO_GFILE_DIR}/distdir" ]]; then
-  BAZEL_EXTRA_ARGS="--distdir=${KOKORO_GFILE_DIR}/distdir ${BAZEL_EXTRA_ARGS:-}"
+# Use Bazel Vendor mode to reduce reliance on external dependencies.
+if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f "${KOKORO_GFILE_DIR}/distdir/abseil-cpp_vendor.tar.gz" ]]; then
+  tar -xf "${KOKORO_GFILE_DIR}/distdir/abseil-cpp_vendor.tar.gz" -C "${TMP}/"
+  BAZEL_EXTRA_ARGS="--vendor_dir=\"${TMP}/abseil-cpp_vendor\" ${BAZEL_EXTRA_ARGS:-}"
 fi
 
 # Print the compiler and Bazel versions.
diff --git a/third_party/abseil-cpp/ci/windows_clangcl_bazel.bat b/third_party/abseil-cpp/ci/windows_clangcl_bazel.bat
index f9512ef..26fd5af 100755
--- a/third_party/abseil-cpp/ci/windows_clangcl_bazel.bat
+++ b/third_party/abseil-cpp/ci/windows_clangcl_bazel.bat
@@ -21,6 +21,14 @@
 CD %~dp0\..
 if %errorlevel% neq 0 EXIT /B 1
 
+:: Use Bazel Vendor mode to reduce reliance on external dependencies.
+IF EXIST "%KOKORO_GFILE_DIR%\distdir\abseil-cpp_vendor.tar.gz" (
+  tar --force-local -xf "%KOKORO_GFILE_DIR%\distdir\abseil-cpp_vendor.tar.gz" -C c:\
+  SET VENDOR_FLAG=--vendor_dir=c:\abseil-cpp_vendor
+) ELSE (
+  SET VENDOR_FLAG=
+)
+
 :: Set the standard version, [c++17|c++20|c++latest]
 :: https://msdn.microsoft.com/en-us/library/mt490614.aspx
 :: The default is c++17 if not set on command line.
@@ -39,7 +47,7 @@
 :: /google/data/rw/teams/absl/kokoro/windows.
 ::
 :: TODO(absl-team): Remove -Wno-microsoft-cast
-%KOKORO_GFILE_DIR%\bazel-8.0.0-windows-x86_64.exe ^
+%KOKORO_GFILE_DIR%\bazel-8.2.1-windows-x86_64.exe ^
   test ... ^
   --compilation_mode=%COMPILATION_MODE% ^
   --compiler=clang-cl ^
@@ -47,7 +55,6 @@
   --copt=-Wno-microsoft-cast ^
   --cxxopt=/std:%STD% ^
   --define=absl=1 ^
-  --distdir=%KOKORO_GFILE_DIR%\distdir ^
   --enable_bzlmod=true ^
   --extra_execution_platforms=//:x64_windows-clang-cl ^
   --extra_toolchains=@local_config_cc//:cc-toolchain-x64_windows-clang-cl ^
@@ -55,7 +62,8 @@
   --test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" ^
   --test_env=TZDIR="%CD%\absl\time\internal\cctz\testdata\zoneinfo" ^
   --test_output=errors ^
-  --test_tag_filters=-benchmark
+  --test_tag_filters=-benchmark ^
+  %VENDOR_FLAG%
 
 if %errorlevel% neq 0 EXIT /B 1
 EXIT /B 0
diff --git a/third_party/abseil-cpp/ci/windows_msvc_bazel.bat b/third_party/abseil-cpp/ci/windows_msvc_bazel.bat
index e0cd016..bbb57b4 100755
--- a/third_party/abseil-cpp/ci/windows_msvc_bazel.bat
+++ b/third_party/abseil-cpp/ci/windows_msvc_bazel.bat
@@ -18,6 +18,14 @@
 CD %~dp0\..
 if %errorlevel% neq 0 EXIT /B 1
 
+:: Use Bazel Vendor mode to reduce reliance on external dependencies.
+IF EXIST "%KOKORO_GFILE_DIR%\distdir\abseil-cpp_vendor.tar.gz" (
+  tar --force-local -xf "%KOKORO_GFILE_DIR%\distdir\abseil-cpp_vendor.tar.gz" -C c:\
+  SET VENDOR_FLAG=--vendor_dir=c:\abseil-cpp_vendor
+) ELSE (
+  SET VENDOR_FLAG=
+)
+
 :: Set the standard version, [c++17|c++latest]
 :: https://msdn.microsoft.com/en-us/library/mt490614.aspx
 :: The default is c++17 if not set on command line.
@@ -34,19 +42,19 @@
 :: To upgrade Bazel, first download a new binary from
 :: https://github.com/bazelbuild/bazel/releases and copy it to
 :: /google/data/rw/teams/absl/kokoro/windows.
-%KOKORO_GFILE_DIR%\bazel-8.0.0-windows-x86_64.exe ^
+"%KOKORO_GFILE_DIR%\bazel-8.2.1-windows-x86_64.exe" ^
   test ... ^
   --compilation_mode=%COMPILATION_MODE% ^
   --copt=/WX ^
   --copt=/std:%STD% ^
   --define=absl=1 ^
-  --distdir=%KOKORO_GFILE_DIR%\distdir ^
   --enable_bzlmod=true ^
   --keep_going ^
   --test_env="GTEST_INSTALL_FAILURE_SIGNAL_HANDLER=1" ^
   --test_env=TZDIR="%CD%\absl\time\internal\cctz\testdata\zoneinfo" ^
   --test_output=errors ^
-  --test_tag_filters=-benchmark
+  --test_tag_filters=-benchmark ^
+  %VENDOR_FLAG%
 
 if %errorlevel% neq 0 EXIT /B 1
 EXIT /B 0
diff --git a/third_party/abseil-cpp/ci/windows_msvc_cmake.bat b/third_party/abseil-cpp/ci/windows_msvc_cmake.bat
index c2d9e42..62cdb70 100755
--- a/third_party/abseil-cpp/ci/windows_msvc_cmake.bat
+++ b/third_party/abseil-cpp/ci/windows_msvc_cmake.bat
@@ -16,7 +16,7 @@
 
 :: The version of GoogleTest to be used in the CMake tests in this directory.
 :: Keep this in sync with the version in the WORKSPACE file.
-SET ABSL_GOOGLETEST_VERSION=1.16.0
+SET ABSL_GOOGLETEST_VERSION=1.17.0
 SET ABSL_GOOGLETEST_DOWNLOAD_URL=https://github.com/google/googletest/releases/download/v%ABSL_GOOGLETEST_VERSION%/googletest-%ABSL_GOOGLETEST_VERSION%.tar.gz
 
 :: Replace '\' with '/' in Windows paths for CMake.
diff --git a/third_party/abseil-cpp/symbols_arm64_dbg.def b/third_party/abseil-cpp/symbols_arm64_dbg.def
index 14e46ace..c6f40ac 100644
--- a/third_party/abseil-cpp/symbols_arm64_dbg.def
+++ b/third_party/abseil-cpp/symbols_arm64_dbg.def
@@ -245,6 +245,7 @@
     ??$?6PEAX@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEAX@Z
     ??$?6PEBD@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEBD@Z
     ??$?6PEBX@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEBX@Z
+    ??$?6PEB_W@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEB_W@Z
     ??$?6_J@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_J@Z
     ??$?6_K@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_K@Z
     ??$?6_N@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_N@Z
@@ -422,8 +423,10 @@
     ??$ConvertIntArg@_W@str_format_internal@absl@@YA_N_WVFormatConversionSpecImpl@01@PEAVFormatSinkImpl@01@@Z
     ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXD_K@Z
     ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXD_K@Z
     ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBufferWithStructuredProtoField@$00@LogMessage@log_internal@absl@@AEAAXUStructuredProtoField@12@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBufferWithStructuredProtoField@$0A@@LogMessage@log_internal@absl@@AEAAXUStructuredProtoField@12@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??$CreateDefault@$0A@@CommonFields@container_internal@absl@@SA?AV012@XZ
@@ -2346,10 +2349,13 @@
     ??4uint128@absl@@QEAAAEAV01@_K@Z
     ??5absl@@YA?AVuint128@0@V10@H@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@AEBV?$basic_string@_WU?$char_traits@_W@__Cr@std@@V?$allocator@_W@23@@__Cr@std@@@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@H@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@P6AAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV345@@Z@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@P6AAEAVios_base@__Cr@std@@AEAV345@@Z@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@V?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@_W@Z
     ??6absl@@YA?AVuint128@0@V10@H@Z
     ??6absl@@YAAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV123@AEBVCord@0@@Z
     ??6absl@@YAAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV123@AEBVStatus@0@@Z
@@ -2845,6 +2851,7 @@
     ?AppendTreeToTree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@W4MethodIdentifier@CordzUpdateTracker@53@@Z
     ?AppendTruncated@log_internal@absl@@YA_KD_KAEAV?$Span@D@2@@Z
     ?AppendTruncated@log_internal@absl@@YA_KV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV?$Span@D@2@@Z
+    ?AppendTruncated@log_internal@absl@@YA_KV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@AEAV?$Span@D@2@@Z
     ?ApplyMask@MaskedPointer@flags_internal@absl@@AEAAX_K@Z
     ?ApplySubstitutions@strings_internal@absl@@YAHV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAV?$vector@UViableSubstitution@strings_internal@absl@@V?$allocator@UViableSubstitution@strings_internal@absl@@@__Cr@std@@@45@PEAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@45@@Z
     ?AreItersFromSameContainer@container_internal@absl@@YA_NPEBW4ctrl_t@12@0AEBQEBX1@Z
@@ -3745,7 +3752,6 @@
     ?Post@StdcppWaiter@synchronization_internal@absl@@QEAAXXZ
     ?Post@Win32Waiter@synchronization_internal@absl@@QEAAXXZ
     ?PrepareForSampling@HashtablezInfo@container_internal@absl@@QEAAX_J_K11G@Z
-    ?PrepareInsertCommon@container_internal@absl@@YAXAEAVCommonFields@12@@Z
     ?PrepareInsertNonSoo@container_internal@absl@@YA_KAEAVCommonFields@12@AEBUPolicyFunctions@12@_KUFindInfo@12@@Z
     ?PrepareToDie@LogMessage@log_internal@absl@@AEAAXXZ
     ?PrepareToModify@Status@absl@@CAPEAVStatusRep@status_internal@2@_K@Z
diff --git a/third_party/abseil-cpp/symbols_arm64_rel.def b/third_party/abseil-cpp/symbols_arm64_rel.def
index c6ad2fa..ad256a4 100644
--- a/third_party/abseil-cpp/symbols_arm64_rel.def
+++ b/third_party/abseil-cpp/symbols_arm64_rel.def
@@ -16,6 +16,7 @@
     ??$?6PEAX@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEAX@Z
     ??$?6PEBD@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEBD@Z
     ??$?6PEBX@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEBX@Z
+    ??$?6PEB_W@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEB_W@Z
     ??$?6_J@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_J@Z
     ??$?6_K@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_K@Z
     ??$?6_N@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_N@Z
@@ -57,8 +58,10 @@
     ??$ConvertIntArg@_W@str_format_internal@absl@@YA_N_WVFormatConversionSpecImpl@01@PEAVFormatSinkImpl@01@@Z
     ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXD_K@Z
     ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXD_K@Z
     ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBufferWithStructuredProtoField@$00@LogMessage@log_internal@absl@@AEAAXUStructuredProtoField@12@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBufferWithStructuredProtoField@$0A@@LogMessage@log_internal@absl@@AEAAXUStructuredProtoField@12@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??$DeallocateBackingArray@$07V?$allocator@D@__Cr@std@@@container_internal@absl@@YAXPEAX_KPEAW4ctrl_t@01@11_N@Z
@@ -296,9 +299,12 @@
     ??4CrcCordState@crc_internal@absl@@QEAAAEAV012@AEBV012@@Z
     ??4FlagsUsageConfig@absl@@QEAAAEAU01@AEBU01@@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@AEBV?$basic_string@_WU?$char_traits@_W@__Cr@std@@V?$allocator@_W@23@@__Cr@std@@@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@P6AAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV345@@Z@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@P6AAEAVios_base@__Cr@std@@AEAV345@@Z@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@V?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@_W@Z
     ??6absl@@YAAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV123@AEBVCord@0@@Z
     ??6absl@@YAAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV123@AEBVStatus@0@@Z
     ??6absl@@YAAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV123@Vint128@0@@Z
diff --git a/third_party/abseil-cpp/symbols_x64_dbg.def b/third_party/abseil-cpp/symbols_x64_dbg.def
index db9f1cc..3175b4f 100644
--- a/third_party/abseil-cpp/symbols_x64_dbg.def
+++ b/third_party/abseil-cpp/symbols_x64_dbg.def
@@ -245,6 +245,7 @@
     ??$?6PEAX@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEAX@Z
     ??$?6PEBD@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEBD@Z
     ??$?6PEBX@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEBX@Z
+    ??$?6PEB_W@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEB_W@Z
     ??$?6_J@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_J@Z
     ??$?6_K@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_K@Z
     ??$?6_N@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_N@Z
@@ -422,8 +423,10 @@
     ??$ConvertIntArg@_W@str_format_internal@absl@@YA_N_WVFormatConversionSpecImpl@01@PEAVFormatSinkImpl@01@@Z
     ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXD_K@Z
     ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXD_K@Z
     ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBufferWithStructuredProtoField@$00@LogMessage@log_internal@absl@@AEAAXUStructuredProtoField@12@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBufferWithStructuredProtoField@$0A@@LogMessage@log_internal@absl@@AEAAXUStructuredProtoField@12@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??$CreateDefault@$0A@@CommonFields@container_internal@absl@@SA?AV012@XZ
@@ -2348,10 +2351,13 @@
     ??4uint128@absl@@QEAAAEAV01@_K@Z
     ??5absl@@YA?AVuint128@0@V10@H@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@AEBV?$basic_string@_WU?$char_traits@_W@__Cr@std@@V?$allocator@_W@23@@__Cr@std@@@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@H@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@P6AAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV345@@Z@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@P6AAEAVios_base@__Cr@std@@AEAV345@@Z@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@V?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@_W@Z
     ??6absl@@YA?AVuint128@0@V10@H@Z
     ??6absl@@YAAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV123@AEBVCord@0@@Z
     ??6absl@@YAAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV123@AEBVStatus@0@@Z
@@ -2847,6 +2853,7 @@
     ?AppendTreeToTree@InlineRep@Cord@absl@@QEAAXPEAUCordRep@cord_internal@3@W4MethodIdentifier@CordzUpdateTracker@53@@Z
     ?AppendTruncated@log_internal@absl@@YA_KD_KAEAV?$Span@D@2@@Z
     ?AppendTruncated@log_internal@absl@@YA_KV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV?$Span@D@2@@Z
+    ?AppendTruncated@log_internal@absl@@YA_KV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@AEAV?$Span@D@2@@Z
     ?ApplyMask@MaskedPointer@flags_internal@absl@@AEAAX_K@Z
     ?ApplySubstitutions@strings_internal@absl@@YAHV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PEAV?$vector@UViableSubstitution@strings_internal@absl@@V?$allocator@UViableSubstitution@strings_internal@absl@@@__Cr@std@@@45@PEAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@45@@Z
     ?AreItersFromSameContainer@container_internal@absl@@YA_NPEBW4ctrl_t@12@0AEBQEBX1@Z
@@ -3749,7 +3756,6 @@
     ?Post@StdcppWaiter@synchronization_internal@absl@@QEAAXXZ
     ?Post@Win32Waiter@synchronization_internal@absl@@QEAAXXZ
     ?PrepareForSampling@HashtablezInfo@container_internal@absl@@QEAAX_J_K11G@Z
-    ?PrepareInsertCommon@container_internal@absl@@YAXAEAVCommonFields@12@@Z
     ?PrepareInsertNonSoo@container_internal@absl@@YA_KAEAVCommonFields@12@AEBUPolicyFunctions@12@_KUFindInfo@12@@Z
     ?PrepareToDie@LogMessage@log_internal@absl@@AEAAXXZ
     ?PrepareToModify@Status@absl@@CAPEAVStatusRep@status_internal@2@_K@Z
diff --git a/third_party/abseil-cpp/symbols_x64_rel.def b/third_party/abseil-cpp/symbols_x64_rel.def
index 05c9783c..7564af2a 100644
--- a/third_party/abseil-cpp/symbols_x64_rel.def
+++ b/third_party/abseil-cpp/symbols_x64_rel.def
@@ -16,6 +16,7 @@
     ??$?6PEAX@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEAX@Z
     ??$?6PEBD@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEBD@Z
     ??$?6PEBX@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEBX@Z
+    ??$?6PEB_W@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEB_W@Z
     ??$?6_J@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_J@Z
     ??$?6_K@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_K@Z
     ??$?6_N@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_N@Z
@@ -57,8 +58,10 @@
     ??$ConvertIntArg@_W@str_format_internal@absl@@YA_N_WVFormatConversionSpecImpl@01@PEAVFormatSinkImpl@01@@Z
     ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXD_K@Z
     ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXD_K@Z
     ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBufferWithStructuredProtoField@$00@LogMessage@log_internal@absl@@AEAAXUStructuredProtoField@12@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBufferWithStructuredProtoField@$0A@@LogMessage@log_internal@absl@@AEAAXUStructuredProtoField@12@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??$DeallocateBackingArray@$07V?$allocator@D@__Cr@std@@@container_internal@absl@@YAXPEAX_KPEAW4ctrl_t@01@11_N@Z
@@ -297,9 +300,12 @@
     ??4CrcCordState@crc_internal@absl@@QEAAAEAV012@AEBV012@@Z
     ??4FlagsUsageConfig@absl@@QEAAAEAU01@AEBU01@@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@AEBV?$basic_string@_WU?$char_traits@_W@__Cr@std@@V?$allocator@_W@23@@__Cr@std@@@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@P6AAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV345@@Z@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@P6AAEAVios_base@__Cr@std@@AEAV345@@Z@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@V?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@_W@Z
     ??6absl@@YAAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV123@AEBVCord@0@@Z
     ??6absl@@YAAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV123@AEBVStatus@0@@Z
     ??6absl@@YAAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV123@Vint128@0@@Z
diff --git a/third_party/abseil-cpp/symbols_x64_rel_asan.def b/third_party/abseil-cpp/symbols_x64_rel_asan.def
index 456c01a..d12f237 100644
--- a/third_party/abseil-cpp/symbols_x64_rel_asan.def
+++ b/third_party/abseil-cpp/symbols_x64_rel_asan.def
@@ -19,6 +19,7 @@
     ??$?6PEAX@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEAX@Z
     ??$?6PEBD@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEBD@Z
     ??$?6PEBX@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEBX@Z
+    ??$?6PEB_W@LogMessage@log_internal@absl@@QEAAAEAV012@AEBQEB_W@Z
     ??$?6_J@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_J@Z
     ??$?6_K@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_K@Z
     ??$?6_N@LogMessage@log_internal@absl@@QEAAAEAV012@AEB_N@Z
@@ -62,8 +63,10 @@
     ??$ConvertIntArg@_W@str_format_internal@absl@@YA_N_WVFormatConversionSpecImpl@01@PEAVFormatSinkImpl@01@@Z
     ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXD_K@Z
     ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXD_K@Z
     ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AEAAXV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBufferWithStructuredProtoField@$00@LogMessage@log_internal@absl@@AEAAXUStructuredProtoField@12@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBufferWithStructuredProtoField@$0A@@LogMessage@log_internal@absl@@AEAAXUStructuredProtoField@12@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??$DeallocateBackingArray@$07V?$allocator@D@__Cr@std@@@container_internal@absl@@YAXPEAX_KPEAW4ctrl_t@01@11_N@Z
@@ -310,9 +313,12 @@
     ??4InlineData@cord_internal@absl@@QEAAAEAV012@AEBV012@@Z
     ??4InlineRep@Cord@absl@@QEAAAEAV012@$$QEAV012@@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@AEBV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@AEBV?$basic_string@_WU?$char_traits@_W@__Cr@std@@V?$allocator@_W@23@@__Cr@std@@@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@P6AAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV345@@Z@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@P6AAEAVios_base@__Cr@std@@AEAV345@@Z@Z
     ??6LogMessage@log_internal@absl@@QEAAAEAV012@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@V?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QEAAAEAV012@_W@Z
     ??6absl@@YAAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV123@AEBVCord@0@@Z
     ??6absl@@YAAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV123@AEBVStatus@0@@Z
     ??6absl@@YAAEAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AEAV123@Vint128@0@@Z
@@ -971,7 +977,6 @@
     ?Post@StdcppWaiter@synchronization_internal@absl@@QEAAXXZ
     ?Post@Win32Waiter@synchronization_internal@absl@@QEAAXXZ
     ?PrepareForSampling@HashtablezInfo@container_internal@absl@@QEAAX_J_K11G@Z
-    ?PrepareInsertCommon@container_internal@absl@@YAXAEAVCommonFields@12@@Z
     ?PrepareInsertNonSoo@container_internal@absl@@YA_KAEAVCommonFields@12@AEBUPolicyFunctions@12@_KUFindInfo@12@@Z
     ?PrepareToDie@LogMessage@log_internal@absl@@AEAAXXZ
     ?PrepareToModify@Status@absl@@CAPEAVStatusRep@status_internal@2@_K@Z
diff --git a/third_party/abseil-cpp/symbols_x86_dbg.def b/third_party/abseil-cpp/symbols_x86_dbg.def
index 3aa6864e..a2f02fe 100644
--- a/third_party/abseil-cpp/symbols_x86_dbg.def
+++ b/third_party/abseil-cpp/symbols_x86_dbg.def
@@ -245,6 +245,7 @@
     ??$?6PAX@LogMessage@log_internal@absl@@QAEAAV012@ABQAX@Z
     ??$?6PBD@LogMessage@log_internal@absl@@QAEAAV012@ABQBD@Z
     ??$?6PBX@LogMessage@log_internal@absl@@QAEAAV012@ABQBX@Z
+    ??$?6PB_W@LogMessage@log_internal@absl@@QAEAAV012@ABQB_W@Z
     ??$?6_J@LogMessage@log_internal@absl@@QAEAAV012@AB_J@Z
     ??$?6_K@LogMessage@log_internal@absl@@QAEAAV012@AB_K@Z
     ??$?6_N@LogMessage@log_internal@absl@@QAEAAV012@AB_N@Z
@@ -422,8 +423,10 @@
     ??$ConvertIntArg@_W@str_format_internal@absl@@YA_N_WVFormatConversionSpecImpl@01@PAVFormatSinkImpl@01@@Z
     ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AAEXDI@Z
     ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AAEXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AAEXV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AAEXDI@Z
     ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AAEXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AAEXV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBufferWithStructuredProtoField@$00@LogMessage@log_internal@absl@@AAEXUStructuredProtoField@12@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBufferWithStructuredProtoField@$0A@@LogMessage@log_internal@absl@@AAEXUStructuredProtoField@12@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??$CreateDefault@$0A@@CommonFields@container_internal@absl@@SA?AV012@XZ
@@ -2346,10 +2349,13 @@
     ??4uint128@absl@@QAEAAV01@_K@Z
     ??5absl@@YA?AVuint128@0@V10@H@Z
     ??6LogMessage@log_internal@absl@@QAEAAV012@ABV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QAEAAV012@ABV?$basic_string@_WU?$char_traits@_W@__Cr@std@@V?$allocator@_W@23@@__Cr@std@@@Z
     ??6LogMessage@log_internal@absl@@QAEAAV012@H@Z
     ??6LogMessage@log_internal@absl@@QAEAAV012@P6AAAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AAV345@@Z@Z
     ??6LogMessage@log_internal@absl@@QAEAAV012@P6AAAVios_base@__Cr@std@@AAV345@@Z@Z
     ??6LogMessage@log_internal@absl@@QAEAAV012@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QAEAAV012@V?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QAEAAV012@_W@Z
     ??6absl@@YA?AVuint128@0@V10@H@Z
     ??6absl@@YAAAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AAV123@ABVCord@0@@Z
     ??6absl@@YAAAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AAV123@ABVStatus@0@@Z
@@ -2845,6 +2851,7 @@
     ?AppendTreeToTree@InlineRep@Cord@absl@@QAEXPAUCordRep@cord_internal@3@W4MethodIdentifier@CordzUpdateTracker@53@@Z
     ?AppendTruncated@log_internal@absl@@YAIDIAAV?$Span@D@2@@Z
     ?AppendTruncated@log_internal@absl@@YAIV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AAV?$Span@D@2@@Z
+    ?AppendTruncated@log_internal@absl@@YAIV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@AAV?$Span@D@2@@Z
     ?ApplyMask@MaskedPointer@flags_internal@absl@@AAEXI@Z
     ?ApplySubstitutions@strings_internal@absl@@YAHV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@PAV?$vector@UViableSubstitution@strings_internal@absl@@V?$allocator@UViableSubstitution@strings_internal@absl@@@__Cr@std@@@45@PAV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@45@@Z
     ?AreItersFromSameContainer@container_internal@absl@@YA_NPBW4ctrl_t@12@0ABQBX1@Z
@@ -3747,7 +3754,6 @@
     ?Post@StdcppWaiter@synchronization_internal@absl@@QAEXXZ
     ?Post@Win32Waiter@synchronization_internal@absl@@QAEXXZ
     ?PrepareForSampling@HashtablezInfo@container_internal@absl@@QAEX_JIIIG@Z
-    ?PrepareInsertCommon@container_internal@absl@@YAXAAVCommonFields@12@@Z
     ?PrepareInsertNonSoo@container_internal@absl@@YAIAAVCommonFields@12@ABUPolicyFunctions@12@IUFindInfo@12@@Z
     ?PrepareToDie@LogMessage@log_internal@absl@@AAEXXZ
     ?PrepareToModify@Status@absl@@CAPAVStatusRep@status_internal@2@I@Z
diff --git a/third_party/abseil-cpp/symbols_x86_rel.def b/third_party/abseil-cpp/symbols_x86_rel.def
index bb4bb38..3316635 100644
--- a/third_party/abseil-cpp/symbols_x86_rel.def
+++ b/third_party/abseil-cpp/symbols_x86_rel.def
@@ -16,6 +16,7 @@
     ??$?6PAX@LogMessage@log_internal@absl@@QAEAAV012@ABQAX@Z
     ??$?6PBD@LogMessage@log_internal@absl@@QAEAAV012@ABQBD@Z
     ??$?6PBX@LogMessage@log_internal@absl@@QAEAAV012@ABQBX@Z
+    ??$?6PB_W@LogMessage@log_internal@absl@@QAEAAV012@ABQB_W@Z
     ??$?6_J@LogMessage@log_internal@absl@@QAEAAV012@AB_J@Z
     ??$?6_K@LogMessage@log_internal@absl@@QAEAAV012@AB_K@Z
     ??$?6_N@LogMessage@log_internal@absl@@QAEAAV012@AB_N@Z
@@ -57,8 +58,10 @@
     ??$ConvertIntArg@_W@str_format_internal@absl@@YA_N_WVFormatConversionSpecImpl@01@PAVFormatSinkImpl@01@@Z
     ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AAEXDI@Z
     ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AAEXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??$CopyToEncodedBuffer@$00@LogMessage@log_internal@absl@@AAEXV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AAEXDI@Z
     ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AAEXV?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??$CopyToEncodedBuffer@$0A@@LogMessage@log_internal@absl@@AAEXV?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBufferWithStructuredProtoField@$00@LogMessage@log_internal@absl@@AAEXUStructuredProtoField@12@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??$CopyToEncodedBufferWithStructuredProtoField@$0A@@LogMessage@log_internal@absl@@AAEXUStructuredProtoField@12@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
     ??$DeallocateBackingArray@$03V?$allocator@D@__Cr@std@@@container_internal@absl@@YAXPAXIPAW4ctrl_t@01@II_N@Z
@@ -305,9 +308,12 @@
     ??4CrcCordState@crc_internal@absl@@QAEAAV012@ABV012@@Z
     ??4FlagsUsageConfig@absl@@QAEAAU01@ABU01@@Z
     ??6LogMessage@log_internal@absl@@QAEAAV012@ABV?$basic_string@DU?$char_traits@D@__Cr@std@@V?$allocator@D@23@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QAEAAV012@ABV?$basic_string@_WU?$char_traits@_W@__Cr@std@@V?$allocator@_W@23@@__Cr@std@@@Z
     ??6LogMessage@log_internal@absl@@QAEAAV012@P6AAAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AAV345@@Z@Z
     ??6LogMessage@log_internal@absl@@QAEAAV012@P6AAAVios_base@__Cr@std@@AAV345@@Z@Z
     ??6LogMessage@log_internal@absl@@QAEAAV012@V?$basic_string_view@DU?$char_traits@D@__Cr@std@@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QAEAAV012@V?$basic_string_view@_WU?$char_traits@_W@__Cr@std@@@__Cr@std@@@Z
+    ??6LogMessage@log_internal@absl@@QAEAAV012@_W@Z
     ??6absl@@YAAAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AAV123@ABVCord@0@@Z
     ??6absl@@YAAAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AAV123@ABVStatus@0@@Z
     ??6absl@@YAAAV?$basic_ostream@DU?$char_traits@D@__Cr@std@@@__Cr@std@@AAV123@Vint128@0@@Z
diff --git a/third_party/android_deps/autorolled/build.gradle b/third_party/android_deps/autorolled/build.gradle
index c4bd0e8..86a0abb 100644
--- a/third_party/android_deps/autorolled/build.gradle
+++ b/third_party/android_deps/autorolled/build.gradle
@@ -383,7 +383,7 @@
 repositories {
     google()
     mavenCentral()
-    // {{ANDROIDX_REPO}}
+    // <ANDROIDX_REPO>
 }
 
 dependencies {
diff --git a/third_party/android_deps/autorolled/build.gradle.template b/third_party/android_deps/autorolled/build.gradle.template
index cd8939be..e92336d 100644
--- a/third_party/android_deps/autorolled/build.gradle.template
+++ b/third_party/android_deps/autorolled/build.gradle.template
@@ -18,7 +18,7 @@
 repositories {
     google()
     mavenCentral()
-    // {{ANDROIDX_REPO}}
+    // <ANDROIDX_REPO>
 }
 
 dependencies {
diff --git a/third_party/android_deps/fetch_all.py b/third_party/android_deps/fetch_all.py
index 4eb470d5..bfac798 100755
--- a/third_party/android_deps/fetch_all.py
+++ b/third_party/android_deps/fetch_all.py
@@ -367,10 +367,10 @@
         build_gradle = os.path.join(subdir, _BUILD_GRADLE)
         src_path = pathlib.Path(android_deps_dir) / original_path
         data = src_path.read_text()
-        if '// {{ANDROIDX_REPO}}' in data:
+        if '// <ANDROIDX_REPO>' in data:
             version = fetch_util.get_current_androidx_version()
             repo_url = fetch_util.make_androidx_maven_url(version)
-            data = data.replace('// {{ANDROIDX_REPO}}',
+            data = data.replace('// <ANDROIDX_REPO>',
                                 f'maven {{ url "{repo_url}" }}')
         dst_path = pathlib.Path(build_android_deps_dir) / build_gradle
         dst_path.parent.mkdir()
diff --git a/third_party/androidx/BUILD.gn b/third_party/androidx/BUILD.gn
index 4ad1c33ac..eea1a4c1 100644
--- a/third_party/androidx/BUILD.gn
+++ b/third_party/androidx/BUILD.gn
@@ -23,6 +23,7 @@
       ":androidx_lifecycle_lifecycle_runtime_java",
       ":androidx_lifecycle_lifecycle_viewmodel_java",
       ":androidx_lifecycle_lifecycle_viewmodel_savedstate_java",
+      ":androidx_navigationevent_navigationevent_java",
       ":androidx_savedstate_savedstate_java",
       ":androidx_tracing_tracing_java",
       "//third_party/android_deps:org_jetbrains_kotlinx_kotlinx_coroutines_core_java",
@@ -58,7 +59,7 @@
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
   androidx_android_aar_prebuilt(
       "androidx_annotation_annotation_experimental_java") {
-    aar_path = "../androidx/cipd/libs/androidx_annotation_annotation_experimental/annotation-experimental-1.5.0-SNAPSHOT.aar"
+    aar_path = "../androidx/cipd/libs/androidx_annotation_annotation_experimental/annotation-experimental-1.5.0.aar"
     info_path = "../androidx/committed/libs/androidx_annotation_annotation_experimental/androidx_annotation_annotation_experimental.info"
     enable_bytecode_checks = false
     deps = [ "//third_party/kotlin_stdlib:kotlin_stdlib_java" ]
@@ -618,7 +619,7 @@
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
   androidx_android_aar_prebuilt("androidx_documentfile_documentfile_java") {
-    aar_path = "../androidx/cipd/libs/androidx_documentfile_documentfile/documentfile-1.1.0-SNAPSHOT.aar"
+    aar_path = "../androidx/cipd/libs/androidx_documentfile_documentfile/documentfile-1.1.0.aar"
     info_path = "../androidx/committed/libs/androidx_documentfile_documentfile/androidx_documentfile_documentfile.info"
     enable_bytecode_checks = false
     deps = [
@@ -1477,7 +1478,7 @@
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
   androidx_android_aar_prebuilt("androidx_tvprovider_tvprovider_java") {
-    aar_path = "../androidx/cipd/libs/androidx_tvprovider_tvprovider/tvprovider-1.1.0-SNAPSHOT.aar"
+    aar_path = "../androidx/cipd/libs/androidx_tvprovider_tvprovider/tvprovider-1.1.0.aar"
     info_path = "../androidx/committed/libs/androidx_tvprovider_tvprovider/androidx_tvprovider_tvprovider.info"
     enable_bytecode_checks = false
     deps = [
@@ -3023,6 +3024,37 @@
   }
 
   # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
+  androidx_java_group("androidx_navigationevent_navigationevent_java") {
+    # To remove visibility constraint, add this dependency to
+    # //third_party/androidx/build.gradle.
+    visibility = [
+      ":*",
+      "//third_party/android_deps:*",
+    ]
+    deps = [ ":androidx_navigationevent_navigationevent_android_java" ]
+  }
+
+  # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
+  androidx_android_aar_prebuilt(
+      "androidx_navigationevent_navigationevent_android_java") {
+    aar_path = "../androidx/cipd/libs/androidx_navigationevent_navigationevent_android/navigationevent-android-1.0.0-SNAPSHOT.aar"
+    info_path = "../androidx/committed/libs/androidx_navigationevent_navigationevent_android/androidx_navigationevent_navigationevent_android.info"
+    enable_bytecode_checks = false
+
+    # To remove visibility constraint, add this dependency to
+    # //third_party/androidx/build.gradle.
+    visibility = [
+      ":*",
+      "//third_party/android_deps:*",
+    ]
+    deps = [
+      ":androidx_annotation_annotation_java",
+      ":androidx_core_core_viewtree_java",
+      "//third_party/kotlin_stdlib:kotlin_stdlib_java",
+    ]
+  }
+
+  # This is generated, do not edit. Update BuildConfigGenerator.groovy instead.
   androidx_java_group("androidx_paging_paging_common_java") {
     # To remove visibility constraint, add this dependency to
     # //third_party/androidx/build.gradle.
@@ -3105,6 +3137,7 @@
     deps = [
       ":androidx_collection_collection_java",
       ":androidx_core_core_java",
+      "//third_party/android_deps:org_jspecify_jspecify_java",
     ]
   }
 
diff --git a/third_party/androidx/additional_readme_paths.json b/third_party/androidx/additional_readme_paths.json
index 8eea2cb..6122ad2 100644
--- a/third_party/androidx/additional_readme_paths.json
+++ b/third_party/androidx/additional_readme_paths.json
@@ -117,6 +117,7 @@
     "committed/libs/androidx_navigation_navigation_common_android",
     "committed/libs/androidx_navigation_navigation_compose_android",
     "committed/libs/androidx_navigation_navigation_runtime_android",
+    "committed/libs/androidx_navigationevent_navigationevent_android",
     "committed/libs/androidx_paging_paging_common_android",
     "committed/libs/androidx_paging_paging_common_ktx",
     "committed/libs/androidx_paging_paging_compose_android",
diff --git a/third_party/androidx/bill_of_materials.json b/third_party/androidx/bill_of_materials.json
index 2e8948c..04e8d58 100644
--- a/third_party/androidx/bill_of_materials.json
+++ b/third_party/androidx/bill_of_materials.json
@@ -22,7 +22,7 @@
     {
         "name": "annotation-experimental",
         "group": "androidx.annotation",
-        "version": "1.5.0-SNAPSHOT"
+        "version": "1.5.0"
     },
     {
         "name": "annotation-jvm",
@@ -432,7 +432,7 @@
     {
         "name": "documentfile",
         "group": "androidx.documentfile",
-        "version": "1.1.0-SNAPSHOT"
+        "version": "1.1.0"
     },
     {
         "name": "drawerlayout",
@@ -765,6 +765,16 @@
         "version": "2.10.0-SNAPSHOT"
     },
     {
+        "name": "navigationevent",
+        "group": "androidx.navigationevent",
+        "version": "1.0.0-SNAPSHOT"
+    },
+    {
+        "name": "navigationevent-android",
+        "group": "androidx.navigationevent",
+        "version": "1.0.0-SNAPSHOT"
+    },
+    {
         "name": "paging-common",
         "group": "androidx.paging",
         "version": "3.4.0-SNAPSHOT"
@@ -1032,7 +1042,7 @@
     {
         "name": "tvprovider",
         "group": "androidx.tvprovider",
-        "version": "1.1.0-SNAPSHOT"
+        "version": "1.1.0"
     },
     {
         "name": "vectordrawable",
diff --git a/third_party/androidx/build.gradle b/third_party/androidx/build.gradle
index 89d73ef..08a0495 100644
--- a/third_party/androidx/build.gradle
+++ b/third_party/androidx/build.gradle
@@ -19,7 +19,7 @@
 versionCache['androidx.activity:activity-compose'] = '1.12.0-SNAPSHOT'
 versionCache['androidx.activity:activity-ktx'] = '1.12.0-SNAPSHOT'
 versionCache['androidx.annotation:annotation'] = '1.10.0-SNAPSHOT'
-versionCache['androidx.annotation:annotation-experimental'] = '1.5.0-SNAPSHOT'
+versionCache['androidx.annotation:annotation-experimental'] = '1.5.0'
 versionCache['androidx.annotation:annotation-jvm'] = '1.10.0-SNAPSHOT'
 versionCache['androidx.appcompat:appcompat'] = '1.8.0-SNAPSHOT'
 versionCache['androidx.appcompat:appcompat-resources'] = '1.8.0-SNAPSHOT'
@@ -101,7 +101,7 @@
 versionCache['androidx.datastore:datastore-core-android'] = '1.2.0-SNAPSHOT'
 versionCache['androidx.datastore:datastore-core-okio'] = '1.2.0-SNAPSHOT'
 versionCache['androidx.datastore:datastore-core-okio-jvm'] = '1.2.0-SNAPSHOT'
-versionCache['androidx.documentfile:documentfile'] = '1.1.0-SNAPSHOT'
+versionCache['androidx.documentfile:documentfile'] = '1.1.0'
 versionCache['androidx.drawerlayout:drawerlayout'] = '1.3.0-SNAPSHOT'
 versionCache['androidx.dynamicanimation:dynamicanimation'] = '1.1.0'
 versionCache['androidx.emoji2:emoji2'] = '1.6.0-SNAPSHOT'
@@ -168,6 +168,8 @@
 versionCache['androidx.navigation:navigation-compose-android'] = '2.10.0-SNAPSHOT'
 versionCache['androidx.navigation:navigation-runtime'] = '2.10.0-SNAPSHOT'
 versionCache['androidx.navigation:navigation-runtime-android'] = '2.10.0-SNAPSHOT'
+versionCache['androidx.navigationevent:navigationevent'] = '1.0.0-SNAPSHOT'
+versionCache['androidx.navigationevent:navigationevent-android'] = '1.0.0-SNAPSHOT'
 versionCache['androidx.paging:paging-common'] = '3.4.0-SNAPSHOT'
 versionCache['androidx.paging:paging-common-android'] = '3.4.0-SNAPSHOT'
 versionCache['androidx.paging:paging-common-ktx'] = '3.4.0-SNAPSHOT'
@@ -221,7 +223,7 @@
 versionCache['androidx.tracing:tracing-perfetto-binary'] = '1.0.0'
 versionCache['androidx.tracing:tracing-perfetto-handshake'] = '1.0.0'
 versionCache['androidx.transition:transition'] = '1.7.0-SNAPSHOT'
-versionCache['androidx.tvprovider:tvprovider'] = '1.1.0-SNAPSHOT'
+versionCache['androidx.tvprovider:tvprovider'] = '1.1.0'
 versionCache['androidx.vectordrawable:vectordrawable'] = '1.2.0'
 versionCache['androidx.vectordrawable:vectordrawable-animated'] = '1.2.0'
 versionCache['androidx.versionedparcelable:versionedparcelable'] = '1.2.1'
@@ -302,7 +304,7 @@
     google()
     maven {
         // This URL is generated by the fetch_all_androidx.py script.
-        url 'https://androidx.dev/snapshots/builds/13452791/artifacts/repository'
+        url 'https://androidx.dev/snapshots/builds/13464459/artifacts/repository'
     }
     mavenCentral()
 }
diff --git a/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium b/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium
index 315e5df..fae631c 100644
--- a/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium
@@ -1,7 +1,7 @@
 Name: Experimental annotation
 Short Name: annotation-experimental
-URL: https://developer.android.com/jetpack/androidx/releases/annotation#1.5.0-SNAPSHOT
-Version: 1.5.0-SNAPSHOT
+URL: https://developer.android.com/jetpack/androidx/releases/annotation#1.5.0
+Version: 1.5.0
 License: Apache-2.0
 License File: LICENSE
 CPEPrefix: unknown
diff --git a/third_party/androidx/committed/libs/androidx_documentfile_documentfile/README.chromium b/third_party/androidx/committed/libs/androidx_documentfile_documentfile/README.chromium
index 6363a80f..d67ee2d 100644
--- a/third_party/androidx/committed/libs/androidx_documentfile_documentfile/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_documentfile_documentfile/README.chromium
@@ -1,7 +1,7 @@
 Name: Document File
 Short Name: documentfile
-URL: https://developer.android.com/jetpack/androidx/releases/documentfile#1.1.0-SNAPSHOT
-Version: 1.1.0-SNAPSHOT
+URL: https://developer.android.com/jetpack/androidx/releases/documentfile#1.1.0
+Version: 1.1.0
 License: Apache-2.0
 License File: LICENSE
 CPEPrefix: unknown
diff --git a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/LICENSE b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/LICENSE
@@ -0,0 +1,202 @@
+
+                                 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/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium
new file mode 100644
index 0000000..09b3065
--- /dev/null
+++ b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium
@@ -0,0 +1,16 @@
+Name: Navigation Event
+Short Name: navigationevent-android
+URL: https://developer.android.com/jetpack/androidx/releases/navigationevent#1.0.0-SNAPSHOT
+Version: 1.0.0-SNAPSHOT
+License: Apache-2.0
+License File: LICENSE
+CPEPrefix: unknown
+Security Critical: yes
+Shipped: yes
+License Android Compatible: yes
+
+Description:
+Provides APIs to easily intercept platform navigation events, including swipes and clicks, to provide a consistent API surface for handling these events.
+
+Local Modifications:
+No modifications.
diff --git a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/androidx_navigationevent_navigationevent_android.info b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/androidx_navigationevent_navigationevent_android.info
new file mode 100644
index 0000000..5e597cab
--- /dev/null
+++ b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/androidx_navigationevent_navigationevent_android.info
@@ -0,0 +1,16 @@
+# Generated by //build/android/gyp/aar.py
+# To regenerate, use "update_android_aar_prebuilts = true" and run "gn gen".
+
+aidl = []
+assets = []
+has_classes_jar = true
+has_native_libraries = false
+has_proguard_flags = true
+has_r_text_file = true
+is_manifest_empty = true
+manifest_package = "androidx.navigationevent"
+resources = [
+  "res/values/values.xml"
+]
+subjar_tuples = []
+subjars = []
diff --git a/third_party/androidx/committed/libs/androidx_tvprovider_tvprovider/README.chromium b/third_party/androidx/committed/libs/androidx_tvprovider_tvprovider/README.chromium
index d691a7f..203264d 100644
--- a/third_party/androidx/committed/libs/androidx_tvprovider_tvprovider/README.chromium
+++ b/third_party/androidx/committed/libs/androidx_tvprovider_tvprovider/README.chromium
@@ -1,7 +1,7 @@
 Name: TV Provider
 Short Name: tvprovider
-URL: https://developer.android.com/jetpack/androidx/releases/tvprovider#1.1.0-SNAPSHOT
-Version: 1.1.0-SNAPSHOT
+URL: https://developer.android.com/jetpack/androidx/releases/tvprovider#1.1.0
+Version: 1.1.0
 License: Apache-2.0
 License File: LICENSE
 CPEPrefix: unknown
diff --git a/third_party/angle b/third_party/angle
index 2d61c57..02bc00c 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 2d61c576f0dd8a1a5ef5adeefe19101c5ea64eb7
+Subproject commit 02bc00c406237810c435277a13f742d14cd16782
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 8e80459f..e340190 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -2149,26 +2149,6 @@
              "Prerender2EarlyDocumentLifecycleUpdate",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kPrerender2WarmUpCompositor,
-             "Prerender2WarmUpCompositor",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-const base::FeatureParam<Prerender2WarmUpCompositorTriggerPoint>::Option
-    prerender2_warm_up_compositor_trigger_point[] = {
-        {Prerender2WarmUpCompositorTriggerPoint::kDidCommitLoad,
-         "did_commit_load"},
-        {Prerender2WarmUpCompositorTriggerPoint::
-             kDidDispatchDOMContentLoadedEvent,
-         "did_dispatch_dom_content_loaded_event"},
-        {Prerender2WarmUpCompositorTriggerPoint::kDidFinishLoad,
-         "did_finish_load"},
-};
-BASE_FEATURE_ENUM_PARAM(Prerender2WarmUpCompositorTriggerPoint,
-                        kPrerender2WarmUpCompositorTriggerPoint,
-                        &kPrerender2WarmUpCompositor,
-                        "trigger_point",
-                        Prerender2WarmUpCompositorTriggerPoint::kDidCommitLoad,
-                        &prerender2_warm_up_compositor_trigger_point);
-
 // Enable limiting previews loading hints to specific resource types.
 BASE_FEATURE(kPreviewsResourceLoadingHintsSpecificResourceTypes,
              "PreviewsResourceLoadingHintsSpecificResourceTypes",
@@ -2841,6 +2821,10 @@
              "WebviewAccelerateSmallCanvases",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+BASE_FEATURE(kNoReferrerForPreloadFromSubresource,
+             "NoReferrerForPreloadFromSubresource",
+             base::FEATURE_ENABLED_BY_DEFAULT);
+
 // When adding new features or constants for features, please keep the features
 // sorted by identifier name (e.g. `kAwesomeFeature`), and the constants for
 // that feature grouped with the associated feature.
diff --git a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
index 009f356..9fe216a 100644
--- a/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
+++ b/third_party/blink/common/web_preferences/web_preferences_mojom_traits.cc
@@ -73,7 +73,6 @@
   out->dns_prefetching_enabled = data.dns_prefetching_enabled();
   out->data_saver_enabled = data.data_saver_enabled();
   out->local_storage_enabled = data.local_storage_enabled();
-  out->databases_enabled = data.databases_enabled();
   out->tabs_to_links = data.tabs_to_links();
   out->disable_ipc_flooding_protection = data.disable_ipc_flooding_protection();
   out->hyperlink_auditing_enabled = data.hyperlink_auditing_enabled();
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 4adbfe7..4fb83eb 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -1458,19 +1458,6 @@
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(
     kPrerender2EarlyDocumentLifecycleUpdate);
 
-// Enables to warm up compositor on certain loading event of prerender initial
-// navigation. The feature `kWarmUpCompositor` in cc is required to enable this
-// feature. Please see crbug.com/41496019 for more details.
-BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kPrerender2WarmUpCompositor);
-enum class Prerender2WarmUpCompositorTriggerPoint {
-  kDidCommitLoad,
-  kDidDispatchDOMContentLoadedEvent,
-  kDidFinishLoad,
-};
-BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE_PARAM(
-    Prerender2WarmUpCompositorTriggerPoint,
-    kPrerender2WarmUpCompositorTriggerPoint);
-
 // Firing pagehide events for intended prerender cancellation. See
 // crbug.com/353628449 for more details.
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kPageHideEventForPrerender2);
@@ -1848,6 +1835,9 @@
 
 BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kWebviewAccelerateSmallCanvases);
 
+// Kill switch for https://crbug.com/415810136.
+BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kNoReferrerForPreloadFromSubresource);
+
 // When adding new features or constants for features, please keep the features
 // sorted by identifier name (e.g. `kAwesomeFeature`), and the constants for
 // that feature grouped with the associated feature.
diff --git a/third_party/blink/public/common/web_preferences/web_preferences.h b/third_party/blink/public/common/web_preferences/web_preferences.h
index 85f312a..15117b45 100644
--- a/third_party/blink/public/common/web_preferences/web_preferences.h
+++ b/third_party/blink/public/common/web_preferences/web_preferences.h
@@ -75,7 +75,6 @@
   // 'Save-Data: on'.
   bool data_saver_enabled = false;
   bool local_storage_enabled = false;
-  bool databases_enabled = false;
   bool tabs_to_links = true;
   bool disable_ipc_flooding_protection = false;
   bool hyperlink_auditing_enabled = true;
diff --git a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
index 1173ace..86e4152 100644
--- a/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
+++ b/third_party/blink/public/common/web_preferences/web_preferences_mojom_traits.h
@@ -150,10 +150,6 @@
     return r.local_storage_enabled;
   }
 
-  static bool databases_enabled(const blink::web_pref::WebPreferences& r) {
-    return r.databases_enabled;
-  }
-
   static bool tabs_to_links(const blink::web_pref::WebPreferences& r) {
     return r.tabs_to_links;
   }
diff --git a/third_party/blink/public/mojom/printing/web_printing.mojom b/third_party/blink/public/mojom/printing/web_printing.mojom
index 76073fb6..d797e97 100644
--- a/third_party/blink/public/mojom/printing/web_printing.mojom
+++ b/third_party/blink/public/mojom/printing/web_printing.mojom
@@ -18,6 +18,12 @@
   kTwoSidedShortEdge
 };
 
+enum WebPrintQuality {
+  kDraft,
+  kNormal,
+  kHigh
+};
+
 enum WebPrintColorMode {
   kColor,
   kMonochrome
@@ -154,6 +160,9 @@
   WebPrintColorMode print_color_mode_default;
   array<WebPrintColorMode> print_color_mode_supported;
 
+  WebPrintQuality print_quality_default;
+  array<WebPrintQuality> print_quality_supported;
+
   WebPrinterState printer_state;
   string printer_state_message;
   array<WebPrinterStateReason> printer_state_reasons;
@@ -190,6 +199,7 @@
   WebPrintingOrientationRequested? orientation_requested;
   gfx.mojom.Size? printer_resolution;
   WebPrintColorMode? print_color_mode;
+  WebPrintQuality? print_quality;
   WebPrintingSides? sides;
 };
 
diff --git a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
index ffd46eb..41d33c9 100644
--- a/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
+++ b/third_party/blink/public/mojom/webpreferences/web_preferences.mojom
@@ -141,7 +141,6 @@
   // 'Save-Data: on'.
   bool data_saver_enabled;
   bool local_storage_enabled;
-  bool databases_enabled;
   bool tabs_to_links;
   bool disable_ipc_flooding_protection;
   bool hyperlink_auditing_enabled;
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index 6ea4a0f..202a7b6 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -524,7 +524,7 @@
                                           gfx::Rect&) const = 0;
 
   // Supports commands like Undo, Redo, Cut, Copy, Paste, SelectAll,
-  // Unselect, etc. See EditorCommand.cpp for the full list of supported
+  // Unselect, etc. See editor_command_names.h for the full list of supported
   // commands.
   virtual bool ExecuteCommand(const WebString&) = 0;
   virtual bool ExecuteCommand(const WebString&, const WebString& value) = 0;
diff --git a/third_party/blink/public/web/web_widget.h b/third_party/blink/public/web/web_widget.h
index f4781f2..59e4b3bc 100644
--- a/third_party/blink/public/web/web_widget.h
+++ b/third_party/blink/public/web/web_widget.h
@@ -78,9 +78,7 @@
   virtual void SetCompositorVisible(bool visible) = 0;
 
   // Asks the compositor to request warming up and request a new frame sink
-  // speculatively. This is an experimental function and only used if
-  // `kWarmUpCompositor` is enabled. Please see crbug.com/41496019
-  // for more details.
+  // speculatively even if invisible.
   virtual void WarmUpCompositor() = 0;
 
   // Returns the current size of the WebWidget.
diff --git a/third_party/blink/renderer/core/css/css_gradient_value.cc b/third_party/blink/renderer/core/css/css_gradient_value.cc
index e9a3469..e941c14 100644
--- a/third_party/blink/renderer/core/css/css_gradient_value.cc
+++ b/third_party/blink/renderer/core/css/css_gradient_value.cc
@@ -122,10 +122,9 @@
     }
   }
 
-  // TODO(crbug.com/979895): This is the result of a refactoring, which might
-  // have revealed an existing bug with calculated lengths. Investigate.
-  return !offset_ || offset_->IsMathFunctionValue() ||
-         !To<CSSNumericLiteralValue>(*offset_).IsFontRelativeLength();
+  return !offset_ ||
+         (!offset_->IsMathFunctionValue() &&
+          !To<CSSNumericLiteralValue>(*offset_).IsFontRelativeLength());
 }
 
 void CSSGradientColorStop::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/core/css/css_image_value.cc b/third_party/blink/renderer/core/css/css_image_value.cc
index 52d347ab..cb4b789 100644
--- a/third_party/blink/renderer/core/css/css_image_value.cc
+++ b/third_party/blink/renderer/core/css/css_image_value.cc
@@ -103,11 +103,11 @@
     ImageResourceContent* image_content =
         document.GetStyleEngine().CacheImageContent(params);
     cached_image_ = MakeGarbageCollected<StyleFetchedImage>(
-        image_content, *url_data.MakeResolvedIfDanglingMarkup(document),
-        document,
+        image_content, document,
         params.GetImageRequestBehavior() ==
             FetchParameters::ImageRequestBehavior::kDeferImageLoad,
-        override_image_resolution);
+        url_data.IsFromOriginCleanStyleSheet(), url_data.IsAdRelated(),
+        params.Url(), override_image_resolution);
   }
   return cached_image_.Get();
 }
@@ -167,6 +167,13 @@
   return UrlData().IsLocal(document);
 }
 
+CSSImageValue* CSSImageValue::ComputedCSSValueMaybeLocal() const {
+  if (UrlData().UnresolvedUrl().StartsWith('#')) {
+    return Clone();
+  }
+  return ComputedCSSValue();
+}
+
 AtomicString CSSImageValue::NormalizedFragmentIdentifier() const {
   // Always use KURL's FragmentIdentifier to ensure that we're handling the
   // fragment in a consistent manner.
diff --git a/third_party/blink/renderer/core/css/css_image_value.h b/third_party/blink/renderer/core/css/css_image_value.h
index d2188598f..41fa958b 100644
--- a/third_party/blink/renderer/core/css/css_image_value.h
+++ b/third_party/blink/renderer/core/css/css_image_value.h
@@ -68,9 +68,10 @@
   bool Equals(const CSSImageValue&) const;
 
   CSSImageValue* ComputedCSSValue() const {
-    return MakeGarbageCollected<CSSImageValue>(*UrlData().MakeComputed(),
+    return MakeGarbageCollected<CSSImageValue>(*UrlData().MakeAbsolute(),
                                                cached_image_.Get());
   }
+  CSSImageValue* ComputedCSSValueMaybeLocal() const;
 
   CSSImageValue* Clone() const {
     return MakeGarbageCollected<CSSImageValue>(*UrlData().MakeWithoutReferrer(),
diff --git a/third_party/blink/renderer/core/css/css_math_function_value.cc b/third_party/blink/renderer/core/css/css_math_function_value.cc
index 417aeff6..70019c2 100644
--- a/third_party/blink/renderer/core/css/css_math_function_value.cc
+++ b/third_party/blink/renderer/core/css/css_math_function_value.cc
@@ -70,11 +70,6 @@
   return ClampToPermittedRange(expression_->DoubleValue());
 }
 
-double CSSMathFunctionValue::ComputeDegrees() const {
-  DCHECK_EQ(kCalcAngle, expression_->Category());
-  return ClampToPermittedRange(*expression_->ComputeValueInCanonicalUnit());
-}
-
 double CSSMathFunctionValue::ComputeDegrees(
     const CSSLengthResolver& length_resolver) const {
   DCHECK_EQ(kCalcAngle, expression_->Category());
diff --git a/third_party/blink/renderer/core/css/css_math_function_value.h b/third_party/blink/renderer/core/css/css_math_function_value.h
index 42ef6aa..8c9c9575 100644
--- a/third_party/blink/renderer/core/css/css_math_function_value.h
+++ b/third_party/blink/renderer/core/css/css_math_function_value.h
@@ -77,7 +77,6 @@
   double DoubleValue() const;
 
   double ComputeSeconds(const CSSLengthResolver&) const;
-  double ComputeDegrees() const;
   double ComputeDegrees(const CSSLengthResolver&) const;
   double ComputeLengthPx(const CSSLengthResolver&) const;
   double ComputeDotsPerPixel(const CSSLengthResolver&) const;
diff --git a/third_party/blink/renderer/core/css/css_primitive_value.cc b/third_party/blink/renderer/core/css/css_primitive_value.cc
index 43956cc1..24cd7b48 100644
--- a/third_party/blink/renderer/core/css/css_primitive_value.cc
+++ b/third_party/blink/renderer/core/css/css_primitive_value.cc
@@ -277,15 +277,6 @@
   NOTREACHED();
 }
 
-// TODO(crbug.com/1133390): When we support <frequency>, we must clamp like
-// <time>.
-double CSSPrimitiveValue::ComputeDegrees() const {
-  double result = IsCalculated()
-                      ? To<CSSMathFunctionValue>(this)->ComputeDegrees()
-                      : To<CSSNumericLiteralValue>(this)->ComputeDegrees();
-  return CSSValueClampingUtils::ClampAngle(result);
-}
-
 double CSSPrimitiveValue::ComputeDegrees(
     const CSSLengthResolver& length_resolver) const {
   double result =
diff --git a/third_party/blink/renderer/core/css/css_primitive_value.h b/third_party/blink/renderer/core/css/css_primitive_value.h
index c12eb1e..d8473a6 100644
--- a/third_party/blink/renderer/core/css/css_primitive_value.h
+++ b/third_party/blink/renderer/core/css/css_primitive_value.h
@@ -377,8 +377,6 @@
   // |CSSPrimitiveValue| that's not of any of its subclasses.
   static CSSPrimitiveValue* CreateFromLength(const Length& value, float zoom);
 
-  double ComputeDegrees() const;
-
   double ComputeDegrees(const CSSLengthResolver&) const;
   double ComputeSeconds(const CSSLengthResolver&) const;
   double ComputeDotsPerPixel(const CSSLengthResolver&) const;
diff --git a/third_party/blink/renderer/core/css/css_uri_value_test.cc b/third_party/blink/renderer/core/css/css_uri_value_test.cc
index 563c072..b3efe7f4 100644
--- a/third_party/blink/renderer/core/css/css_uri_value_test.cc
+++ b/third_party/blink/renderer/core/css/css_uri_value_test.cc
@@ -16,7 +16,7 @@
   cssvalue::CSSURIValue* rel = MakeGarbageCollected<cssvalue::CSSURIValue>(
       *MakeGarbageCollected<CSSUrlData>(
           AtomicString("a"), KURL("http://foo.com/a"), Referrer(),
-          /*origin_clean=*/true, /*is_ad_related=*/false));
+          OriginClean::kTrue, /*is_ad_related=*/false));
   cssvalue::CSSURIValue* abs =
       rel->ComputedCSSValue(KURL("http://bar.com"), WTF::TextEncoding());
   EXPECT_EQ("url(\"http://bar.com/a\")", abs->CssText());
@@ -26,7 +26,7 @@
   cssvalue::CSSURIValue* rel = MakeGarbageCollected<cssvalue::CSSURIValue>(
       *MakeGarbageCollected<CSSUrlData>(
           AtomicString("http://baz.com/a"), KURL("http://baz.com/a"),
-          Referrer(), /*origin_clean=*/true, /*is_ad_related=*/false));
+          Referrer(), OriginClean::kTrue, /*is_ad_related=*/false));
   cssvalue::CSSURIValue* abs =
       rel->ComputedCSSValue(KURL("http://bar.com"), WTF::TextEncoding());
   EXPECT_EQ("url(\"http://baz.com/a\")", abs->CssText());
@@ -36,7 +36,7 @@
   cssvalue::CSSURIValue* rel = MakeGarbageCollected<cssvalue::CSSURIValue>(
       *MakeGarbageCollected<CSSUrlData>(
           AtomicString("#a"), KURL("http://baz.com/a"), Referrer(),
-          /*origin_clean=*/true, /*is_ad_related=*/false));
+          OriginClean::kTrue, /*is_ad_related=*/false));
   cssvalue::CSSURIValue* abs =
       rel->ComputedCSSValue(KURL("http://bar.com"), WTF::TextEncoding());
   EXPECT_EQ("url(\"#a\")", abs->CssText());
@@ -45,7 +45,7 @@
 TEST(CSSURIValueTest, EmptyComputedCSSValue) {
   cssvalue::CSSURIValue* rel = MakeGarbageCollected<cssvalue::CSSURIValue>(
       *MakeGarbageCollected<CSSUrlData>(g_empty_atom, KURL(), Referrer(),
-                                        /*origin_clean=*/true,
+                                        OriginClean::kTrue,
                                         /*is_ad_related=*/false));
   cssvalue::CSSURIValue* abs =
       rel->ComputedCSSValue(KURL("http://bar.com"), WTF::TextEncoding());
diff --git a/third_party/blink/renderer/core/css/css_url_data.cc b/third_party/blink/renderer/core/css/css_url_data.cc
index 2efe98f..ad3ae90e 100644
--- a/third_party/blink/renderer/core/css/css_url_data.cc
+++ b/third_party/blink/renderer/core/css/css_url_data.cc
@@ -30,21 +30,21 @@
 CSSUrlData::CSSUrlData(const AtomicString& unresolved_url,
                        const KURL& resolved_url,
                        const Referrer& referrer,
-                       bool is_from_origin_clean_style_sheet,
+                       OriginClean origin_clean,
                        bool is_ad_related)
     : relative_url_(unresolved_url),
       absolute_url_(resolved_url.GetString()),
       referrer_(referrer),
-      is_local_(unresolved_url.StartsWith('#')),
-      is_from_origin_clean_style_sheet_(is_from_origin_clean_style_sheet),
+      is_from_origin_clean_style_sheet_(origin_clean == OriginClean::kTrue),
       is_ad_related_(is_ad_related),
+      is_local_(unresolved_url.StartsWith('#')),
       potentially_dangling_markup_(resolved_url.PotentiallyDanglingMarkup()) {}
 
 CSSUrlData::CSSUrlData(const AtomicString& resolved_url)
     : CSSUrlData(resolved_url,
                  KURL(resolved_url),
                  Referrer(),
-                 /*is_from_origin_clean_style_sheet=*/true,
+                 OriginClean::kTrue,
                  /*is_ad_related=*/false) {}
 
 KURL CSSUrlData::ResolveUrl(const Document& document) const {
@@ -82,13 +82,13 @@
   return true;
 }
 
-const CSSUrlData* CSSUrlData::MakeComputed() const {
-  if (relative_url_.empty() || is_local_ || absolute_url_.empty()) {
+const CSSUrlData* CSSUrlData::MakeAbsolute() const {
+  if (relative_url_.empty()) {
     return this;
   }
-  return MakeGarbageCollected<CSSUrlData>(
-      absolute_url_, KURL(absolute_url_), Referrer(),
-      is_from_origin_clean_style_sheet_, is_ad_related_);
+  return MakeGarbageCollected<CSSUrlData>(absolute_url_, KURL(absolute_url_),
+                                          Referrer(), GetOriginClean(),
+                                          is_ad_related_);
 }
 
 const CSSUrlData* CSSUrlData::MakeResolved(
@@ -101,29 +101,19 @@
                                 ? KURL(base_url, relative_url_, charset)
                                 : KURL(base_url, relative_url_);
   if (is_local_) {
-    return MakeGarbageCollected<CSSUrlData>(
-        relative_url_, resolved_url, Referrer(),
-        is_from_origin_clean_style_sheet_, is_ad_related_);
+    return MakeGarbageCollected<CSSUrlData>(relative_url_, resolved_url,
+                                            Referrer(), GetOriginClean(),
+                                            is_ad_related_);
   }
   return MakeGarbageCollected<CSSUrlData>(
       AtomicString(resolved_url.GetString()), resolved_url, Referrer(),
-      is_from_origin_clean_style_sheet_, is_ad_related_);
-}
-
-const CSSUrlData* CSSUrlData::MakeResolvedIfDanglingMarkup(
-    const Document& document) const {
-  if (!potentially_dangling_markup_) {
-    return this;
-  }
-  return MakeGarbageCollected<CSSUrlData>(
-      relative_url_, ResolveUrl(document), referrer_,
-      is_from_origin_clean_style_sheet_, is_ad_related_);
+      GetOriginClean(), is_ad_related_);
 }
 
 const CSSUrlData* CSSUrlData::MakeWithoutReferrer() const {
-  return MakeGarbageCollected<CSSUrlData>(
-      relative_url_, KURL(absolute_url_), Referrer(),
-      is_from_origin_clean_style_sheet_, is_ad_related_);
+  return MakeGarbageCollected<CSSUrlData>(relative_url_, KURL(absolute_url_),
+                                          Referrer(), GetOriginClean(),
+                                          is_ad_related_);
 }
 
 bool CSSUrlData::IsLocal(const Document& document) const {
diff --git a/third_party/blink/renderer/core/css/css_url_data.h b/third_party/blink/renderer/core/css/css_url_data.h
index db94c0b..1169bbf 100644
--- a/third_party/blink/renderer/core/css/css_url_data.h
+++ b/third_party/blink/renderer/core/css/css_url_data.h
@@ -22,6 +22,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_CSS_CSS_URL_DATA_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/css/css_origin_clean.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/weborigin/referrer.h"
 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
@@ -41,7 +42,7 @@
   CSSUrlData(const AtomicString& unresolved_url,
              const KURL& resolved_url,
              const Referrer&,
-             bool is_from_origin_clean_style_sheet,
+             OriginClean,
              bool is_ad_related);
 
   // Create URL data with a resolved (absolute) URL. Generally used for
@@ -56,19 +57,14 @@
   // Document. Returns true if the resolved URL changed, otherwise false.
   bool ReResolveUrl(const Document&) const;
 
-  // Returns a copy of this URL data suitable for computed value.
-  const CSSUrlData* MakeComputed() const;
+  // Returns an absolutized copy of this URL data (suitable for computed value).
+  const CSSUrlData* MakeAbsolute() const;
 
   // Returns a copy where the unresolved URL has been resolved against
   // `base_url` (using `charset` encoding if valid).
   const CSSUrlData* MakeResolved(const KURL& base_url,
                                  const WTF::TextEncoding& charset) const;
 
-  // Returns a copy with the URL (re)resolved against the base URL of the
-  // document if there's is potential risk of "dangling markup". Otherwise
-  // returns itself.
-  const CSSUrlData* MakeResolvedIfDanglingMarkup(const Document&) const;
-
   // Returns a copy where the referrer has been reset.
   const CSSUrlData* MakeWithoutReferrer() const;
 
@@ -84,6 +80,10 @@
   bool IsFromOriginCleanStyleSheet() const {
     return is_from_origin_clean_style_sheet_;
   }
+  OriginClean GetOriginClean() const {
+    return is_from_origin_clean_style_sheet_ ? OriginClean::kTrue
+                                             : OriginClean::kFalse;
+  }
   bool IsAdRelated() const { return is_ad_related_; }
 
   // Returns true if this URL is "local" to the specified Document (either by
@@ -101,9 +101,6 @@
   mutable AtomicString absolute_url_;
   const Referrer referrer_;
 
-  // The 'local url flag': https://drafts.csswg.org/css-values/#local-urls
-  const bool is_local_;
-
   // Whether the stylesheet that requested this image is origin-clean:
   // https://drafts.csswg.org/cssom-1/#concept-css-style-sheet-origin-clean-flag
   const bool is_from_origin_clean_style_sheet_;
@@ -111,6 +108,8 @@
   // Whether this was created by an ad-related CSSParserContext.
   const bool is_ad_related_;
 
+  const bool is_local_;
+
   // The url passed into the constructor had the PotentiallyDanglingMarkup flag
   // set. That information needs to be passed on to the fetch code to block such
   // resources from loading.
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
index 310706bb..a12c93d5 100644
--- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
+++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -1670,7 +1670,9 @@
   AtomicString url_string = url.ToAtomicString();
   return MakeGarbageCollected<CSSUrlData>(
       url_string, context.CompleteNonEmptyURL(url_string),
-      context.GetReferrer(), context.IsOriginClean(), context.IsAdRelated());
+      context.GetReferrer(),
+      context.IsOriginClean() ? OriginClean::kTrue : OriginClean::kFalse,
+      context.IsAdRelated());
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/core/css/selector_checker-inl.h b/third_party/blink/renderer/core/css/selector_checker-inl.h
index 72c22651..04e9f40 100644
--- a/third_party/blink/renderer/core/css/selector_checker-inl.h
+++ b/third_party/blink/renderer/core/css/selector_checker-inl.h
@@ -163,6 +163,16 @@
              tag_q_name.NamespaceURI() == g_star_atom;
     }
     case CSSSelector::kClass:
+      if (!element->CouldHaveClass(selector->Value())) {
+#if DCHECK_IS_ON()
+        DCHECK(!element->HasClass() ||
+               !element->ClassNames().Contains(selector->Value()))
+            << element << " should have matched class " << selector->Value()
+            << ", Bloom bits on element are "
+            << element->AttributeOrClassBloomFilterForDebug();
+#endif
+        return false;
+      }
       return element->HasClass() &&
              element->ClassNames().Contains(selector->Value());
     case CSSSelector::kId:
@@ -221,7 +231,7 @@
       DCHECK(element.CouldHaveAttribute(attr))
           << element << " should have contained attribute " << attr
           << ", Bloom bits on element are "
-          << element.AttributeBloomFilterForDebug();
+          << element.AttributeOrClassBloomFilterForDebug();
 #endif
       return attribute_item.Value() == value ||
              (case_insensitive &&
diff --git a/third_party/blink/renderer/core/css/selector_checker.cc b/third_party/blink/renderer/core/css/selector_checker.cc
index 5f9c9e4..d7270263 100644
--- a/third_party/blink/renderer/core/css/selector_checker.cc
+++ b/third_party/blink/renderer/core/css/selector_checker.cc
@@ -906,7 +906,7 @@
     DCHECK(element.CouldHaveAttribute(selector_attr))
         << element << " should have contained attribute " << selector_attr
         << ", Bloom bits on element are "
-        << element.AttributeBloomFilterForDebug();
+        << element.AttributeOrClassBloomFilterForDebug();
 #endif
 
     if (AttributeValueMatches(attribute_item, match, selector_value,
@@ -960,6 +960,16 @@
     case CSSSelector::kUniversalTag:
       return MatchesUniversalTagName(element, selector.TagQName());
     case CSSSelector::kClass:
+      if (!element.CouldHaveClass(selector.Value())) {
+#if DCHECK_IS_ON()
+        DCHECK(!element.HasClass() ||
+               !element.ClassNames().Contains(selector.Value()))
+            << element << " should have matched class " << selector.Value()
+            << ", Bloom bits on element are "
+            << element.AttributeOrClassBloomFilterForDebug();
+#endif
+        return false;
+      }
       return element.HasClass() &&
              element.ClassNames().Contains(selector.Value());
     case CSSSelector::kId:
diff --git a/third_party/blink/renderer/core/css/selector_query.cc b/third_party/blink/renderer/core/css/selector_query.cc
index 741ec2c..ad591f0 100644
--- a/third_party/blink/renderer/core/css/selector_query.cc
+++ b/third_party/blink/renderer/core/css/selector_query.cc
@@ -160,9 +160,20 @@
     const AtomicString& class_name,
     const CSSSelector* selector,
     typename SelectorQueryTrait::OutputType& output) {
+  const Element::TinyBloomFilter filter = Element::FilterForString(class_name);
+
   SelectorChecker checker(SelectorChecker::kQueryingRules);
   for (Element& element : ElementTraversal::DescendantsOf(root_node)) {
     QUERY_STATS_INCREMENT(fast_class);
+    if (!element.CouldHaveClassWithPrecomputedFilter(filter)) {
+#if DCHECK_IS_ON()
+      DCHECK(!element.HasClassName(class_name))
+          << element << " should have contained class " << class_name
+          << ", Bloom bits on element are "
+          << element.AttributeOrClassBloomFilterForDebug();
+#endif
+      continue;
+    }
     if (!element.HasClassName(class_name)) {
       continue;
     }
@@ -263,7 +274,8 @@
   const bool needs_synchronize_attribute =
       NeedsSynchronizeAttribute(selector_attr, is_html_doc);
 
-  const uint32_t filter = Element::FilterForAttribute(selector_attr);
+  const Element::TinyBloomFilter filter =
+      Element::FilterForAttribute(selector_attr);
 
   for (Element& element : ElementTraversal::DescendantsOf(root_node)) {
     QUERY_STATS_INCREMENT(fast_scan);
@@ -308,7 +320,7 @@
       DCHECK(element.CouldHaveAttributeWithPrecomputedFilter(filter))
           << element << " should have contained attribute " << selector_attr
           << ", Bloom bits on element are "
-          << element.AttributeBloomFilterForDebug();
+          << element.AttributeOrClassBloomFilterForDebug();
 #endif
 
       if (AttributeValueMatchesExact(attribute_item, selector_value,
diff --git a/third_party/blink/renderer/core/dom/container_node.cc b/third_party/blink/renderer/core/dom/container_node.cc
index e0e8252..c898ab2 100644
--- a/third_party/blink/renderer/core/dom/container_node.cc
+++ b/third_party/blink/renderer/core/dom/container_node.cc
@@ -557,7 +557,7 @@
     DCHECK(firstChild() == next_child);
     SetFirstChild(&new_child);
   }
-  new_child.SetParentOrShadowHostNode(this);
+  new_child.SetParentNode(this);
   new_child.SetPreviousSibling(prev);
   new_child.SetNextSibling(&next_child);
 }
@@ -568,7 +568,7 @@
 #endif
   DCHECK(ScriptForbiddenScope::IsScriptForbidden());
 
-  child.SetParentOrShadowHostNode(this);
+  child.SetParentNode(this);
   if (last_child_) {
     child.SetPreviousSibling(last_child_);
     last_child_->SetNextSibling(&child);
@@ -1031,7 +1031,7 @@
 
   old_child.SetPreviousSibling(nullptr);
   old_child.SetNextSibling(nullptr);
-  old_child.SetParentOrShadowHostNode(nullptr);
+  old_child.SetParentNode(nullptr);
 
   GetDocument().AdoptIfNeeded(old_child);
 }
diff --git a/third_party/blink/renderer/core/dom/document_fragment.cc b/third_party/blink/renderer/core/dom/document_fragment.cc
index c548af0e..d6d0575 100644
--- a/third_party/blink/renderer/core/dom/document_fragment.cc
+++ b/third_party/blink/renderer/core/dom/document_fragment.cc
@@ -113,7 +113,7 @@
   Node* next_child = firstChild();
   do {
     Node* child = next_child;
-    child->SetParentOrShadowHostNode(nullptr);
+    child->SetParentNode(nullptr);
     child->SetPreviousSibling(nullptr);
     next_child = child->nextSibling();
     child->SetNextSibling(nullptr);
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index faceadb6..780a330 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -1283,7 +1283,8 @@
   // be in non-ancestor shadow trees. We don't want to leak references into
   // those scopes, so retarget the elements.
   if (RuntimeEnabledFeatures::ShadowRootReferenceTargetEnabled(
-          GetExecutionContext()) && elements) {
+          GetExecutionContext()) &&
+      elements) {
     std::transform(elements->begin(), elements->end(), elements->begin(),
                    [this](Element* element) {
                      return &this->GetTreeScope().Retarget(*element);
@@ -3283,6 +3284,9 @@
     GetElementData()->SetClass(new_class_string);
   }
   const SpaceSplitString& new_classes = GetElementData()->ClassNames();
+  for (const AtomicString& class_name : new_classes) {
+    attribute_or_class_bloom_ |= FilterForString(class_name);
+  }
   GetDocument().GetStyleEngine().ClassChangedForElement(old_classes,
                                                         new_classes, *this);
 }
@@ -3354,9 +3358,11 @@
           ShareableElementData::CreateWithAttributes(attribute_vector);
     }
 
-    attribute_bloom_ = 0;
+    // NOTE: AttributeChanged() will add back the class names (if any),
+    // so it is safe to reset the filter here.
+    attribute_or_class_bloom_ = 0;
     for (const Attribute& attribute : attribute_vector) {
-      attribute_bloom_ |= FilterForAttribute(attribute.GetName());
+      attribute_or_class_bloom_ |= FilterForAttribute(attribute.GetName());
     }
   }
 
@@ -5418,7 +5424,7 @@
     }
   }
   EnsureElementRareData().SetShadowRoot(*shadow_root);
-  shadow_root->SetParentOrShadowHostNode(this);
+  shadow_root->SetShadowHostNode(this);
   shadow_root->SetParentTreeScope(GetTreeScope());
   shadow_root->InsertedInto(*this);
 
@@ -6681,7 +6687,7 @@
     DidRemoveAttribute(name, value_being_removed);
   }
 
-  // TODO(sesse): Consider recalculating attribute_bloom_ filter here,
+  // TODO(sesse): Consider recalculating attribute_or_class_bloom_ filter here,
   // so that it reflects the removal (but beware of pathological cases
   // where removing all attributes send us into O(n²)).
 }
@@ -6689,7 +6695,7 @@
 void Element::AppendAttributeInternal(const QualifiedName& name,
                                       const AtomicString& value,
                                       AttributeModificationReason reason) {
-  attribute_bloom_ |= FilterForAttribute(name);
+  attribute_or_class_bloom_ |= FilterForAttribute(name);
 
   if (reason !=
       AttributeModificationReason::kBySynchronizationOfLazyAttribute) {
@@ -10198,7 +10204,7 @@
     setNonce(other.nonce());
   }
 
-  attribute_bloom_ = other.attribute_bloom_;
+  attribute_or_class_bloom_ = other.attribute_or_class_bloom_;
 }
 
 void Element::CreateUniqueElementData() {
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 7a73542..be85231 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -750,13 +750,21 @@
     return CouldHaveAttributeWithPrecomputedFilter(
         FilterForAttribute(attribute_name));
   }
+  bool CouldHaveClass(const AtomicString& class_name) const {
+    return CouldHaveClassWithPrecomputedFilter(FilterForString(class_name));
+  }
 
-  // A variant of CouldHaveAttribute() that allows you to compute
+  // A variant of CouldHave{Attribute,Class}() that allows you to compute
   // the filter ahead-of-time; useful if you want to test many elements
-  // against the same attribute name.
-  static uint32_t FilterForAttribute(const QualifiedName& attribute_name) {
-    unsigned hash = attribute_name.LocalNameUpper().Hash();
-    uint32_t filter = 0;
+  // against the same attribute/class name.
+  using TinyBloomFilter = uint32_t;
+  static TinyBloomFilter FilterForAttribute(
+      const QualifiedName& attribute_name) {
+    return FilterForString(attribute_name.LocalNameUpper());
+  }
+  static TinyBloomFilter FilterForString(const AtomicString& str) {
+    unsigned hash = str.Hash();
+    TinyBloomFilter filter = 0;
     // Build a 32-bit Bloom filter, with k=2. We extract the two
     // (5-bit) hashes that we need from non-overlapping parts of the
     // (24-bit) String hash, which should be independent.
@@ -764,11 +772,16 @@
     filter |= 1u << ((hash >> 5) & 31);
     return filter;
   }
-  bool CouldHaveAttributeWithPrecomputedFilter(uint32_t filter) const {
-    return (attribute_bloom_ & filter) == filter;
+  bool CouldHaveAttributeWithPrecomputedFilter(TinyBloomFilter filter) const {
+    return (attribute_or_class_bloom_ & filter) == filter;
+  }
+  bool CouldHaveClassWithPrecomputedFilter(TinyBloomFilter filter) const {
+    return (attribute_or_class_bloom_ & filter) == filter;
   }
 #if DCHECK_IS_ON()
-  uint32_t AttributeBloomFilterForDebug() const { return attribute_bloom_; }
+  TinyBloomFilter AttributeOrClassBloomFilterForDebug() const {
+    return attribute_or_class_bloom_;
+  }
 #endif
 
   // Step 5 of https://dom.spec.whatwg.org/#concept-node-clone
@@ -2238,11 +2251,12 @@
   subtle::UncompressedMember<const ComputedStyle> computed_style_;
   Member<ElementData> element_data_;
 
-  // A tiny Bloom filter for which attribute names we have; saves going to
-  // ElementData if the attribute doesn't exist. May have false positives,
-  // of course. We do not currently update this when attributes are removed,
-  // only when they are added. Attribute _values_ are not part of this filter.
-  uint32_t attribute_bloom_ = 0;
+  // A tiny Bloom filter for which attribute names and class names we have;
+  // saves going to ElementData if the attribute/class doesn't exist. May have
+  // false positives, of course. We do not currently update this when
+  // attributes/classes are removed, only when they are added. Attribute
+  // _values_ are not part of this filter, except for the values of class="".
+  uint32_t attribute_or_class_bloom_ = 0;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc
index 36b22659..66009173 100644
--- a/third_party/blink/renderer/core/dom/node.cc
+++ b/third_party/blink/renderer/core/dom/node.cc
@@ -325,8 +325,8 @@
 
 Node::Node(TreeScope* tree_scope, ConstructionType type)
     : node_flags_(type),
-      parent_or_shadow_host_node_(nullptr),
       tree_scope_(tree_scope),
+      parent_or_shadow_host_node_(kParentNodeTag, nullptr),
       previous_(nullptr),
       next_(nullptr),
       layout_object_(nullptr),
@@ -3713,8 +3713,8 @@
 }
 
 void Node::Trace(Visitor* visitor) const {
-  visitor->Trace(parent_or_shadow_host_node_);
   visitor->Trace(tree_scope_);
+  visitor->Trace(parent_or_shadow_host_node_);
   visitor->Trace(previous_);
   visitor->Trace(next_);
   visitor->Trace(layout_object_);
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h
index e8061e4..0ba574f 100644
--- a/third_party/blink/renderer/core/dom/node.h
+++ b/third_party/blink/renderer/core/dom/node.h
@@ -215,7 +215,8 @@
   virtual void setNodeValue(const String&,
                             ExceptionState& = ASSERT_NO_EXCEPTION);
   ContainerNode* parentNode() const {
-    return IsShadowRoot() ? nullptr : ParentOrShadowHostNode();
+    return reinterpret_cast<ContainerNode*>(
+        parent_or_shadow_host_node_.TryGetAs<ParentNodeTag>());
   }
 
   Element* parentElement() const;
@@ -456,7 +457,13 @@
 
   bool IsDocumentNode() const;
   bool IsTreeScope() const;
-  bool IsShadowRoot() const { return IsDocumentFragment() && IsTreeScope(); }
+  bool IsShadowRoot() const {
+    const bool result = parent_or_shadow_host_node_.Is<ShadowHostTag>();
+#if DCHECK_IS_ON()
+    DCHECK(!result || (IsDocumentFragment() && IsTreeScope()));
+#endif
+    return result;
+  }
 
   bool IsActiveSlot() const;
   bool IsSlotable() const { return IsTextNode() || IsElementNode(); }
@@ -484,7 +491,8 @@
   // Node's parent, shadow tree host.
   ContainerNode* ParentOrShadowHostNode() const;
   Element* ParentOrShadowHostElement() const;
-  void SetParentOrShadowHostNode(ContainerNode*);
+  void SetParentNode(ContainerNode*);
+  void SetShadowHostNode(ContainerNode*);
 
   // Knows about all kinds of hosts.
   ContainerNode* ParentOrShadowHostOrTemplateHostNode() const;
@@ -590,13 +598,15 @@
   // a micro-benchmark regression (https://crbug.com/926343).
   void SetStyleChangeOnInsertion() {
     DCHECK(isConnected());
-    if (ShouldSkipMarkingStyleDirty())
+    if (ShouldSkipMarkingStyleDirty()) {
       return;
+    }
     if (InvalidationTracingFlag::IsEnabled()) [[unlikely]] {
       MaybeAddNodeInsertedTraceEvent();
     }
-    if (!NeedsStyleRecalc())
+    if (!NeedsStyleRecalc()) {
       SetStyleChange(kLocalStyleChange);
+    }
     MarkAncestorsWithChildNeedsStyleRecalc();
   }
 
@@ -607,8 +617,9 @@
     DCHECK(IsElementNode());
     DCHECK(isConnected());
     DCHECK(parentElement() && !GetStyleRecalcParent());
-    if (!NeedsStyleRecalc())
+    if (!NeedsStyleRecalc()) {
       SetStyleChange(kLocalStyleChange);
+    }
   }
 
   bool NeedsReattachLayoutTree() const {
@@ -1246,6 +1257,14 @@
   void InvalidateIfHasEffectiveAppearance() const;
 
  private:
+  static constexpr struct ParentNodeTag {
+  } kParentNodeTag;
+  static constexpr struct ShadowHostTag {
+  } kShadowHostTag;
+
+  using TaggedParentOrShadowHostNode =
+      subtle::TaggedUncompressedMember<Node, ParentNodeTag, ShadowHostTag>;
+
   Node* ToNode() final;
 
   bool IsUserActionElementActive() const;
@@ -1274,10 +1293,10 @@
   // EventTarget ends with a single 32-bit member, so put one 32-bit member
   // first to avoid padding on 64-bit.
   uint32_t node_flags_;
-  // Both parent and tree_scope are hot accessed members. Keep them uncompressed
+  // Both tree_scope and parent are hot accessed members. Keep them uncompressed
   // for performance reasons.
-  subtle::UncompressedMember<Node> parent_or_shadow_host_node_;
   subtle::UncompressedMember<TreeScope> tree_scope_;
+  TaggedParentOrShadowHostNode parent_or_shadow_host_node_;
   // Compressed members and flags are after uncompressed members to minimize
   // padding.
   Member<Node> previous_;
@@ -1286,14 +1305,22 @@
   Member<NodeRareData> data_;
 };
 
-inline void Node::SetParentOrShadowHostNode(ContainerNode* parent) {
+inline void Node::SetParentNode(ContainerNode* parent) {
   DCHECK(IsMainThread());
-  parent_or_shadow_host_node_ = reinterpret_cast<Node*>(parent);
+  parent_or_shadow_host_node_.SetAs<ParentNodeTag>(
+      reinterpret_cast<Node*>(parent));
+}
+
+inline void Node::SetShadowHostNode(ContainerNode* shadow_host) {
+  DCHECK(IsMainThread());
+  parent_or_shadow_host_node_.SetAs<ShadowHostTag>(
+      reinterpret_cast<Node*>(shadow_host));
 }
 
 inline ContainerNode* Node::ParentOrShadowHostNode() const {
   DCHECK(IsMainThread());
-  return reinterpret_cast<ContainerNode*>(parent_or_shadow_host_node_.Get());
+  return reinterpret_cast<ContainerNode*>(
+      parent_or_shadow_host_node_.GetUntagged());
 }
 
 // Allow equality comparisons of Nodes by reference or pointer, interchangeably.
diff --git a/third_party/blink/renderer/core/dom/pseudo_element.cc b/third_party/blink/renderer/core/dom/pseudo_element.cc
index 87cfbc9e..fbb127f7 100644
--- a/third_party/blink/renderer/core/dom/pseudo_element.cc
+++ b/third_party/blink/renderer/core/dom/pseudo_element.cc
@@ -285,7 +285,7 @@
       view_transition_name_(view_transition_name) {
   DCHECK_NE(pseudo_id, kPseudoIdNone);
   parent->GetTreeScope().AdoptIfNeeded(*this);
-  SetParentOrShadowHostNode(parent);
+  SetParentNode(parent);
   SetHasCustomStyleCallbacks();
   if ((pseudo_id == kPseudoIdBefore || pseudo_id == kPseudoIdAfter) &&
       parent->HasTagName(html_names::kInputTag)) {
@@ -395,7 +395,7 @@
   DetachLayoutTree();
   Element* parent = ParentOrShadowHostElement();
   GetDocument().AdoptIfNeeded(*this);
-  SetParentOrShadowHostNode(nullptr);
+  SetParentNode(nullptr);
   RemovedFrom(*parent);
 }
 
diff --git a/third_party/blink/renderer/core/editing/local_caret_rect_test.cc b/third_party/blink/renderer/core/editing/local_caret_rect_test.cc
index d6f1e7a5..0e9abf1 100644
--- a/third_party/blink/renderer/core/editing/local_caret_rect_test.cc
+++ b/third_party/blink/renderer/core/editing/local_caret_rect_test.cc
@@ -779,18 +779,25 @@
   Position position4 = RuntimeEnabledFeatures::TextareaLineEndingsAsBrEnabled()
                            ? Position(br_in_2nd_line, 0)
                            : Position(inner_text, 4);
-  EXPECT_EQ(LocalCaretRect(position4.AnchorNode()->GetLayoutObject(),
-                           PhysicalRect(0, 10, 1, 10)),
-            LocalCaretRectOfPosition(
-                PositionWithAffinity(position4, TextAffinity::kDownstream)));
+  PhysicalRect local_rect4 =
+      RuntimeEnabledFeatures::TextareaMultipleIfcsEnabled()
+          ? PhysicalRect(0, 0, 1, 10)
+          : PhysicalRect(0, 10, 1, 10);
+  EXPECT_EQ(
+      LocalCaretRect(position4.AnchorNode()->GetLayoutObject(), local_rect4),
+      LocalCaretRectOfPosition(
+          PositionWithAffinity(position4, TextAffinity::kDownstream)));
 
   // Test the third line.
   const Node* placeholder_br = textarea->InnerEditorElement()->lastChild();
   Position position5 = RuntimeEnabledFeatures::TextareaLineEndingsAsBrEnabled()
                            ? Position(placeholder_br, 0)
                            : Position(inner_text, 5);
-  EXPECT_EQ(LocalCaretRect(placeholder_br->GetLayoutObject(),
-                           PhysicalRect(0, 20, 1, 10)),
+  PhysicalRect local_rect5 =
+      RuntimeEnabledFeatures::TextareaMultipleIfcsEnabled()
+          ? PhysicalRect(0, 0, 1, 10)
+          : PhysicalRect(0, 20, 1, 10);
+  EXPECT_EQ(LocalCaretRect(placeholder_br->GetLayoutObject(), local_rect5),
             LocalCaretRectOfPosition(
                 PositionWithAffinity(position5, TextAffinity::kDownstream)));
 }
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
index 8fbe8a7b..4d1a925 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.cc
@@ -544,43 +544,6 @@
 }
 
 // static
-void ContentSecurityPolicy::FillInCSPHashValues(
-    const String& source,
-    WTF::HashSet<IntegrityAlgorithm> hash_algorithms_used,
-    Vector<network::mojom::blink::CSPHashSourcePtr>& csp_hash_values) {
-  // Any additions or subtractions from this struct should also modify the
-  // respective entries in the kSupportedPrefixes array in
-  // SourceListDirective::parseHash().
-  static const struct {
-    IntegrityAlgorithm csp_hash_algorithm;
-    HashAlgorithm algorithm;
-  } kAlgorithmMap[] = {{IntegrityAlgorithm::kSha256, kHashAlgorithmSha256},
-                       {IntegrityAlgorithm::kSha384, kHashAlgorithmSha384},
-                       {IntegrityAlgorithm::kSha512, kHashAlgorithmSha512}};
-
-  // Only bother normalizing the source/computing digests if there are any
-  // checks to be done.
-  if (hash_algorithms_used.empty()) {
-    return;
-  }
-
-  StringUTF8Adaptor utf8_source(source,
-                                Utf8ConversionMode::kStrictReplacingErrors);
-
-  for (const auto& algorithm_map : kAlgorithmMap) {
-    DigestValue digest;
-    if (hash_algorithms_used.Contains(algorithm_map.csp_hash_algorithm)) {
-      bool digest_success = ComputeDigest(
-          algorithm_map.algorithm, base::as_byte_span(utf8_source), digest);
-      if (digest_success) {
-        csp_hash_values.push_back(network::mojom::blink::CSPHashSource::New(
-            algorithm_map.csp_hash_algorithm, Vector<uint8_t>(digest)));
-      }
-    }
-  }
-}
-
-// static
 bool ContentSecurityPolicy::CheckHashAgainstPolicy(
     Vector<network::mojom::blink::CSPHashSourcePtr>& csp_hash_values,
     const network::mojom::blink::ContentSecurityPolicy& csp,
diff --git a/third_party/blink/renderer/core/frame/csp/content_security_policy.h b/third_party/blink/renderer/core/frame/csp/content_security_policy.h
index 5b50c33..2bd49c33 100644
--- a/third_party/blink/renderer/core/frame/csp/content_security_policy.h
+++ b/third_party/blink/renderer/core/frame/csp/content_security_policy.h
@@ -458,11 +458,6 @@
                        const IntegrityMetadataSet& = IntegrityMetadataSet(),
                        ParserDisposition = kParserInserted);
 
-  static void FillInCSPHashValues(
-      const String& source,
-      WTF::HashSet<IntegrityAlgorithm> hash_algorithms_used,
-      Vector<network::mojom::blink::CSPHashSourcePtr>& csp_hash_values);
-
   // checks a vector of csp hashes against policy, probably a good idea
   // to use in tandem with FillInCSPHashValues.
   static bool CheckHashAgainstPolicy(
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
index b92e4cc..0285c93f 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
+++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.cc
@@ -1034,4 +1034,40 @@
   return OperativeDirective(csp, type);
 }
 
+void FillInCSPHashValues(
+    const String& source,
+    const WTF::HashSet<IntegrityAlgorithm>& hash_algorithms_used,
+    Vector<network::mojom::blink::CSPHashSourcePtr>& csp_hash_values) {
+  // Any additions or subtractions from this struct should also modify the
+  // respective entries in the kSupportedPrefixes array in
+  // SourceListDirective::parseHash().
+  static const struct {
+    IntegrityAlgorithm csp_hash_algorithm;
+    HashAlgorithm algorithm;
+  } kAlgorithmMap[] = {{IntegrityAlgorithm::kSha256, kHashAlgorithmSha256},
+                       {IntegrityAlgorithm::kSha384, kHashAlgorithmSha384},
+                       {IntegrityAlgorithm::kSha512, kHashAlgorithmSha512}};
+
+  // Only bother normalizing the source/computing digests if there are any
+  // checks to be done.
+  if (hash_algorithms_used.empty()) {
+    return;
+  }
+
+  StringUTF8Adaptor utf8_source(source,
+                                Utf8ConversionMode::kStrictReplacingErrors);
+
+  for (const auto& algorithm_map : kAlgorithmMap) {
+    DigestValue digest;
+    if (hash_algorithms_used.Contains(algorithm_map.csp_hash_algorithm)) {
+      bool digest_success = ComputeDigest(
+          algorithm_map.algorithm, base::as_byte_span(utf8_source), digest);
+      if (digest_success) {
+        csp_hash_values.push_back(network::mojom::blink::CSPHashSource::New(
+            algorithm_map.csp_hash_algorithm, Vector<uint8_t>(digest)));
+      }
+    }
+  }
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/csp/csp_directive_list.h b/third_party/blink/renderer/core/frame/csp/csp_directive_list.h
index 26adc510..6f38ffd 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_directive_list.h
+++ b/third_party/blink/renderer/core/frame/csp/csp_directive_list.h
@@ -162,6 +162,11 @@
     const network::mojom::blink::ContentSecurityPolicy& csp,
     CSPDirectiveName type);
 
+void FillInCSPHashValues(
+    const String& source,
+    const WTF::HashSet<IntegrityAlgorithm>& hash_algorithms_used,
+    Vector<network::mojom::blink::CSPHashSourcePtr>& csp_hash_values);
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_CSP_CSP_DIRECTIVE_LIST_H_
diff --git a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
index 479dd8a7..ffad482 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_client_impl.cc
@@ -400,8 +400,6 @@
 void LocalFrameClientImpl::DispatchDidDispatchDOMContentLoadedEvent() {
   if (web_frame_->Client())
     web_frame_->Client()->DidDispatchDOMContentLoadedEvent();
-
-  web_frame_->DidDispatchDOMContentLoadedEvent();
 }
 
 void LocalFrameClientImpl::DispatchDidLoadResourceFromMemoryCache(
diff --git a/third_party/blink/renderer/core/frame/remote_frame.cc b/third_party/blink/renderer/core/frame/remote_frame.cc
index b5b166c..96b0b13d 100644
--- a/third_party/blink/renderer/core/frame/remote_frame.cc
+++ b/third_party/blink/renderer/core/frame/remote_frame.cc
@@ -212,7 +212,8 @@
       frame_request.GetResourceRequest(), fetch_client_settings_object, window,
       frame_request.GetFrameType(),
       window->GetFrame() ? window->GetFrame()->GetContentSettingsClient()
-                         : nullptr);
+                         : nullptr,
+      window->GetFrame());
 
   if (NavigationShouldReplaceCurrentHistoryEntry(frame_load_type))
     frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
index 61fd7ff4..b9adaba 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.cc
@@ -2597,48 +2597,19 @@
   return GetFrame()->GetPage()->GetChromeClient().GetWebView();
 }
 
-bool WebLocalFrameImpl::ShouldWarmUpCompositorOnPrerenderFromThisPoint(
-    features::Prerender2WarmUpCompositorTriggerPoint trigger_point) {
-  static const bool is_warm_up_compositor_enabled =
-      base::FeatureList::IsEnabled(::features::kWarmUpCompositor);
-  if (!is_warm_up_compositor_enabled) {
-    return false;
-  }
-
+bool WebLocalFrameImpl::ShouldWarmUpCompositor() {
   if (!GetFrame()->IsOutermostMainFrame()) {
     return false;
   }
 
-  if (!GetFrame()->GetPage() || !GetFrame()->GetPage()->IsPrerendering() ||
-      !GetFrame()->GetPage()->ShouldWarmUpCompositorOnPrerender()) {
-    return false;
-  }
-
-  static const bool is_prerender2_warm_up_compositor_enabled =
-      base::FeatureList::IsEnabled(features::kPrerender2WarmUpCompositor);
-  // TODO(crbug.com/41496019): Seek the best point to start warm-up.
-  static const auto prerender2_warm_up_compositor_trigger_point =
-      features::kPrerender2WarmUpCompositorTriggerPoint.Get();
-  if (!is_prerender2_warm_up_compositor_enabled ||
-      prerender2_warm_up_compositor_trigger_point != trigger_point) {
-    return false;
-  }
-
-  return true;
+  // It can be effective for prerendering pages to consider warming up their
+  // composers before they are activated and visible.
+  return GetFrame()->GetPage() && GetFrame()->GetPage()->IsPrerendering() &&
+         GetFrame()->GetPage()->ShouldWarmUpCompositorOnPrerender();
 }
 
 void WebLocalFrameImpl::DidCommitLoad() {
-  if (frame_widget_ &&
-      ShouldWarmUpCompositorOnPrerenderFromThisPoint(
-          features::Prerender2WarmUpCompositorTriggerPoint::kDidCommitLoad)) {
-    frame_widget_->WarmUpCompositor();
-  }
-}
-
-void WebLocalFrameImpl::DidDispatchDOMContentLoadedEvent() {
-  if (frame_widget_ && ShouldWarmUpCompositorOnPrerenderFromThisPoint(
-                           features::Prerender2WarmUpCompositorTriggerPoint::
-                               kDidDispatchDOMContentLoadedEvent)) {
+  if (frame_widget_ && ShouldWarmUpCompositor()) {
     frame_widget_->WarmUpCompositor();
   }
 }
@@ -2657,12 +2628,6 @@
   if (!Client())
     return;
 
-  if (frame_widget_ &&
-      ShouldWarmUpCompositorOnPrerenderFromThisPoint(
-          features::Prerender2WarmUpCompositorTriggerPoint::kDidFinishLoad)) {
-    frame_widget_->WarmUpCompositor();
-  }
-
   if (WebPluginContainerImpl* plugin = GetFrame()->GetWebPluginContainer())
     plugin->DidFinishLoading();
 
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
index 87d933b..7ec474c 100644
--- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h
+++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -529,7 +529,6 @@
   void SetFindEndstateFocusAndSelection();
 
   void DidCommitLoad();
-  void DidDispatchDOMContentLoadedEvent();
   void DidFailLoad(const ResourceError&, WebHistoryCommitType);
   void DidFinish();
   void DidFinishLoadForPrinting();
@@ -663,10 +662,8 @@
   mojom::blink::BackForwardCacheNotRestoredReasonsPtr ConvertNotRestoredReasons(
       const mojom::BackForwardCacheNotRestoredReasonsPtr& reasons_struct);
 
-  // If true, requests compositor warm-up when the page is under prerendering.
-  // Please see crbug.com/41496019 for more details.
-  bool ShouldWarmUpCompositorOnPrerenderFromThisPoint(
-      features::Prerender2WarmUpCompositorTriggerPoint trigger_point);
+  // Returns whether we should perform compositor warm-up.
+  bool ShouldWarmUpCompositor();
 
   WebLocalFrameClient* client_;
 
diff --git a/third_party/blink/renderer/core/html/forms/html_input_element.cc b/third_party/blink/renderer/core/html/forms/html_input_element.cc
index b86614ad..d5ede9a 100644
--- a/third_party/blink/renderer/core/html/forms/html_input_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_input_element.cc
@@ -2477,7 +2477,8 @@
 }
 
 bool HTMLInputElement::IsFirstTextInputInAncestorSelect() const {
-  if (!RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() ||
+  if ((!RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() &&
+       !RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) ||
       !first_ancestor_select_) {
     return false;
   }
@@ -2485,7 +2486,8 @@
 }
 
 HTMLSelectElement* HTMLInputElement::FirstAncestorSelectElement() const {
-  if (!RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+  if (!RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() &&
+      !RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) {
     return nullptr;
   }
   return first_ancestor_select_;
diff --git a/third_party/blink/renderer/core/html/forms/html_option_element.cc b/third_party/blink/renderer/core/html/forms/html_option_element.cc
index 3aa6f80..b9ef112 100644
--- a/third_party/blink/renderer/core/html/forms/html_option_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_option_element.cc
@@ -730,8 +730,10 @@
       if (key == keywords::kArrowUp) {
         if (auto* previous_option = options.PreviousFocusableOption(*this)) {
           previous_option->Focus(focus_params);
-        } else if (RuntimeEnabledFeatures::
-                       SelectAccessibilityReparentInputEnabled() &&
+        } else if ((RuntimeEnabledFeatures::
+                        SelectAccessibilityReparentInputEnabled() ||
+                    RuntimeEnabledFeatures::
+                        SelectAccessibilityNestedInputEnabled()) &&
                    select->FirstDescendantTextInput()) {
           select->FirstDescendantTextInput()->Focus(focus_params);
         }
diff --git a/third_party/blink/renderer/core/html/forms/html_select_element.cc b/third_party/blink/renderer/core/html/forms/html_select_element.cc
index 4ffde71d..845218f 100644
--- a/third_party/blink/renderer/core/html/forms/html_select_element.cc
+++ b/third_party/blink/renderer/core/html/forms/html_select_element.cc
@@ -134,8 +134,10 @@
         if (record->attributeName() == html_names::kTabindexAttr ||
             record->attributeName() == html_names::kContenteditableAttr) {
           AddDescendantDisallowedErrorToNode(*record->target());
-        } else if (RuntimeEnabledFeatures::
-                       SelectAccessibilityReparentInputEnabled() &&
+        } else if ((RuntimeEnabledFeatures::
+                        SelectAccessibilityReparentInputEnabled() ||
+                    RuntimeEnabledFeatures::
+                        SelectAccessibilityNestedInputEnabled()) &&
                    record->attributeName() == html_names::kTypeAttr) {
           if (auto* input = DynamicTo<HTMLInputElement>(record->target())) {
             if (input->IsTextField()) {
@@ -216,7 +218,8 @@
   }
 
   void MaybeAddDescendantTextInput(Node* node) {
-    if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+    if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() ||
+        RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) {
       if (auto* input = DynamicTo<HTMLInputElement>(node);
           input && input->IsTextField()) {
         select_->AddDescendantTextInput(input);
@@ -225,7 +228,8 @@
   }
 
   void MaybeRemoveDescendantTextInput(Node* node) {
-    if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+    if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() ||
+        RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) {
       if (auto* input = DynamicTo<HTMLInputElement>(node);
           input && input->IsTextField()) {
         select_->RemoveDescendantTextInput(input);
@@ -327,7 +331,8 @@
       return parent && IsA<HTMLSelectElement>(*parent) &&
              !ElementTraversal::PreviousSibling(node);
     }
-    if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+    if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() ||
+        RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) {
       // <select>s are allowed to have one <input> before the options. We should
       // probably find a way to figure out if the <input> is actually placed
       // before the <option>s or not.
@@ -449,7 +454,8 @@
   }
 
   bool IsAllowedDescendantOfSelect(const Node& descendant, const Node& parent) {
-    if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+    if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() ||
+        RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) {
       // <select>s are allowed to have one text <input>, although it should be
       // placed before any of the <option>s.
       if (select_->FirstDescendantTextInput() == descendant) {
@@ -2356,14 +2362,16 @@
 }
 
 void HTMLSelectElement::AddDescendantTextInput(HTMLInputElement* input) {
-  CHECK(RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled());
+  CHECK(RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() ||
+        RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled());
   CHECK(input->IsTextField());
   descendant_text_inputs_.Add(input);
   input->SetFirstAncestorSelectElement(this);
 }
 
 void HTMLSelectElement::RemoveDescendantTextInput(HTMLInputElement* input) {
-  CHECK(RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled());
+  CHECK(RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() ||
+        RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled());
   descendant_text_inputs_.Remove(input);
   input->SetFirstAncestorSelectElement(nullptr);
 }
diff --git a/third_party/blink/renderer/core/html/forms/select_type.cc b/third_party/blink/renderer/core/html/forms/select_type.cc
index f81577523..4833c283 100644
--- a/third_party/blink/renderer/core/html/forms/select_type.cc
+++ b/third_party/blink/renderer/core/html/forms/select_type.cc
@@ -151,7 +151,9 @@
         HTMLElement* element_to_focus = nullptr;
         if (auto* input = select->FirstDescendantTextInput();
             input &&
-            RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+            (RuntimeEnabledFeatures::
+                 SelectAccessibilityReparentInputEnabled() ||
+             RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled())) {
           // If there is a filter input at the top of the picker, then that
           // should be focused instead of options when opening.
           element_to_focus = input;
diff --git a/third_party/blink/renderer/core/html/forms/text_control_element.cc b/third_party/blink/renderer/core/html/forms/text_control_element.cc
index 0a82b714..630d974 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_element.cc
+++ b/third_party/blink/renderer/core/html/forms/text_control_element.cc
@@ -86,6 +86,41 @@
   return Position();
 }
 
+void AppendWrappedNode(const Element& container,
+                       const Node& node,
+                       const OffsetMapping& mapping,
+                       InlineCursor& cursor,
+                       Position& break_position,
+                       StringBuilder& result) {
+  if (IsA<HTMLBRElement>(node)) {
+    if (RuntimeEnabledFeatures::TextareaLineEndingsAsBrEnabled() &&
+        !TextControlElement::IsPlaceholderBreakElement(&node)) {
+      result.Append(kNewlineCharacter);
+    } else {
+      DCHECK_EQ(&node, container.lastChild());
+    }
+  } else if (auto* text_node = DynamicTo<Text>(node)) {
+    String data = text_node->data();
+    unsigned length = data.length();
+    unsigned position = 0;
+    while (break_position.AnchorNode() == node &&
+           static_cast<unsigned>(break_position.OffsetInContainerNode()) <=
+               length) {
+      unsigned break_offset = break_position.OffsetInContainerNode();
+      if (break_offset > position) {
+        result.Append(data, position, break_offset - position);
+        position = break_offset;
+        result.Append(kNewlineCharacter);
+      }
+      break_position = GetNextSoftBreak(mapping, cursor);
+    }
+    result.Append(data, position, length - position);
+  }
+  while (break_position.AnchorNode() == node) {
+    break_position = GetNextSoftBreak(mapping, cursor);
+  }
+}
+
 }  // namespace
 
 TextControlElement::TextControlElement(const QualifiedName& tag_name,
@@ -1015,6 +1050,34 @@
   if (!layout_object)
     return Value();
 
+  if (RuntimeEnabledFeatures::TextareaMultipleIfcsEnabled()) {
+    StringBuilder result;
+    bool has_valid_ifcs = false;
+    for (auto* anonymous = To<LayoutBlockFlow>(layout_object->FirstChild());
+         anonymous; anonymous = To<LayoutBlockFlow>(anonymous->NextSibling())) {
+      InlineCursor cursor(*anonymous);
+      if (!cursor) {
+        continue;
+      }
+      const auto* mapping = InlineNode::GetOffsetMapping(anonymous);
+      if (!mapping) {
+        continue;
+      }
+      has_valid_ifcs = true;
+      Position break_position = GetNextSoftBreak(*mapping, cursor);
+      const Node* node = anonymous->FirstChild()
+                             ? anonymous->FirstChild()->GetNode()
+                             : nullptr;
+      for (; node && node->GetLayoutObject() &&
+             node->GetLayoutObject()->Parent() == anonymous;
+           node = node->nextSibling()) {
+        AppendWrappedNode(*inner_text, *node, *mapping, cursor, break_position,
+                          result);
+      }
+    }
+    return has_valid_ifcs ? result.ReleaseString() : Value();
+  }
+
   if (layout_object->IsLayoutNGObject()) {
     InlineCursor cursor(*layout_object);
     if (!cursor)
@@ -1025,32 +1088,8 @@
     Position break_position = GetNextSoftBreak(*mapping, cursor);
     StringBuilder result;
     for (Node& node : NodeTraversal::DescendantsOf(*inner_text)) {
-      if (IsA<HTMLBRElement>(node)) {
-        if (RuntimeEnabledFeatures::TextareaLineEndingsAsBrEnabled() &&
-            !IsPlaceholderBreakElement(&node)) {
-          result.Append(kNewlineCharacter);
-        } else {
-          DCHECK_EQ(&node, inner_text->lastChild());
-        }
-      } else if (auto* text_node = DynamicTo<Text>(node)) {
-        String data = text_node->data();
-        unsigned length = data.length();
-        unsigned position = 0;
-        while (break_position.AnchorNode() == node &&
-               static_cast<unsigned>(break_position.OffsetInContainerNode()) <=
-                   length) {
-          unsigned break_offset = break_position.OffsetInContainerNode();
-          if (break_offset > position) {
-            result.Append(data, position, break_offset - position);
-            position = break_offset;
-            result.Append(kNewlineCharacter);
-          }
-          break_position = GetNextSoftBreak(*mapping, cursor);
-        }
-        result.Append(data, position, length - position);
-      }
-      while (break_position.AnchorNode() == node)
-        break_position = GetNextSoftBreak(*mapping, cursor);
+      AppendWrappedNode(*inner_text, node, *mapping, cursor, break_position,
+                        result);
     }
     return result.ToString();
   }
diff --git a/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc b/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
index ca0fa78..3f04043 100644
--- a/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
+++ b/third_party/blink/renderer/core/html/forms/text_control_inner_elements.cc
@@ -150,7 +150,12 @@
           ? EUserModify::kReadOnly
           : EUserModify::kReadWritePlaintextOnly);
   style_builder.SetDisplay(EDisplay::kBlock);
-  style_builder.SetHasLineIfEmpty(true);
+  // HasLineIfEmpty is unnecessary for <textarea> with anonymous IFCs because:
+  //  - <textarea> has the placeholder break element.
+  //  - HasLineIfEmpty is harmful for internal anonymous blocks.
+  if (!RuntimeEnabledFeatures::TextareaMultipleIfcsEnabled()) {
+    style_builder.SetHasLineIfEmpty(true);
+  }
   if (!start_style.ApplyControlFixedSize(host)) {
     Length caret_width(GetDocument().View()->CaretWidth(), Length::kFixed);
     if (IsHorizontalWritingMode(style_builder.GetWritingMode())) {
@@ -162,6 +167,7 @@
   style_builder.SetShouldIgnoreOverflowPropertyForInlineBlockBaseline();
 
   if (!IsA<HTMLTextAreaElement>(host)) {
+    style_builder.SetHasLineIfEmpty(true);
     style_builder.SetScrollbarColor(nullptr);
     style_builder.SetWhiteSpace(EWhiteSpace::kPre);
     style_builder.SetOverflowWrap(EOverflowWrap::kNormal);
diff --git a/third_party/blink/renderer/core/html/html_element.cc b/third_party/blink/renderer/core/html/html_element.cc
index 71ebf70..c5f6e87 100644
--- a/third_party/blink/renderer/core/html/html_element.cc
+++ b/third_party/blink/renderer/core/html/html_element.cc
@@ -2791,7 +2791,7 @@
           AtomicString(url), GetDocument().CompleteURL(url),
           Referrer(GetExecutionContext()->OutgoingReferrer(),
                    GetExecutionContext()->GetReferrerPolicy()),
-          /*origin_clean=*/true, /*is_ad_related=*/false));
+          OriginClean::kTrue, false /* is_ad_related */));
   if (initiator_name) {
     image_value->SetInitiator(initiator_name);
   }
diff --git a/third_party/blink/renderer/core/layout/block_node.cc b/third_party/blink/renderer/core/layout/block_node.cc
index a3974f93f..75633e4 100644
--- a/third_party/blink/renderer/core/layout/block_node.cc
+++ b/third_party/blink/renderer/core/layout/block_node.cc
@@ -38,6 +38,7 @@
 #include "third_party/blink/renderer/core/layout/inline/inline_cursor.h"
 #include "third_party/blink/renderer/core/layout/inline/inline_node.h"
 #include "third_party/blink/renderer/core/layout/layout_block_flow.h"
+#include "third_party/blink/renderer/core/layout/layout_box_utils.h"
 #include "third_party/blink/renderer/core/layout/layout_inline.h"
 #include "third_party/blink/renderer/core/layout/layout_input_node.h"
 #include "third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h"
diff --git a/third_party/blink/renderer/core/layout/build.gni b/third_party/blink/renderer/core/layout/build.gni
index ce3ce1f..2fef536 100644
--- a/third_party/blink/renderer/core/layout/build.gni
+++ b/third_party/blink/renderer/core/layout/build.gni
@@ -133,6 +133,7 @@
   "forms/layout_fieldset.h",
   "forms/layout_text_control.cc",
   "forms/layout_text_control.h",
+  "forms/layout_text_control_inner_editor.cc",
   "forms/layout_text_control_inner_editor.h",
   "forms/layout_text_control_multi_line.cc",
   "forms/layout_text_control_multi_line.h",
@@ -723,6 +724,7 @@
   "flex/flex_layout_algorithm_test.cc",
   "forms/fieldset_layout_algorithm_test.cc",
   "forms/layout_fieldset_test.cc",
+  "forms/layout_text_control_inner_editor_test.cc",
   "forms/layout_text_control_single_line_test.cc",
   "forms/layout_text_control_test.cc",
   "fragmentation_test.cc",
diff --git a/third_party/blink/renderer/core/layout/forms/layout_text_control_inner_editor.cc b/third_party/blink/renderer/core/layout/forms/layout_text_control_inner_editor.cc
new file mode 100644
index 0000000..03a002d1
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/forms/layout_text_control_inner_editor.cc
@@ -0,0 +1,76 @@
+// 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/forms/layout_text_control_inner_editor.h"
+
+#include "third_party/blink/renderer/core/html/forms/html_text_area_element.h"
+
+namespace blink {
+
+LayoutTextControlInnerEditor::LayoutTextControlInnerEditor(Element* element)
+    : LayoutBlockFlow(element),
+      is_multiline_(IsA<HTMLTextAreaElement>(element->OwnerShadowHost()) &&
+                    RuntimeEnabledFeatures::TextareaMultipleIfcsEnabled()) {}
+
+void LayoutTextControlInnerEditor::AddChild(LayoutObject* new_child,
+                                            LayoutObject* before_child) {
+  NOT_DESTROYED();
+  if (!is_multiline_) {
+    LayoutBlockFlow::AddChild(new_child, before_child);
+    return;
+  }
+
+  // If a <textarea> has a value "line1\nline2\n", a
+  // TextControlInnerEditorElement for the <textarea> has the following DOM
+  // structure:
+  //
+  // TextControlInnerEditorElement
+  //   * Text "line1"
+  //   * HTMLBRElement
+  //   * Text "line2"
+  //   * HTMLBRElement
+  //   * HTMLBRElement id="textarea-placeholder-break"
+  //
+  // This function wraps a pair of a Text and an HTMLBRElement with an anonymous
+  // block. So a child of LayoutTextControlInnerEditor must be an anonymous
+  // LayoutBlockFlow.
+  // Exception: It can have non-anonymous blocks during "TestRendering".
+  //            See blink::ReplacementFragment.
+  //
+  // LayoutTextControlInnerEditor
+  //   * LayoutBlockFlow (anonymous)
+  //     - LayoutText "line1"
+  //     - LayoutBR
+  //   * LayoutBlockFlow (anonymous)
+  //     - LayoutText "line2"
+  //     - LayoutBR
+  //   * LayoutBlockFlow (anonymous)
+  //     - LayoutBR
+
+  if (!before_child) {
+    auto* last_anonymous = DynamicTo<LayoutBlockFlow>(LastChild());
+    if (last_anonymous && !last_anonymous->LastChild()->IsBR()) {
+      last_anonymous->AddChild(new_child);
+      return;
+    }
+    auto* anonymous = LayoutBlockFlow::CreateAnonymous(&GetDocument(), Style());
+    LayoutBlockFlow::AddChild(anonymous);
+    anonymous->AddChild(new_child);
+    return;
+  }
+
+  DCHECK(FirstChild());
+  auto* before_parent = To<LayoutBlockFlow>(before_child->Parent());
+  if (!new_child->IsBR()) {
+    before_parent->AddChild(new_child, before_child);
+    return;
+  }
+  auto* anonymous = LayoutBlockFlow::CreateAnonymous(&GetDocument(), Style());
+  LayoutBlockFlow::AddChild(anonymous, before_parent);
+  before_parent->MoveChildrenTo(anonymous, before_parent->FirstChild(),
+                                before_child, /* full_remove_insert */ true);
+  anonymous->AddChild(new_child);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/forms/layout_text_control_inner_editor.h b/third_party/blink/renderer/core/layout/forms/layout_text_control_inner_editor.h
index bd9d478a..010192b 100644
--- a/third_party/blink/renderer/core/layout/forms/layout_text_control_inner_editor.h
+++ b/third_party/blink/renderer/core/layout/forms/layout_text_control_inner_editor.h
@@ -13,13 +13,23 @@
 // in <input> and <textarea>.
 class LayoutTextControlInnerEditor final : public LayoutBlockFlow {
  public:
-  explicit LayoutTextControlInnerEditor(Element* element)
-      : LayoutBlockFlow(element) {}
+  explicit LayoutTextControlInnerEditor(Element* element);
 
   const char* GetName() const override {
     NOT_DESTROYED();
     return "LayoutTextControlInnerEditor";
   }
+
+  bool IsTextControlInnerEditor() const override {
+    NOT_DESTROYED();
+    return true;
+  }
+
+  void AddChild(LayoutObject* new_child,
+                LayoutObject* before_child = nullptr) override;
+
+ private:
+  const bool is_multiline_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/forms/layout_text_control_inner_editor_test.cc b/third_party/blink/renderer/core/layout/forms/layout_text_control_inner_editor_test.cc
new file mode 100644
index 0000000..7cc7edb
--- /dev/null
+++ b/third_party/blink/renderer/core/layout/forms/layout_text_control_inner_editor_test.cc
@@ -0,0 +1,121 @@
+// 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/testing/core_unit_test_helper.h"
+
+namespace blink {
+
+class LayoutTextControlInnerEditorTest : public RenderingTest {};
+
+TEST_F(LayoutTextControlInnerEditorTest, AddChildWithoutTrailingLf) {
+  if (!RuntimeEnabledFeatures::TextareaMultipleIfcsEnabled()) {
+    return;
+  }
+  SetBodyInnerHTML("<textarea id=ta>foo\nbar</textarea>");
+  const auto* ta = GetLayoutBoxByElementId("ta");
+  const auto* inner_editor = To<LayoutBlockFlow>(ta->FirstChildBox());
+  ASSERT_TRUE(inner_editor);
+
+  // The first anonymous block should have a LayoutText and a LayoutBR.
+  const auto* child = inner_editor->FirstChild();
+  ASSERT_TRUE(child);
+  EXPECT_TRUE(child->IsAnonymousBlockFlow());
+  const auto* grand_child = child->SlowFirstChild();
+  ASSERT_TRUE(grand_child);
+  EXPECT_TRUE(grand_child->IsText());
+  grand_child = grand_child->NextSibling();
+  ASSERT_TRUE(grand_child);
+  EXPECT_TRUE(grand_child->IsBR());
+  EXPECT_FALSE(grand_child->NextSibling());
+
+  // The second anonymous block should have only a LayoutText.
+  child = child->NextSibling();
+  ASSERT_TRUE(child);
+  EXPECT_TRUE(child->IsAnonymousBlockFlow());
+  grand_child = child->SlowFirstChild();
+  ASSERT_TRUE(grand_child);
+  EXPECT_TRUE(grand_child->IsText());
+  EXPECT_FALSE(grand_child->NextSibling());
+
+  child = child->NextSibling();
+  EXPECT_FALSE(child);
+}
+
+TEST_F(LayoutTextControlInnerEditorTest, AddChildWithTrailingLf) {
+  if (!RuntimeEnabledFeatures::TextareaMultipleIfcsEnabled()) {
+    return;
+  }
+  SetBodyInnerHTML("<textarea id=ta>foo\nbar\n</textarea>");
+  const auto* ta = GetLayoutBoxByElementId("ta");
+  const auto* inner_editor = To<LayoutBlockFlow>(ta->FirstChildBox());
+  ASSERT_TRUE(inner_editor);
+
+  // The first anonymous block should have a LayoutText and a LayoutBR.
+  const auto* child = inner_editor->FirstChild();
+  ASSERT_TRUE(child);
+  EXPECT_TRUE(child->IsAnonymousBlockFlow());
+  const auto* grand_child = child->SlowFirstChild();
+  ASSERT_TRUE(grand_child);
+  EXPECT_TRUE(grand_child->IsText());
+  grand_child = grand_child->NextSibling();
+  ASSERT_TRUE(grand_child);
+  EXPECT_TRUE(grand_child->IsBR());
+  EXPECT_FALSE(grand_child->NextSibling());
+
+  // The second anonymous block should have a LayoutText and a LayoutBR.
+  child = child->NextSibling();
+  ASSERT_TRUE(child);
+  EXPECT_TRUE(child->IsAnonymousBlockFlow());
+  grand_child = child->SlowFirstChild();
+  ASSERT_TRUE(grand_child);
+  EXPECT_TRUE(grand_child->IsText());
+  grand_child = grand_child->NextSibling();
+  ASSERT_TRUE(grand_child);
+  EXPECT_TRUE(grand_child->IsBR());
+  EXPECT_FALSE(grand_child->NextSibling());
+
+  // The third anonymous block should have only a placeholder break.
+  child = child->NextSibling();
+  ASSERT_TRUE(child);
+  EXPECT_TRUE(child->IsAnonymousBlockFlow());
+  grand_child = child->SlowFirstChild();
+  ASSERT_TRUE(grand_child);
+  EXPECT_TRUE(grand_child->IsBR());
+  EXPECT_FALSE(grand_child->NextSibling());
+
+  EXPECT_FALSE(child->NextSibling());
+}
+
+TEST_F(LayoutTextControlInnerEditorTest, RemoveChildWithoutTrailingLf) {
+  if (!RuntimeEnabledFeatures::TextareaMultipleIfcsEnabled()) {
+    return;
+  }
+  SetBodyInnerHTML("<textarea id=ta>foo\nbar</textarea>");
+  const auto* ta = GetLayoutBoxByElementId("ta");
+  const auto* inner_editor = To<LayoutBlockFlow>(ta->FirstChildBox());
+  ASSERT_TRUE(inner_editor);
+  auto* inner_editor_element = To<Element>(inner_editor->GetNode());
+
+  // There should be two anonymous blocks before editing.
+  const auto* child = inner_editor->FirstChild();
+  EXPECT_TRUE(child->IsAnonymousBlockFlow());
+  child = child->NextSibling();
+  EXPECT_TRUE(child);
+  EXPECT_TRUE(child->IsAnonymousBlockFlow());
+  EXPECT_FALSE(child->NextSibling());
+
+  // Remove "bar".
+  auto* child_node = inner_editor_element->lastChild();
+  ASSERT_TRUE(child_node);
+  EXPECT_TRUE(child_node->IsTextNode());
+  child_node->remove();
+  UpdateAllLifecyclePhasesForTest();
+
+  // There should be one anonymous block after editing.
+  child = inner_editor->FirstChild();
+  EXPECT_TRUE(child->IsAnonymousBlockFlow());
+  EXPECT_FALSE(child->NextSibling());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/forms/layout_text_control_test.cc b/third_party/blink/renderer/core/layout/forms/layout_text_control_test.cc
index c80900b6..263c0215 100644
--- a/third_party/blink/renderer/core/layout/forms/layout_text_control_test.cc
+++ b/third_party/blink/renderer/core/layout/forms/layout_text_control_test.cc
@@ -24,8 +24,15 @@
   }
   // Return the LayoutText from inside a text control's user agent shadow tree.
   LayoutText* GetInnerLayoutText(TextControlElement* control) {
-    return To<LayoutText>(
-        control->InnerEditorElement()->GetLayoutObject()->SlowFirstChild());
+    auto* editor_box = control->InnerEditorElement()->GetLayoutObject();
+    if (!RuntimeEnabledFeatures::TextareaMultipleIfcsEnabled()) {
+      return To<LayoutText>(editor_box->SlowFirstChild());
+    }
+    if (auto* anonymous_block =
+            DynamicTo<LayoutBlockFlow>(editor_box->SlowFirstChild())) {
+      return To<LayoutText>(anonymous_block->FirstChild());
+    }
+    return To<LayoutText>(editor_box->SlowFirstChild());
   }
 
   // Focus on |control|, select 1-3 characters, get the first LayoutText, and
diff --git a/third_party/blink/renderer/core/layout/grid/grid_named_line_collection.h b/third_party/blink/renderer/core/layout/grid/grid_named_line_collection.h
index 7547e873..2ebdb578 100644
--- a/third_party/blink/renderer/core/layout/grid/grid_named_line_collection.h
+++ b/third_party/blink/renderer/core/layout/grid/grid_named_line_collection.h
@@ -6,11 +6,10 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_GRID_GRID_NAMED_LINE_COLLECTION_H_
 
 #include "third_party/blink/renderer/core/style/grid_enums.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/core/style/named_grid_lines_map.h"
 
 namespace blink {
 
-using NamedGridLinesMap = HashMap<String, Vector<wtf_size_t>>;
 struct ComputedGridTrackList;
 
 class GridNamedLineCollection {
diff --git a/third_party/blink/renderer/core/layout/inline/inline_cursor_test.cc b/third_party/blink/renderer/core/layout/inline/inline_cursor_test.cc
index 4160771..182aa9e0 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_cursor_test.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_cursor_test.cc
@@ -426,13 +426,26 @@
       textarea.InnerEditorElement()->GetLayoutObject();
   const LayoutBlockFlow& block_flow = *To<LayoutBlockFlow>(textarea_layout);
 
-  InlineCursor move_to_end_of_line(block_flow);
-  // Preparing the InlineCursor to start from beginning
-  // of second line(Empty Line).
-  move_to_end_of_line.MoveToNextLine();
+  // Preparing the InlineCursor to start from beginning of the second line
+  // (Empty Line).
+  const LayoutBlockFlow* second_anonymous =
+      RuntimeEnabledFeatures::TextareaMultipleIfcsEnabled()
+          ? To<LayoutBlockFlow>(block_flow.FirstChild()->NextSibling())
+          : nullptr;
+  InlineCursor move_to_end_of_line(second_anonymous ? *second_anonymous
+                                                    : block_flow);
+  if (!second_anonymous) {
+    // Preparing the InlineCursor to start from beginning
+    // of second line(Empty Line).
+    move_to_end_of_line.MoveToNextLine();
+  }
   InlineCursor next_line = move_to_end_of_line.CursorForDescendants();
   // Verify if it has been successfully placed at the correct position.
-  EXPECT_EQ(4u, next_line.Current().TextStartOffset());
+  if (!second_anonymous) {
+    EXPECT_EQ(4u, next_line.Current().TextStartOffset());
+  } else {
+    EXPECT_EQ(0u, next_line.Current().TextStartOffset());
+  }
   const PositionWithAffinity end_position =
       move_to_end_of_line.PositionForEndOfLine();
   if (RuntimeEnabledFeatures::TextareaLineEndingsAsBrEnabled()) {
@@ -451,13 +464,26 @@
       textarea.InnerEditorElement()->GetLayoutObject();
   const LayoutBlockFlow& block_flow = *To<LayoutBlockFlow>(textarea_layout);
 
-  InlineCursor move_to_end_of_line(block_flow);
-  // Preparing the InlineCursor to start from beginning
-  // of second line(Empty Line).
-  move_to_end_of_line.MoveToNextLine();
+  // Preparing the InlineCursor to start from beginning of the second line
+  // (Empty Line).
+  const LayoutBlockFlow* second_anonymous =
+      RuntimeEnabledFeatures::TextareaMultipleIfcsEnabled()
+          ? To<LayoutBlockFlow>(block_flow.FirstChild()->NextSibling())
+          : nullptr;
+  InlineCursor move_to_end_of_line(second_anonymous ? *second_anonymous
+                                                    : block_flow);
+  if (!second_anonymous) {
+    // Preparing the InlineCursor to start from beginning
+    // of second line(Empty Line).
+    move_to_end_of_line.MoveToNextLine();
+  }
   InlineCursor next_line = move_to_end_of_line.CursorForDescendants();
   // Verify if it has been successfully placed at the correct position.
-  EXPECT_EQ(4u, next_line.Current().TextStartOffset());
+  if (!second_anonymous) {
+    EXPECT_EQ(4u, next_line.Current().TextStartOffset());
+  } else {
+    EXPECT_EQ(0u, next_line.Current().TextStartOffset());
+  }
   const PositionWithAffinity end_position =
       move_to_end_of_line.PositionForEndOfLine();
   if (RuntimeEnabledFeatures::TextareaLineEndingsAsBrEnabled()) {
diff --git a/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm_test.cc b/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm_test.cc
index a8c33c62..abcca3c 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm_test.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_layout_algorithm_test.cc
@@ -66,8 +66,12 @@
     HTMLTextAreaElement* textarea = To<HTMLTextAreaElement>(GetElementById(id));
     DCHECK(textarea);
 
-    InlineCursor cursor(*To<LayoutBlockFlow>(
-        textarea->InnerEditorElement()->GetLayoutObject()));
+    LayoutBlockFlow* block_flow =
+        To<LayoutBlockFlow>(textarea->InnerEditorElement()->GetLayoutObject());
+    if (RuntimeEnabledFeatures::TextareaMultipleIfcsEnabled()) {
+      block_flow = To<LayoutBlockFlow>(block_flow->FirstChild());
+    }
+    InlineCursor cursor(*block_flow);
     cursor.MoveToFirstLine();
     EXPECT_TRUE(cursor.IsNotNull());
 
diff --git a/third_party/blink/renderer/core/layout/layout_block_flow.cc b/third_party/blink/renderer/core/layout/layout_block_flow.cc
index 4677515c..77098d3 100644
--- a/third_party/blink/renderer/core/layout/layout_block_flow.cc
+++ b/third_party/blink/renderer/core/layout/layout_block_flow.cc
@@ -84,6 +84,10 @@
          !block.IsScrollMarkerGroup();
 }
 
+bool IsInnerEditorChild(const LayoutBlockFlow& block) {
+  return block.Parent() && block.Parent()->IsTextControlInnerEditor();
+}
+
 }  // anonymous namespace
 
 struct SameSizeAsLayoutBlockFlow : public LayoutBlock {
@@ -221,7 +225,7 @@
 
 static bool IsMergeableAnonymousBlock(const LayoutBlockFlow* block) {
   return block->IsAnonymousBlockFlow() && !block->BeingDestroyed() &&
-         !block->IsViewTransitionRoot();
+         !block->IsViewTransitionRoot() && !IsInnerEditorChild(*block);
 }
 
 void LayoutBlockFlow::RemoveChild(LayoutObject* old_child) {
@@ -232,6 +236,7 @@
     LayoutBox::RemoveChild(old_child);
     return;
   }
+  const bool is_inner_editor_child = IsAnonymous() && IsInnerEditorChild(*this);
 
   // If this child is a block, and if our previous and next siblings are both
   // anonymous blocks with inline content, then we can go ahead and fold the
@@ -274,6 +279,24 @@
 
   LayoutBlock::RemoveChild(old_child);
 
+  if (is_inner_editor_child && !BeingDestroyed()) {
+    if (old_child->IsBR() && FirstChild()) {
+      // We removed a LayoutBR from `this`. If this still contains LayoutTexts,
+      // we move them to the next anonymous block. Then, remove `this` from the
+      // parent.
+      if (auto* next_anonymous = To<LayoutBlockFlow>(NextSibling())) {
+        CHECK(next_anonymous->IsAnonymous());
+        MoveAllChildrenTo(next_anonymous, next_anonymous->FirstChild(),
+                          /* full_remove_insert */ true);
+      }
+    }
+    if (!FirstChild() && Parent()) {
+      Parent()->RemoveChild(this);
+      Destroy();
+    }
+    return;
+  }
+
   LayoutObject* child = prev ? prev : next;
   auto* child_block_flow = DynamicTo<LayoutBlockFlow>(child);
   if (child_block_flow && !child_block_flow->PreviousSibling() &&
@@ -287,7 +310,9 @@
 
   if (FirstChild() && !BeingDestroyed() &&
       !old_child->IsFloatingOrOutOfFlowPositioned() &&
-      !old_child->IsAnonymousBlockFlow()) {
+      !old_child->IsAnonymousBlockFlow() &&
+      !(RuntimeEnabledFeatures::TextareaMultipleIfcsEnabled() &&
+        IsTextControlInnerEditor())) {
     // If the child we're removing means that we can now treat all children as
     // inline without the need for anonymous blocks, then do that.
     MakeChildrenInlineIfPossible();
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index a739462..06d4ccd 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -237,10 +237,14 @@
   }
 
   const auto* inner_editor = textarea.InnerEditorElement();
+  const auto* reference_box =
+      inner_editor ? inner_editor->GetLayoutBox() : nullptr;
+  if (RuntimeEnabledFeatures::TextareaMultipleIfcsEnabled() && reference_box &&
+      reference_box->FirstChildBox()) {
+    reference_box = reference_box->FirstChildBox();
+  }
   const LayoutUnit line_height =
-      inner_editor && inner_editor->GetLayoutBox()
-          ? inner_editor->GetLayoutBox()->FirstLineHeight()
-          : box.FirstLineHeight();
+      reference_box ? reference_box->FirstLineHeight() : box.FirstLineHeight();
 
   return line_height * textarea.rows() + scrollbar_thickness;
 }
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object.h b/third_party/blink/renderer/core/layout/layout_box_model_object.h
index fde70c2..ec149a5 100644
--- a/third_party/blink/renderer/core/layout/layout_box_model_object.h
+++ b/third_party/blink/renderer/core/layout/layout_box_model_object.h
@@ -53,7 +53,7 @@
 //
 // This class actually doesn't have the box model but it exposes some common
 // functions or concepts that sub-classes can extend upon. For example, there
-// are accessors for margins, borders, paddings and borderBoundingBox().
+// are accessors for margins, borders, and paddings.
 //
 // The reason for this partial implementation is that the 2 classes inheriting
 // from it (LayoutBox and LayoutInline) have different requirements but need to
@@ -61,7 +61,7 @@
 //
 // An important member of this class is PaintLayer, which is stored in a rare-
 // data pattern (see: Layer()). PaintLayers are instantiated for several reasons
-// based on the return value of layerTypeRequired().
+// based on the return value of LayerTypeRequired().
 // Interestingly, most SVG objects inherit from LayoutSVGModelObject and thus
 // can't have a PaintLayer. This is an unfortunate artifact of our
 // design as it limits code sharing and prevents hardware accelerating SVG
@@ -438,7 +438,7 @@
   // structure via remove/insert/appendChildNode.
   // Since they are typically called only to move objects around within
   // anonymous blocks (which only have layers in the case of column spans), the
-  // default for fullRemoveInsert is false rather than true.
+  // default for `full_remove_insert` is false rather than true.
   void MoveChildTo(LayoutBoxModelObject* to_box_model_object,
                    LayoutObject* child,
                    LayoutObject* before_child,
@@ -461,9 +461,10 @@
     MoveChildrenTo(to_box_model_object, SlowFirstChild(), nullptr, before_child,
                    full_remove_insert);
   }
-  // Move all of the kids from |startChild| up to but excluding |endChild|. 0
-  // can be passed as the |endChild| to denote that all the kids from
-  // |startChild| onwards should be moved.
+  // Move all of the kids from `start_child` up to but excluding `end_child`. 0
+  // can be passed as the `end_child` to denote that all the kids from
+  // `start_child` onwards should be moved. Nothing happens if start_child ==
+  // end_child.
   void MoveChildrenTo(LayoutBoxModelObject* to_box_model_object,
                       LayoutObject* start_child,
                       LayoutObject* end_child,
diff --git a/third_party/blink/renderer/core/layout/layout_input_node.h b/third_party/blink/renderer/core/layout/layout_input_node.h
index d189c29..0272d1e 100644
--- a/third_party/blink/renderer/core/layout/layout_input_node.h
+++ b/third_party/blink/renderer/core/layout/layout_input_node.h
@@ -13,7 +13,6 @@
 #include "third_party/blink/renderer/core/layout/geometry/axis.h"
 #include "third_party/blink/renderer/core/layout/geometry/logical_size.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
-#include "third_party/blink/renderer/core/layout/layout_box_utils.h"
 #include "third_party/blink/renderer/core/layout/list/layout_outside_list_marker.h"
 #include "third_party/blink/renderer/platform/geometry/layout_unit.h"
 #include "third_party/blink/renderer/platform/text/writing_mode.h"
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc b/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc
index 0734df1c..bbc1945 100644
--- a/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc
+++ b/third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.cc
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/css/resolver/style_resolver.h"
 #include "third_party/blink/renderer/core/layout/fragmentation_utils.h"
 #include "third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h"
+#include "third_party/blink/renderer/core/layout/layout_box_utils.h"
 #include "third_party/blink/renderer/core/layout/layout_multi_column_set.h"
 #include "third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
diff --git a/third_party/blink/renderer/core/layout/layout_multi_column_set.cc b/third_party/blink/renderer/core/layout/layout_multi_column_set.cc
index 529e24c..5d502c94 100644
--- a/third_party/blink/renderer/core/layout/layout_multi_column_set.cc
+++ b/third_party/blink/renderer/core/layout/layout_multi_column_set.cc
@@ -29,6 +29,7 @@
 #include "third_party/blink/renderer/core/editing/position_with_affinity.h"
 #include "third_party/blink/renderer/core/layout/fragmentation_utils.h"
 #include "third_party/blink/renderer/core/layout/geometry/box_strut.h"
+#include "third_party/blink/renderer/core/layout/layout_box_utils.h"
 #include "third_party/blink/renderer/core/layout/layout_multi_column_flow_thread.h"
 #include "third_party/blink/renderer/core/layout/layout_multi_column_spanner_placeholder.h"
 #include "third_party/blink/renderer/core/layout/multi_column_fragmentainer_group.h"
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index e201aa73d..0866009 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -4930,7 +4930,8 @@
   // styling may apply to the entire subtree.
   for (LayoutObject* child = SlowFirstChild(); child;
        child = child->NextSibling()) {
-    if (!child->IsSelected()) {
+    // Anonymous objects never be IsSelected(). See SetSelectionStateIfNeeded().
+    if (!child->IsSelected() && !child->IsAnonymous()) {
       continue;
     }
     if (child->CanBeSelectionLeaf()) {
diff --git a/third_party/blink/renderer/core/layout/layout_object.h b/third_party/blink/renderer/core/layout/layout_object.h
index c2d1156..71a8508 100644
--- a/third_party/blink/renderer/core/layout/layout_object.h
+++ b/third_party/blink/renderer/core/layout/layout_object.h
@@ -994,6 +994,10 @@
     NOT_DESTROYED();
     return false;
   }
+  virtual bool IsTextControlInnerEditor() const {
+    NOT_DESTROYED();
+    return false;
+  }
   virtual bool IsTextField() const {
     NOT_DESTROYED();
     return false;
diff --git a/third_party/blink/renderer/core/layout/layout_text.cc b/third_party/blink/renderer/core/layout/layout_text.cc
index 6e6d864..41bb7c9 100644
--- a/third_party/blink/renderer/core/layout/layout_text.cc
+++ b/third_party/blink/renderer/core/layout/layout_text.cc
@@ -1056,14 +1056,8 @@
 void LayoutText::TextDidChangeWithoutInvalidation() {
   NOT_DESTROYED();
   TextOffsetMap offset_map;
-  String original_text =
-      (RuntimeEnabledFeatures::UseOriginalDomOffsetsForOffsetMapEnabled() &&
-       GetDocument().GetSettings() &&
-       GetDocument().GetSettings()->GetPasswordEchoEnabled())
-          ? OriginalText()
-          : text_;
-  wtf_size_t original_length = original_text.length();
-  text_ = TransformAndSecureText(original_text, offset_map);
+  wtf_size_t original_length = text_.length();
+  text_ = TransformAndSecureText(text_, offset_map);
   SetVariableLengthTransformResult(original_length, offset_map);
   if (auto* secure_text_timer = SecureTextTimer::ActiveInstanceFor(this)) {
     // text_ may be updated later before timer fires. We invalidate the
diff --git a/third_party/blink/renderer/core/layout/table/layout_table_column.cc b/third_party/blink/renderer/core/layout/table/layout_table_column.cc
index d675f05..b88fe93b 100644
--- a/third_party/blink/renderer/core/layout/table/layout_table_column.cc
+++ b/third_party/blink/renderer/core/layout/table/layout_table_column.cc
@@ -428,6 +428,12 @@
     // block-size of the table column / table column group.
     LogicalRect sections_bounding_box;
     for (const PhysicalFragmentLink& child : fragment.Children()) {
+      if (child->IsLayoutObjectDestroyedOrMoved()) {
+        // This code may be run on a dirty layout tree. The scroll anchor code,
+        // for instance, essentially always works on a dirty tree, and may
+        // invoke LayoutTableColumn::PhysicalLocation(), so that we end up here.
+        continue;
+      }
       if (child->IsTableSection()) {
         LogicalRect section_rect = table_converter.ToLogical(
             PhysicalRect(child.offset, child->Size()));
diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc
index bb0848d..eb4422f9 100644
--- a/third_party/blink/renderer/core/loader/frame_loader.cc
+++ b/third_party/blink/renderer/core/loader/frame_loader.cc
@@ -1797,7 +1797,7 @@
 
   MixedContentChecker::UpgradeInsecureRequest(
       resource_request, fetch_client_settings_object, window_for_logging,
-      frame_type, frame_->GetContentSettingsClient());
+      frame_type, frame_->GetContentSettingsClient(), frame_);
 }
 
 void FrameLoader::WriteIntoTrace(perfetto::TracedValue context) const {
diff --git a/third_party/blink/renderer/core/loader/mixed_content_checker.cc b/third_party/blink/renderer/core/loader/mixed_content_checker.cc
index c3a04f6..92fbcfc 100644
--- a/third_party/blink/renderer/core/loader/mixed_content_checker.cc
+++ b/third_party/blink/renderer/core/loader/mixed_content_checker.cc
@@ -318,6 +318,31 @@
 }
 
 // static
+bool MixedContentChecker::IsMixedContentRestrictedInFrameContext(
+    LocalFrame* frame) {
+  if (!frame) {
+    return false;
+  }
+  // Check the top frame first.
+  Frame& top = frame->Tree().Top();
+  if (SchemeRegistry::ShouldTreatURLSchemeAsRestrictingMixedContent(
+          top.GetSecurityContext()
+              ->GetSecurityOrigin()
+              ->GetOriginOrPrecursorOriginIfOpaque()
+              ->Protocol())) {
+    return true;
+  }
+  if (SchemeRegistry::ShouldTreatURLSchemeAsRestrictingMixedContent(
+          frame->GetSecurityContext()
+              ->GetSecurityOrigin()
+              ->GetOriginOrPrecursorOriginIfOpaque()
+              ->Protocol())) {
+    return true;
+  }
+  return false;
+}
+
+// static
 Frame* MixedContentChecker::InWhichFrameIsContentMixed(LocalFrame* frame,
                                                        const KURL& url) {
   // Frameless requests cannot be mixed content.
@@ -858,14 +883,22 @@
     mojom::blink::RequestContextType type,
     WebContentSettingsClient* settings_client,
     const ResourceRequest& resource_request,
-    ExecutionContext* execution_context_for_logging) {
-  const HttpsState https_state = fetch_client_settings_object->GetHttpsState();
+    ExecutionContext* execution_context_for_logging,
+    LocalFrame* frame) {
   const KURL& request_url = resource_request.Url();
   // We are currently not autoupgrading plugin loaded content, which is why
   // check_mode_for_plugin is hardcoded to kStrict.
+  bool settings_restricts_mixed_content;
+  if (frame) {
+    settings_restricts_mixed_content =
+        IsMixedContentRestrictedInFrameContext(frame);
+  } else {
+    settings_restricts_mixed_content =
+        fetch_client_settings_object->GetHttpsState() == HttpsState::kModern;
+  }
   if (!base::FeatureList::IsEnabled(
           blink::features::kMixedContentAutoupgrade) ||
-      https_state == HttpsState::kNone ||
+      !settings_restricts_mixed_content ||
       MixedContent::ContextTypeFromRequestContext(
           type, MixedContent::CheckModeForPlugin::kStrict) !=
           mojom::blink::MixedContentContextType::kOptionallyBlockable) {
@@ -1000,7 +1033,8 @@
     const FetchClientSettingsObject* fetch_client_settings_object,
     ExecutionContext* execution_context_for_logging,
     mojom::RequestContextFrameType frame_type,
-    WebContentSettingsClient* settings_client) {
+    WebContentSettingsClient* settings_client,
+    LocalFrame* frame) {
   // We always upgrade requests that meet any of the following criteria:
   //  1. Are for subresources.
   //  2. Are for nested frames.
@@ -1025,7 +1059,7 @@
     if (context == mojom::blink::RequestContextType::UNSPECIFIED ||
         !MixedContentChecker::ShouldAutoupgrade(
             fetch_client_settings_object, context, settings_client,
-            resource_request, execution_context_for_logging)) {
+            resource_request, execution_context_for_logging, frame)) {
       return;
     }
     // We set the upgrade if insecure flag regardless of whether we autoupgrade
diff --git a/third_party/blink/renderer/core/loader/mixed_content_checker.h b/third_party/blink/renderer/core/loader/mixed_content_checker.h
index 1c8149e..39e3c66 100644
--- a/third_party/blink/renderer/core/loader/mixed_content_checker.h
+++ b/third_party/blink/renderer/core/loader/mixed_content_checker.h
@@ -112,7 +112,8 @@
       mojom::blink::RequestContextType type,
       WebContentSettingsClient* settings_client,
       const ResourceRequest& resource_request,
-      ExecutionContext* execution_context_for_logging);
+      ExecutionContext* execution_context_for_logging,
+      LocalFrame* frame);
 
   static mojom::blink::MixedContentContextType ContextTypeForInspector(
       LocalFrame*,
@@ -152,7 +153,8 @@
       const FetchClientSettingsObject* fetch_client_settings_object,
       ExecutionContext* execution_context_for_logging,
       mojom::RequestContextFrameType,
-      WebContentSettingsClient* settings_client);
+      WebContentSettingsClient* settings_client,
+      LocalFrame* frame);
 
   static MixedContent::CheckModeForPlugin DecideCheckModeForPlugin(Settings*);
 
@@ -162,6 +164,8 @@
  private:
   FRIEND_TEST_ALL_PREFIXES(MixedContentCheckerTest, HandleCertificateError);
 
+  static bool IsMixedContentRestrictedInFrameContext(LocalFrame* frame);
+
   static Frame* InWhichFrameIsContentMixed(LocalFrame*, const KURL&);
 
   static ConsoleMessage* CreateConsoleMessageAboutFetch(
diff --git a/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc b/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc
index 56a4545e..c95b504 100644
--- a/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc
+++ b/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc
@@ -16,6 +16,8 @@
 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/mixed_content.mojom-blink.h"
 #include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
+#include "third_party/blink/renderer/core/execution_context/security_context.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/loader/empty_clients.h"
@@ -349,7 +351,9 @@
   // These are not used in test, but need to be implemented since they are pure
   // virtual.
   const KURL& BaseUrl() const override { return url; }
-  const SecurityOrigin* GetSecurityOrigin() const override { return nullptr; }
+  const SecurityOrigin* GetSecurityOrigin() const override {
+    return origin_.get();
+  }
   network::mojom::ReferrerPolicy GetReferrerPolicy() const override {
     return network::mojom::ReferrerPolicy::kAlways;
   }
@@ -362,10 +366,19 @@
       const override {
     return set;
   }
+  void SetSecurityOrigin(String origin_url, String reference_origin) {
+    KURL origin_kurl(origin_url);
+    scoped_refptr<SecurityOrigin> reference =
+        SecurityOrigin::CreateFromString(reference_origin);
+    origin_ = SecurityOrigin::CreateWithReferenceOrigin(KURL(origin_url),
+                                                        reference.get());
+  }
+  scoped_refptr<SecurityOrigin> GetSecurityOrigin() { return origin_; }
 
  private:
   const KURL url = KURL("https://example.test");
   const InsecureNavigationsSet set;
+  scoped_refptr<SecurityOrigin> origin_;
 };
 
 TEST(MixedContentCheckerTest,
@@ -376,12 +389,17 @@
   request.SetRequestContext(mojom::blink::RequestContextType::AUDIO);
   TestFetchClientSettingsObject* settings =
       MakeGarbageCollected<TestFetchClientSettingsObject>();
+  settings->SetSecurityOrigin("https://example.test", "");
   // Used to get a non-null document.
   DummyPageHolder holder;
+  holder.GetFrame()
+      .DomWindow()
+      ->GetSecurityContext()
+      .SetSecurityOriginForTesting(settings->GetSecurityOrigin());
 
   MixedContentChecker::UpgradeInsecureRequest(
       request, settings, holder.GetDocument().GetExecutionContext(),
-      mojom::RequestContextFrameType::kTopLevel, nullptr);
+      mojom::RequestContextFrameType::kTopLevel, nullptr, &holder.GetFrame());
 
   EXPECT_FALSE(request.IsAutomaticUpgrade());
   EXPECT_TRUE(request.UpgradeIfInsecure());
@@ -394,12 +412,18 @@
   request.SetRequestContext(mojom::blink::RequestContextType::AUDIO);
   TestFetchClientSettingsObject* settings =
       MakeGarbageCollected<TestFetchClientSettingsObject>();
+  settings->SetSecurityOrigin("https://example.test", "");
+
   // Used to get a non-null document.
   DummyPageHolder holder;
+  holder.GetFrame()
+      .DomWindow()
+      ->GetSecurityContext()
+      .SetSecurityOriginForTesting(settings->GetSecurityOrigin());
 
   MixedContentChecker::UpgradeInsecureRequest(
       request, settings, holder.GetDocument().GetExecutionContext(),
-      mojom::RequestContextFrameType::kTopLevel, nullptr);
+      mojom::RequestContextFrameType::kTopLevel, nullptr, &holder.GetFrame());
 
   EXPECT_TRUE(request.IsAutomaticUpgrade());
   EXPECT_TRUE(request.UpgradeIfInsecure());
@@ -413,12 +437,18 @@
   request.SetRequestContext(mojom::blink::RequestContextType::AUDIO);
   TestFetchClientSettingsObject* settings =
       MakeGarbageCollected<TestFetchClientSettingsObject>();
+  settings->SetSecurityOrigin("https://example.test", "");
+
   // Used to get a non-null document.
   DummyPageHolder holder;
+  holder.GetFrame()
+      .DomWindow()
+      ->GetSecurityContext()
+      .SetSecurityOriginForTesting(settings->GetSecurityOrigin());
 
   MixedContentChecker::UpgradeInsecureRequest(
       request, settings, holder.GetDocument().GetExecutionContext(),
-      mojom::RequestContextFrameType::kTopLevel, nullptr);
+      mojom::RequestContextFrameType::kTopLevel, nullptr, &holder.GetFrame());
 
   EXPECT_FALSE(request.IsAutomaticUpgrade());
   EXPECT_FALSE(request.UpgradeIfInsecure());
@@ -432,15 +462,76 @@
   request.SetRequestContext(mojom::blink::RequestContextType::AUDIO);
   TestFetchClientSettingsObject* settings =
       MakeGarbageCollected<TestFetchClientSettingsObject>();
+  settings->SetSecurityOrigin("https://example.test", "");
+
   // Used to get a non-null document.
   DummyPageHolder holder;
+  holder.GetFrame()
+      .DomWindow()
+      ->GetSecurityContext()
+      .SetSecurityOriginForTesting(settings->GetSecurityOrigin());
 
   MixedContentChecker::UpgradeInsecureRequest(
       request, settings, holder.GetDocument().GetExecutionContext(),
-      mojom::RequestContextFrameType::kTopLevel, nullptr);
+      mojom::RequestContextFrameType::kTopLevel, nullptr, &holder.GetFrame());
 
   EXPECT_FALSE(request.IsAutomaticUpgrade());
   EXPECT_FALSE(request.UpgradeIfInsecure());
 }
 
+TEST(MixedContentCheckerTest,
+     AutoupgradeMixedContentInOpaqueOriginIfPrecursorIsSecure) {
+  test::TaskEnvironment task_environment;
+  ResourceRequest request;
+  request.SetUrl(KURL("http://example.test"));
+  request.SetRequestContext(mojom::blink::RequestContextType::IMAGE);
+  TestFetchClientSettingsObject* settings =
+      MakeGarbageCollected<TestFetchClientSettingsObject>();
+  // Set the security origin to an opaque one, with a secure precursor.
+  settings->SetSecurityOrigin(
+      "data:text/html,<img src=http://example.test/insecureimage.jpg>",
+      "https://example.test");
+
+  // Used to get a non-null document.
+  DummyPageHolder holder;
+  holder.GetFrame()
+      .DomWindow()
+      ->GetSecurityContext()
+      .SetSecurityOriginForTesting(settings->GetSecurityOrigin());
+
+  MixedContentChecker::UpgradeInsecureRequest(
+      request, settings, holder.GetDocument().GetExecutionContext(),
+      mojom::RequestContextFrameType::kTopLevel, nullptr, &holder.GetFrame());
+
+  EXPECT_TRUE(request.IsAutomaticUpgrade());
+  EXPECT_TRUE(request.UpgradeIfInsecure());
+}
+
+TEST(MixedContentCheckerTest,
+     DontAutoupgradeMixedContentInOpaqueOriginIfPrecursorIsNotSecure) {
+  test::TaskEnvironment task_environment;
+  ResourceRequest request;
+  request.SetUrl(KURL("http://example.test"));
+  request.SetRequestContext(mojom::blink::RequestContextType::IMAGE);
+  TestFetchClientSettingsObject* settings =
+      MakeGarbageCollected<TestFetchClientSettingsObject>();
+  // Set the security origin to an opaque one, with a not secure precursor.
+  settings->SetSecurityOrigin(
+      "data:text/html,<img src=http://example.test/insecureimage.jpg>",
+      "http://example.test");
+
+  // Used to get a non-null document.
+  DummyPageHolder holder;
+  holder.GetFrame()
+      .DomWindow()
+      ->GetSecurityContext()
+      .SetSecurityOriginForTesting(settings->GetSecurityOrigin());
+
+  MixedContentChecker::UpgradeInsecureRequest(
+      request, settings, holder.GetDocument().GetExecutionContext(),
+      mojom::RequestContextFrameType::kTopLevel, nullptr, &holder.GetFrame());
+
+  EXPECT_FALSE(request.IsAutomaticUpgrade());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/loader/preload_helper.cc b/third_party/blink/renderer/core/loader/preload_helper.cc
index 6dbb02e..c9a105a 100644
--- a/third_party/blink/renderer/core/loader/preload_helper.cc
+++ b/third_party/blink/renderer/core/loader/preload_helper.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/core/loader/preload_helper.h"
 
+#include "base/feature_list.h"
 #include "base/metrics/histogram_functions.h"
 #include "base/timer/elapsed_timer.h"
 #include "third_party/blink/public/common/features.h"
@@ -246,6 +247,23 @@
   }
 }
 
+bool IsSubresourceLoad(PreloadHelper::LoadLinksFromHeaderMode mode) {
+  switch (mode) {
+    case PreloadHelper::LoadLinksFromHeaderMode::kDocumentBeforeCommit:
+    case PreloadHelper::LoadLinksFromHeaderMode::
+        kDocumentAfterCommitWithoutViewport:
+    case PreloadHelper::LoadLinksFromHeaderMode::
+        kDocumentAfterCommitWithViewport:
+    case PreloadHelper::LoadLinksFromHeaderMode::kDocumentAfterLoadCompleted:
+      return false;
+    case PreloadHelper::LoadLinksFromHeaderMode::kSubresourceFromMemoryCache:
+    case PreloadHelper::LoadLinksFromHeaderMode::kSubresourceNotFromMemoryCache:
+      return true;
+    default:
+      NOTREACHED();
+  }
+}
+
 }  // namespace
 
 void PreloadHelper::DnsPrefetchIfNeeded(
@@ -790,6 +808,15 @@
     LinkLoadParameters params(header, base_url);
     bool change_rel_to_prefetch = false;
 
+    // For security purposes, set `referrerpolicy: "no-referrer"` in link loads
+    // from subresources. See https://crbug.com/415810136 for details.
+    if (base::FeatureList::IsEnabled(
+            blink::features::kNoReferrerForPreloadFromSubresource)) {
+      if (IsSubresourceLoad(mode)) {
+        params.referrer_policy = network::mojom::ReferrerPolicy::kNever;
+      }
+    }
+
     if (params.rel.IsLinkPreload() && recursive_prefetch_token) {
       // Only preload headers are expected to have a recursive prefetch token
       // In response to that token's existence, we treat the request as a
diff --git a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
index fd44637..f381ca4d 100644
--- a/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
+++ b/third_party/blink/renderer/core/loader/resource/font_resource_test.cc
@@ -257,7 +257,7 @@
       *MakeGarbageCollected<CSSUrlData>(
           AtomicString(url.GetString()), url,
           Referrer(document.Url(), document.GetReferrerPolicy()),
-          /*origin_clean=*/true, /*is_ad_related=*/false));
+          OriginClean::kTrue, false /* is_ad_related */));
   auto* src_value =
       CSSFontFaceSrcValue::Create(src_uri_value, nullptr /* world */);
 
diff --git a/third_party/blink/renderer/core/loader/worker_fetch_context.cc b/third_party/blink/renderer/core/loader/worker_fetch_context.cc
index f6117f1..d6df000 100644
--- a/third_party/blink/renderer/core/loader/worker_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/worker_fetch_context.cc
@@ -242,7 +242,7 @@
   MixedContentChecker::UpgradeInsecureRequest(
       request, &GetResourceFetcherProperties().GetFetchClientSettingsObject(),
       global_scope_, mojom::blink::RequestContextFrameType::kNone,
-      global_scope_->ContentSettingsClient());
+      global_scope_->ContentSettingsClient(), nullptr);
 }
 
 void WorkerFetchContext::PopulateResourceRequestBeforeCacheAccess(
diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h
index 7412056..038c2c38 100644
--- a/third_party/blink/renderer/core/page/page.h
+++ b/third_party/blink/renderer/core/page/page.h
@@ -715,12 +715,8 @@
   bool is_prerendering_ = false;
   String prerender_metric_suffix_;
 
-  // If true, warms up compositor on a certain loading event if the page is
-  // under prerendering. Only valid when the cc feature `kWarmUpCompositor`
-  // (controls the independent cc internal feature) and blink feature
-  // `kPrerender2WarmUpCompositor` (manages the trigger point of that cc
-  // feature for prerender case) are enabled. Please see crbug.com/41496019 for
-  // more details.
+  // If true, warms up compositor on `WebLocalFrameImpl::DidCommitLoad` if the
+  // page is under prerendering.
   bool should_warm_up_compositor_on_prerender_ = false;
   // If true, prepares the paint tree if the page is under prerendering.
   bool should_prepare_paint_tree_on_prerender_ = false;
diff --git a/third_party/blink/renderer/core/paint/inline_paint_context.cc b/third_party/blink/renderer/core/paint/inline_paint_context.cc
index 21b65b3..0de61e20 100644
--- a/third_party/blink/renderer/core/paint/inline_paint_context.cc
+++ b/third_party/blink/renderer/core/paint/inline_paint_context.cc
@@ -123,8 +123,7 @@
             return 0;
           }
           if (decorations->size() == 1 &&
-              (decorations->front().Lines() == style->GetTextDecorationLine() ||
-               !RuntimeEnabledFeatures::CssDecoratingBoxPseudoFixEnabled())) {
+              decorations->front().Lines() == style->GetTextDecorationLine()) {
             inline_context_->ClearDecoratingBoxes(saved_decorating_boxes_);
             PushDecoratingBox(item, *layout_object, *style, *decorations);
             return 1;
diff --git a/third_party/blink/renderer/core/style/computed_style.cc b/third_party/blink/renderer/core/style/computed_style.cc
index 33aeade..148bfd23 100644
--- a/third_party/blink/renderer/core/style/computed_style.cc
+++ b/third_party/blink/renderer/core/style/computed_style.cc
@@ -761,208 +761,8 @@
     const ComputedStyle& other) const {
   StyleDifference diff;
   uint64_t field_diff = FieldInvalidationDiff(*this, other);
-  while (field_diff) {
-    FieldDifference field =
-        static_cast<FieldDifference>(field_diff & (~field_diff + 1));
-    field_diff &= field_diff - 1;  // Clear the lowest bit.
 
-    switch (field) {
-      case kAccentColor:
-        if (AccentColorResolved() != other.AccentColorResolved()) {
-          diff.SetNeedsNormalPaintInvalidation();
-        }
-        break;
-      case kBackground:
-        if (!BackgroundInternal().VisuallyEqual(other.BackgroundInternal())) {
-          diff.SetNeedsNormalPaintInvalidation();
-        }
-        break;
-      case kBackgroundColor:
-        // If the background-color change is not due to a composited animation,
-        // then paint invalidation is required; but we can defer the decision
-        // until we know whether the color change will be rendered by the
-        // compositor.
-        diff.SetBackgroundColorChanged();
-        break;
-      case kBlendMode:
-        diff.SetBlendModeChanged();
-        break;
-      case kBorderImage:
-        if (!BorderVisualOverflowEqual(other)) {
-          diff.SetNeedsRecomputeVisualOverflow();
-        }
-        break;
-      case kBorderOutlineVisitedColor:
-        if (BorderOutlineVisitedColorChanged(other)) {
-          diff.SetNeedsNormalPaintInvalidation();
-        }
-        break;
-      case kBorderRadius:
-        diff.SetBorderRadiusChanged();
-        break;
-      case kBorderVisual:
-        if (!BorderVisuallyEqual(other)) {
-          diff.SetNeedsNormalPaintInvalidation();
-        }
-        break;
-      case kBorderWidth:
-        if (BorderTopWidth() != other.BorderTopWidth() ||
-            BorderRightWidth() != other.BorderRightWidth() ||
-            BorderBottomWidth() != other.BorderBottomWidth() ||
-            BorderLeftWidth() != other.BorderLeftWidth()) {
-          diff.SetNeedsFullLayout();
-        }
-        break;
-      case kClip: {
-        bool has_clip = HasOutOfFlowPosition() && !HasAutoClip();
-        bool other_has_clip =
-            other.HasOutOfFlowPosition() && !other.HasAutoClip();
-        if (has_clip != other_has_clip ||
-            (has_clip && Clip() != other.Clip())) {
-          diff.SetCSSClipChanged();
-        }
-        break;
-      }
-      case kClipPath:
-        diff.SetClipPathChanged();
-        break;
-      case kColor:
-        diff.SetTextDecorationOrColorChanged();
-        // If the (current)color changes and a filter or backdrop-filter uses
-        // it, the filter or backdrop-filter needs to be updated.
-        if (HasFilter() && Filter().UsesCurrentColor()) {
-          diff.SetFilterChanged();
-        }
-        if (HasBackdropFilter() && BackdropFilter().UsesCurrentColor()) {
-          diff.SetCompositingReasonsChanged();
-        }
-        break;
-      case kCompositing:
-        diff.SetCompositingReasonsChanged();
-        break;
-      case kCornerShape:
-        // Unused.
-        break;
-      case kCurrentcolor:
-        // If a property has a value that contains a <color> that depends on
-        // 'currentcolor', for example:
-        //
-        //   background-image: linear-gradient(currentColor, #fff)
-        //   background-color: color-mix(in srgb, currentcolor ...)
-        //
-        // If the (current)color has changed, we need to recompute it even
-        // though the old and new property values are identical.
-        //
-        // NOTE: This is also handled to some degree by
-        // LayoutObject::AdjustStyleDifference. We should probably
-        // re-distribute the responsibilities between these two locations.
-        if ((GetCurrentColor() != other.GetCurrentColor() ||
-             GetInternalVisitedCurrentColor() !=
-                 other.GetInternalVisitedCurrentColor()) &&
-            HasPropertyDependingOnCurrentColor()) {
-          diff.SetNeedsNormalPaintInvalidation();
-        }
-        break;
-      case kFilterData:
-        diff.SetFilterChanged();
-        break;
-      case kHasTransform:
-        if (HasTransform() != other.HasTransform()) {
-          diff.SetOtherTransformPropertyChanged();
-        }
-        break;
-      case kInset:
-        if (!diff.NeedsFullLayout() && HasInFlowPosition()) {
-          diff.SetNeedsPositionedMovementLayout();
-        }
-        break;
-      case kLayout:
-        diff.SetNeedsFullLayout();
-        break;
-      case kMargin:
-        if (!HasOutOfFlowPosition()) {
-          diff.SetNeedsFullLayout();
-        }
-        break;
-      case kMask:
-        diff.SetMaskChanged();
-        break;
-      case kOpacity:
-        diff.SetOpacityChanged();
-        break;
-      case kOutline:
-        if (!OutlineVisuallyEqual(other)) {
-          diff.SetNeedsNormalPaintInvalidation();
-          diff.SetNeedsRecomputeVisualOverflow();
-        }
-        break;
-      case kOutOfFlow:
-        if (!diff.NeedsFullLayout() && HasOutOfFlowPosition()) {
-          diff.SetNeedsPositionedMovementLayout();
-        }
-        break;
-      case kPaint:
-        diff.SetNeedsNormalPaintInvalidation();
-        break;
-      case kReshape:
-        diff.SetNeedsReshape();
-        diff.SetNeedsFullLayout();
-        diff.SetNeedsNormalPaintInvalidation();
-        break;
-      case kScrollAnchor:
-        diff.SetScrollAnchorDisablingPropertyChanged();
-        break;
-      case kScrollbarColor:
-        if (UsedScrollbarColor() != other.UsedScrollbarColor()) {
-          diff.SetNeedsNormalPaintInvalidation();
-        }
-        break;
-      case kScrollbarStyle:
-        if (HasPseudoElementStyle(kPseudoIdScrollbar) !=
-                other.HasPseudoElementStyle(kPseudoIdScrollbar) ||
-            UsesStandardScrollbarStyle() !=
-                other.UsesStandardScrollbarStyle()) {
-          diff.SetNeedsFullLayout();
-          diff.SetNeedsNormalPaintInvalidation();
-        }
-        break;
-      case kStroke:
-        if (HasStroke() != other.HasStroke() ||
-            HasDashArray() != other.HasDashArray()) {
-          diff.SetNeedsFullLayout();
-        }
-        break;
-      case kTextDecoration:
-        diff.SetTextDecorationOrColorChanged();
-        if (TextDecorationVisualOverflowChanged(other)) {
-          diff.SetNeedsRecomputeVisualOverflow();
-        }
-        break;
-      case kTransformData:
-        diff.SetTransformDataChanged();
-        break;
-      case kTransformOther:
-        diff.SetOtherTransformPropertyChanged();
-        break;
-      case kTransformProperty:
-        diff.SetTransformPropertyChanged();
-        break;
-      case kVisibility:
-        if ((Visibility() == EVisibility::kCollapse) !=
-            (other.Visibility() == EVisibility::kCollapse)) {
-          diff.SetNeedsFullLayout();
-        }
-        break;
-      case kVisualOverflow:
-        diff.SetNeedsRecomputeVisualOverflow();
-        break;
-      case kZIndex:
-        diff.SetZIndexChanged();
-        break;
-    }
-  }
-
-  if (ShouldWrapLine() != other.ShouldWrapLine()) {
+  if ((field_diff & kReshape) || ShouldWrapLine() != other.ShouldWrapLine()) {
     diff.SetNeedsReshape();
     diff.SetNeedsFullLayout();
     diff.SetNeedsNormalPaintInvalidation();
@@ -981,34 +781,124 @@
     diff.SetNeedsNormalPaintInvalidation();
   }
 
-  if (!diff.NeedsFullLayout()) {
-    if (IsDisplayLayoutCustomBox() &&
-        DiffNeedsFullLayoutForLayoutCustom(document, other)) {
-      diff.SetNeedsFullLayout();
-    }
-    if (DisplayLayoutCustomParentName() &&
-        DiffNeedsFullLayoutForLayoutCustomChild(document, other)) {
-      diff.SetNeedsFullLayout();
+  if (!diff.NeedsFullLayout() &&
+      DiffNeedsFullLayout(document, other, field_diff)) {
+    diff.SetNeedsFullLayout();
+  }
+
+  if (!diff.NeedsLayout()) {
+    if ((field_diff & kOutOfFlow) && HasOutOfFlowPosition()) {
+      diff.SetNeedsPositionedMovementLayout();
+    } else if ((field_diff & kInset) && HasInFlowPosition()) {
+      diff.SetNeedsPositionedMovementLayout();
     }
   }
 
-  if (!diff.NeedsNormalPaintInvalidation() && PaintImagesInternal()) {
-    for (const auto& image : PaintImagesInternal()->Images()) {
-      DCHECK(image);
-      if (DiffNeedsPaintInvalidationForPaintImage(*image, other, document)) {
-        diff.SetNeedsNormalPaintInvalidation();
-        break;
-      }
-    }
+  if (!diff.NeedsNormalPaintInvalidation() &&
+      DiffNeedsNormalPaintInvalidation(document, other, field_diff)) {
+    diff.SetNeedsNormalPaintInvalidation();
+  }
+
+  if (DiffNeedsRecomputeVisualOverflow(other, field_diff)) {
+    diff.SetNeedsRecomputeVisualOverflow();
   }
 
   if (DiffCompositingReasonsChanged(other, field_diff)) {
     diff.SetCompositingReasonsChanged();
   }
 
+  if (field_diff & kBackgroundColor) {
+    // If the background color change is not due to a composited animation,
+    // then paint invalidation is required; but we can defer the decision until
+    // we know whether the color change will be rendered by the compositor.
+    diff.SetBackgroundColorChanged();
+  }
+  if (field_diff & kBlendMode) {
+    diff.SetBlendModeChanged();
+  }
+  if (field_diff & kBorderRadius) {
+    diff.SetBorderRadiusChanged();
+  }
+  if (field_diff & kClip) {
+    bool has_clip = HasOutOfFlowPosition() && !HasAutoClip();
+    bool other_has_clip = other.HasOutOfFlowPosition() && !other.HasAutoClip();
+    if (has_clip != other_has_clip || (has_clip && Clip() != other.Clip())) {
+      diff.SetCSSClipChanged();
+    }
+  }
+  if (field_diff & kClipPath) {
+    diff.SetClipPathChanged();
+  }
+  if (field_diff & kColor) {
+    diff.SetTextDecorationOrColorChanged();
+  }
+  if (field_diff & kFilterData) {
+    diff.SetFilterChanged();
+  }
+  if (field_diff & kHasTransform) {
+    if (HasTransform() != other.HasTransform()) {
+      diff.SetOtherTransformPropertyChanged();
+    }
+  }
+  if (field_diff & kMask) {
+    diff.SetMaskChanged();
+  }
+  if (field_diff & kOpacity) {
+    diff.SetOpacityChanged();
+  }
+  if (field_diff & kScrollbarColor) {
+    if (UsedScrollbarColor() != other.UsedScrollbarColor()) {
+      diff.SetNeedsNormalPaintInvalidation();
+    }
+  }
+  if (field_diff & kScrollbarStyle) {
+    if (HasPseudoElementStyle(kPseudoIdScrollbar) !=
+            other.HasPseudoElementStyle(kPseudoIdScrollbar) ||
+        UsesStandardScrollbarStyle() != other.UsesStandardScrollbarStyle()) {
+      diff.SetNeedsFullLayout();
+      diff.SetNeedsNormalPaintInvalidation();
+    }
+  }
+  if (field_diff & kTextDecoration) {
+    diff.SetTextDecorationOrColorChanged();
+  }
+  if (field_diff & kTransformData) {
+    diff.SetTransformDataChanged();
+  }
+  if (field_diff & kTransformOther) {
+    diff.SetOtherTransformPropertyChanged();
+  }
+  if (field_diff & kTransformProperty) {
+    diff.SetTransformPropertyChanged();
+  }
+  if (field_diff & kVisibility) {
+    if ((Visibility() == EVisibility::kCollapse) !=
+        (other.Visibility() == EVisibility::kCollapse)) {
+      diff.SetNeedsFullLayout();
+    }
+  }
+  if (field_diff & kZIndex) {
+    diff.SetZIndexChanged();
+  }
+
+  // If the (current)color changes and a filter or backdrop-filter uses it, the
+  // filter or backdrop-filter needs to be updated. This reads
+  // `diff.TextDecorationOrColorChanged()` and so needs to be after the setters,
+  // above.
+  if (diff.TextDecorationOrColorChanged()) {
+    if (HasFilter() && Filter().UsesCurrentColor()) {
+      diff.SetFilterChanged();
+    }
+    if (HasBackdropFilter() && BackdropFilter().UsesCurrentColor()) {
+      // This could be optimized with a targeted backdrop-filter-changed
+      // invalidation.
+      diff.SetCompositingReasonsChanged();
+    }
+  }
+
   // The following condition needs to be at last, because it may depend on
   // conditions in diff computed above.
-  if (diff.TransformChanged()) {
+  if ((field_diff & kScrollAnchor) || diff.TransformChanged()) {
     diff.SetScrollAnchorDisablingPropertyChanged();
   }
 
@@ -1056,6 +946,48 @@
   return false;
 }
 
+bool ComputedStyle::DiffNeedsFullLayout(const Document& document,
+                                        const ComputedStyle& other,
+                                        uint64_t field_diff) const {
+  if (field_diff & kLayout) {
+    return true;
+  }
+
+  if (field_diff & kBorderWidth) {
+    if (BorderTopWidth() != other.BorderTopWidth() ||
+        BorderRightWidth() != other.BorderRightWidth() ||
+        BorderBottomWidth() != other.BorderBottomWidth() ||
+        BorderLeftWidth() != other.BorderLeftWidth()) {
+      return true;
+    }
+  }
+
+  if ((field_diff & kMargin) && !HasOutOfFlowPosition()) {
+    return true;
+  }
+
+  if (field_diff & kStroke) {
+    if (HasStroke() != other.HasStroke()) {
+      return true;
+    }
+    if (HasDashArray() != other.HasDashArray()) {
+      return true;
+    }
+  }
+
+  if (IsDisplayLayoutCustomBox() &&
+      DiffNeedsFullLayoutForLayoutCustom(document, other)) {
+    return true;
+  }
+
+  if (DisplayLayoutCustomParentName() &&
+      DiffNeedsFullLayoutForLayoutCustomChild(document, other)) {
+    return true;
+  }
+
+  return false;
+}
+
 bool ComputedStyle::DiffNeedsFullLayoutForLayoutCustom(
     const Document& document,
     const ComputedStyle& other) const {
@@ -1115,6 +1047,70 @@
   return false;
 }
 
+bool ComputedStyle::DiffNeedsNormalPaintInvalidation(
+    const Document& document,
+    const ComputedStyle& other,
+    uint64_t field_diff) const {
+  if (field_diff & kPaint) {
+    return true;
+  }
+
+  if ((field_diff & kAccentColor) &&
+      AccentColorResolved() != other.AccentColorResolved()) {
+    return true;
+  }
+
+  if ((field_diff & kOutline) && !OutlineVisuallyEqual(other)) {
+    return true;
+  }
+
+  if ((field_diff & kBackground) &&
+      !BackgroundInternal().VisuallyEqual(other.BackgroundInternal())) {
+    return true;
+  }
+
+  if (field_diff & kCurrentcolor) {
+    // If a property has a value that contains a <color> that depends on
+    // 'currentcolor', for example:
+    //
+    //   background-image: linear-gradient(currentColor, #fff)
+    //   background-color: color-mix(in srgb, currentcolor ...)
+    //
+    // If the (current)color has changed, we need to recompute it even though
+    // the old and new property values are identical.
+    //
+    // NOTE: This is also handled to some degree by
+    // LayoutObject::AdjustStyleDifference. We should probably re-distribute
+    // the responsibilities between these two locations.
+    if ((GetCurrentColor() != other.GetCurrentColor() ||
+         GetInternalVisitedCurrentColor() !=
+             other.GetInternalVisitedCurrentColor()) &&
+        HasPropertyDependingOnCurrentColor()) {
+      return true;
+    }
+  }
+
+  if ((field_diff & kBorderVisual) && !BorderVisuallyEqual(other)) {
+    return true;
+  }
+
+  if ((field_diff & kBorderOutlineVisitedColor) &&
+      BorderOutlineVisitedColorChanged(other)) {
+    return true;
+  }
+
+  if (PaintImagesInternal()) {
+    for (const auto& image : PaintImagesInternal()->Images()) {
+      DCHECK(image);
+      if (DiffNeedsPaintInvalidationForPaintImage(*image, other, document)) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
 bool ComputedStyle::DiffNeedsPaintInvalidationForPaintImage(
     const StyleImage& image,
     const ComputedStyle& other,
@@ -1196,8 +1192,35 @@
              other);
 }
 
+bool ComputedStyle::DiffNeedsRecomputeVisualOverflow(
+    const ComputedStyle& other,
+    uint64_t field_diff) const {
+  if (field_diff & kVisualOverflow) {
+    return true;
+  }
+
+  if ((field_diff & kBorderImage) && !BorderVisualOverflowEqual(other)) {
+    return true;
+  }
+
+  if ((field_diff & kOutline) && !OutlineVisuallyEqual(other)) {
+    return true;
+  }
+
+  if ((field_diff & kTextDecoration) &&
+      TextDecorationVisualOverflowChanged(other)) {
+    return true;
+  }
+
+  return false;
+}
+
 bool ComputedStyle::DiffCompositingReasonsChanged(const ComputedStyle& other,
                                                   uint64_t field_diff) const {
+  if (field_diff & kCompositing) {
+    return true;
+  }
+
   if (UsedTransformStyle3D() != other.UsedTransformStyle3D()) {
     return true;
   }
diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h
index b6b3fef1..61cc0ef 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -2611,14 +2611,22 @@
 
   bool DiffNeedsFullLayoutAndPaintInvalidation(const ComputedStyle& other,
                                                uint64_t field_diff) const;
+  bool DiffNeedsFullLayout(const Document&,
+                           const ComputedStyle& other,
+                           uint64_t field_diff) const;
   bool DiffNeedsFullLayoutForLayoutCustom(const Document&,
                                           const ComputedStyle& other) const;
   bool DiffNeedsFullLayoutForLayoutCustomChild(
       const Document&,
       const ComputedStyle& other) const;
+  bool DiffNeedsNormalPaintInvalidation(const Document&,
+                                        const ComputedStyle& other,
+                                        uint64_t field_diff) const;
   bool DiffNeedsPaintInvalidationForPaintImage(const StyleImage&,
                                                const ComputedStyle& other,
                                                const Document&) const;
+  bool DiffNeedsRecomputeVisualOverflow(const ComputedStyle& other,
+                                        uint64_t field_diff) const;
   bool DiffCompositingReasonsChanged(const ComputedStyle& other,
                                      uint64_t field_diff) const;
   bool PotentialCompositingReasonsFor3DTransformChanged(
diff --git a/third_party/blink/renderer/core/style/style_fetched_image.cc b/third_party/blink/renderer/core/style/style_fetched_image.cc
index d1b2f74b..18fbf0c 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image.cc
+++ b/third_party/blink/renderer/core/style/style_fetched_image.cc
@@ -37,13 +37,17 @@
 namespace blink {
 
 StyleFetchedImage::StyleFetchedImage(ImageResourceContent* image,
-                                     const CSSUrlData& url_data,
                                      const Document& document,
                                      bool is_lazyload_possibly_deferred,
+                                     bool is_from_origin_clean_style_sheet,
+                                     bool is_ad_related,
+                                     const KURL& url,
                                      const float override_image_resolution)
-    : url_data_(url_data),
-      document_(document),
-      override_image_resolution_(override_image_resolution) {
+    : document_(document),
+      url_(url),
+      override_image_resolution_(override_image_resolution),
+      is_from_origin_clean_style_sheet_(is_from_origin_clean_style_sheet),
+      is_ad_related_(is_ad_related) {
   is_image_resource_ = true;
   is_lazyload_possibly_deferred_ = is_lazyload_possibly_deferred;
 
@@ -67,8 +71,10 @@
   if (!other.IsImageResource()) {
     return false;
   }
+
   const auto& other_image = To<StyleFetchedImage>(other);
-  return image_ == other_image.image_ && *url_data_ == *other_image.url_data_ &&
+
+  return image_ == other_image.image_ && url_ == other_image.url_ &&
          EqualResolutions(override_image_resolution_,
                           other_image.override_image_resolution_);
 }
@@ -95,7 +101,12 @@
 
 CSSValue* StyleFetchedImage::CssValue() const {
   return MakeGarbageCollected<CSSImageValue>(
-      *url_data_->MakeComputed(), const_cast<StyleFetchedImage*>(this));
+      *MakeGarbageCollected<CSSUrlData>(
+          AtomicString(url_.GetString()), url_, Referrer(),
+          is_from_origin_clean_style_sheet_ ? OriginClean::kTrue
+                                            : OriginClean::kFalse,
+          is_ad_related_),
+      const_cast<StyleFetchedImage*>(this));
 }
 
 CSSValue* StyleFetchedImage::ComputedCSSValue(const ComputedStyle&,
@@ -129,10 +140,6 @@
   return false;
 }
 
-bool StyleFetchedImage::IsFromOriginCleanStyleSheet() const {
-  return url_data_->IsFromOriginCleanStyleSheet();
-}
-
 float StyleFetchedImage::ApplyImageResolution(float multiplier) const {
   const Image& image = *image_->GetImage();
   if (image.IsBitmapImage() && override_image_resolution_ > 0.0f) {
@@ -153,7 +160,7 @@
   gfx::SizeF size;
   if (auto* svg_image = DynamicTo<SVGImage>(image)) {
     const SVGImageViewInfo* view_info =
-        SVGImageForContainer::CreateViewInfo(*svg_image, FragmentIdentifier());
+        SVGImageForContainer::CreateViewInfo(*svg_image, url_);
     const gfx::SizeF unzoomed_default_object_size =
         gfx::ScaleSize(default_object_size, 1 / multiplier);
     size = SVGImageForContainer::ConcreteObjectSize(
@@ -172,7 +179,7 @@
   NaturalSizingInfo sizing_info;
   if (auto* svg_image = DynamicTo<SVGImage>(image)) {
     const SVGImageViewInfo* view_info =
-        SVGImageForContainer::CreateViewInfo(*svg_image, FragmentIdentifier());
+        SVGImageForContainer::CreateViewInfo(*svg_image, url_);
     sizing_info =
         SVGImageForContainer::GetNaturalDimensions(*svg_image, view_info)
             .value_or(NaturalSizingInfo::None());
@@ -191,7 +198,7 @@
   Image& image = *image_->GetImage();
   if (auto* svg_image = DynamicTo<SVGImage>(image)) {
     const SVGImageViewInfo* view_info =
-        SVGImageForContainer::CreateViewInfo(*svg_image, FragmentIdentifier());
+        SVGImageForContainer::CreateViewInfo(*svg_image, url_);
     std::optional<NaturalSizingInfo> natural_sizing_info =
         SVGImageForContainer::GetNaturalDimensions(*svg_image, view_info);
     return natural_sizing_info && !natural_sizing_info->IsNone();
@@ -244,7 +251,7 @@
     return image;
   }
   const SVGImageViewInfo* view_info =
-      SVGImageForContainer::CreateViewInfo(*svg_image, FragmentIdentifier());
+      SVGImageForContainer::CreateViewInfo(*svg_image, url_);
   return SVGImageForContainer::Create(
       *svg_image, target_size, style.EffectiveZoom(), view_info,
       document.GetStyleEngine().ResolveColorSchemeForEmbedding(&style));
@@ -286,26 +293,12 @@
   return true;
 }
 
-const String& StyleFetchedImage::FragmentIdentifier() const {
-  if (cached_fragment_identifier_.IsNull()) {
-    cached_fragment_identifier_ =
-        KURL(url_data_->ResolvedUrl()).FragmentIdentifier().ToString();
-    // If URL does not have a fragment identifier we'll get a null
-    // String. Normalize to the empty String to avoid repeated URL resolving.
-    if (!cached_fragment_identifier_) {
-      cached_fragment_identifier_ = g_empty_string;
-    }
-  }
-  return cached_fragment_identifier_;
-}
-
 bool StyleFetchedImage::CanBeSpeculativelyDecoded() const {
   return false;
 }
 
 void StyleFetchedImage::Trace(Visitor* visitor) const {
   visitor->Trace(image_);
-  visitor->Trace(url_data_);
   visitor->Trace(document_);
   StyleImage::Trace(visitor);
   ImageResourceObserver::Trace(visitor);
diff --git a/third_party/blink/renderer/core/style/style_fetched_image.h b/third_party/blink/renderer/core/style/style_fetched_image.h
index 0b34e14..89992cb 100644
--- a/third_party/blink/renderer/core/style/style_fetched_image.h
+++ b/third_party/blink/renderer/core/style/style_fetched_image.h
@@ -34,7 +34,6 @@
 
 namespace blink {
 
-class CSSUrlData;
 class Document;
 
 // This class represents an <image> that loads a single image resource (the
@@ -45,9 +44,11 @@
 
  public:
   StyleFetchedImage(ImageResourceContent* image,
-                    const CSSUrlData& url_data,
                     const Document& document,
                     bool is_lazyload_possibly_deferred,
+                    bool is_from_origin_clean_style_sheet,
+                    bool is_ad_related,
+                    const KURL& url,
                     const float override_image_resolution = 0.0f);
   ~StyleFetchedImage() override;
 
@@ -65,7 +66,6 @@
   bool IsLoading() const override;
   bool ErrorOccurred() const override;
   bool IsAccessAllowed(String&) const override;
-  bool IsFromOriginCleanStyleSheet() const override;
 
   NaturalSizingInfo GetNaturalSizingInfo(
       float multiplier,
@@ -91,6 +91,10 @@
 
   void Trace(Visitor*) const override;
 
+  bool IsFromOriginCleanStyleSheet() const override {
+    return is_from_origin_clean_style_sheet_;
+  }
+
  private:
   bool IsEqual(const StyleImage&) const override;
   void Prefinalize();
@@ -104,16 +108,19 @@
   bool GetImageAnimationPolicy(mojom::blink::ImageAnimationPolicy&) override;
   bool CanBeSpeculativelyDecoded() const override;
 
-  const String& FragmentIdentifier() const;
-
-  mutable String cached_fragment_identifier_;
   Member<ImageResourceContent> image_;
-  Member<const CSSUrlData> url_data_;
   Member<const Document> document_;
 
+  const KURL url_;
+
   // This overrides an images natural resolution.
   // A value of zero indicates no override.
   const float override_image_resolution_;
+
+  const bool is_from_origin_clean_style_sheet_;
+
+  // Whether this was created by an ad-related CSSParserContext.
+  const bool is_ad_related_;
 };
 
 template <>
diff --git a/third_party/blink/renderer/core/style/style_mask_source_image.cc b/third_party/blink/renderer/core/style/style_mask_source_image.cc
index ae73f17e..4eabef3 100644
--- a/third_party/blink/renderer/core/style/style_mask_source_image.cc
+++ b/third_party/blink/renderer/core/style/style_mask_source_image.cc
@@ -36,7 +36,7 @@
     const ComputedStyle& style,
     bool allow_visited_style,
     CSSValuePhase value_phase) const {
-  return resource_css_value_->ComputedCSSValue();
+  return resource_css_value_->ComputedCSSValueMaybeLocal();
 }
 
 bool StyleMaskSourceImage::CanRender() const {
diff --git a/third_party/blink/renderer/core/url_pattern/BUILD.gn b/third_party/blink/renderer/core/url_pattern/BUILD.gn
index 84616fbc3..c5f78da5 100644
--- a/third_party/blink/renderer/core/url_pattern/BUILD.gn
+++ b/third_party/blink/renderer/core/url_pattern/BUILD.gn
@@ -13,6 +13,7 @@
     "url_pattern_canon.h",
     "url_pattern_component.cc",
     "url_pattern_component.h",
+    "url_pattern_options.h",
   ]
 
   public_deps = [
diff --git a/third_party/blink/renderer/core/url_pattern/url_pattern.cc b/third_party/blink/renderer/core/url_pattern/url_pattern.cc
index eab01e9..22c3e12c 100644
--- a/third_party/blink/renderer/core/url_pattern/url_pattern.cc
+++ b/third_party/blink/renderer/core/url_pattern/url_pattern.cc
@@ -547,8 +547,7 @@
   if (exception_state.HadException())
     return nullptr;
 
-  Options urlpattern_options;
-  urlpattern_options.ignore_case = options->ignoreCase();
+  auto urlpattern_options = Options::FromV8URLPatternOptions(options);
 
   return MakeGarbageCollected<URLPattern>(
       protocol_component, username_component, password_component,
@@ -564,7 +563,7 @@
                        Component* pathname,
                        Component* search,
                        Component* hash,
-                       Options options,
+                       const Options& options,
                        base::PassKey<URLPattern> key)
     : protocol_(protocol),
       username_(username),
diff --git a/third_party/blink/renderer/core/url_pattern/url_pattern.h b/third_party/blink/renderer/core/url_pattern/url_pattern.h
index c51c78a..c2be7a1 100644
--- a/third_party/blink/renderer/core/url_pattern/url_pattern.h
+++ b/third_party/blink/renderer/core/url_pattern/url_pattern.h
@@ -9,6 +9,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_url_pattern_component.h"
 #include "third_party/blink/renderer/core/core_export.h"
 #include "third_party/blink/renderer/core/url_pattern/url_pattern_component.h"
+#include "third_party/blink/renderer/core/url_pattern/url_pattern_options.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/heap/member.h"
 #include "third_party/liburlpattern/parse.h"
@@ -24,9 +25,8 @@
 
 class CORE_EXPORT URLPattern : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
-  struct Options final {
-    bool ignore_case;
-  };
+
+  using Options = url_pattern::Options;
   using Component = url_pattern::Component;
 
  public:
@@ -72,7 +72,7 @@
              Component* pathname,
              Component* search,
              Component* hash,
-             Options options,
+             const Options& options,
              base::PassKey<URLPattern> key);
 
   bool test(ScriptState* script_state,
diff --git a/third_party/blink/renderer/core/url_pattern/url_pattern_options.h b/third_party/blink/renderer/core/url_pattern/url_pattern_options.h
new file mode 100644
index 0000000..fea7b83
--- /dev/null
+++ b/third_party/blink/renderer/core/url_pattern/url_pattern_options.h
@@ -0,0 +1,32 @@
+// 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_URL_PATTERN_URL_PATTERN_OPTIONS_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_URL_PATTERN_URL_PATTERN_OPTIONS_H_
+
+#include "base/check.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_url_pattern_options.h"
+
+namespace blink {
+
+class URLPatternOptions;
+
+namespace url_pattern {
+
+// A struct corresponds to `URLPatternOptions` in url_pattern_options.idl.
+struct Options {
+  bool ignore_case = false;
+
+  static Options FromV8URLPatternOptions(const URLPatternOptions* options) {
+    CHECK(options);
+    CHECK(options->hasIgnoreCase());
+
+    return {.ignore_case = options->ignoreCase()};
+  }
+};
+
+}  // namespace url_pattern
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_URL_PATTERN_URL_PATTERN_OPTIONS_H_
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
index 75c13500..7d39283 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -4459,7 +4459,8 @@
 }
 
 AXObject* AXNodeObject::ChooserPopup() const {
-  if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled()) {
+  if (RuntimeEnabledFeatures::SelectAccessibilityReparentInputEnabled() ||
+      RuntimeEnabledFeatures::SelectAccessibilityNestedInputEnabled()) {
     // The first input inside of a select filters the listbox, and therefore
     // controls it.
     if (auto* input = DynamicTo<HTMLInputElement>(GetNode())) {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
index 73efa1a..8d9a7a8 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
@@ -457,10 +457,11 @@
   }
 }
 
-base::span<std::pair<QualifiedName, uint32_t>>
+base::span<std::pair<QualifiedName, Element::TinyBloomFilter>>
 AXRelationCache::GetTextRelationAttributes() {
   // Avoid issues with commas within the type name in DEFINE_STATIC_LOCAL().
-  using QualifiedNameArray = std::array<std::pair<QualifiedName, uint32_t>, 3>;
+  using QualifiedNameArray =
+      std::array<std::pair<QualifiedName, Element::TinyBloomFilter>, 3>;
   DEFINE_STATIC_LOCAL(
       QualifiedNameArray, text_attributes,
       ({{html_names::kAriaLabelledbyAttr,
@@ -563,10 +564,11 @@
   }
 }
 
-base::span<std::pair<QualifiedName, uint32_t>>
+base::span<std::pair<QualifiedName, Element::TinyBloomFilter>>
 AXRelationCache::GetOtherRelationAttributes() {
   // Avoid issues with commas within the type name in DEFINE_STATIC_LOCAL().
-  using QualifiedNameArray = std::array<std::pair<QualifiedName, uint32_t>, 5>;
+  using QualifiedNameArray =
+      std::array<std::pair<QualifiedName, Element::TinyBloomFilter>, 5>;
   DEFINE_STATIC_LOCAL(
       QualifiedNameArray, attributes,
       ({{html_names::kAriaControlsAttr,
diff --git a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.cc b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.cc
index 8c102f3..b1b4965 100644
--- a/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.cc
+++ b/third_party/blink/renderer/modules/mediastream/media_stream_constraints_util_audio.cc
@@ -52,12 +52,6 @@
 using IntRangeSet = blink::media_constraints::NumericRangeSet<int>;
 using StringSet = blink::media_constraints::DiscreteSet<std::string>;
 
-// The presence of a MediaStreamAudioSource object indicates whether the source
-// in question is currently in use, or not. This convenience enum helps
-// identifying whether a source is available and, if so, whether it has audio
-// processing enabled or disabled.
-enum class SourceType { kNone, kUnprocessed, kNoApmProcessed, kApmProcessed };
-
 // The sample size is set to 16 due to the Signed-16 format representation.
 int32_t GetSampleSize() {
   return media::SampleFormatToBitsPerChannel(media::kSampleFormatS16);
@@ -120,35 +114,44 @@
   std::tuple<double, bool, EcModeScore, int> score;
 };
 
-// This class represents the output of DeviceContainer::InfoFromSource and is
-// used to obtain information regarding an active source, if that exists.
+// Information regarding an active source, if that exists.
 class SourceInfo {
  public:
-  SourceInfo(SourceType type,
-             const AudioProcessingProperties& properties,
-             std::optional<int> channels,
-             std::optional<int> sample_rate,
-             std::optional<double> latency)
-      : type_(type),
-        properties_(properties),
+  static std::optional<SourceInfo> FromSource(
+      blink::MediaStreamAudioSource* source) {
+    if (!source) {
+      return std::nullopt;
+    }
+
+    media::AudioParameters source_parameters = source->GetAudioParameters();
+    std::optional<AudioProcessingProperties> properties =
+        source->GetAudioProcessingProperties();
+    CHECK(properties);
+
+    return SourceInfo(*properties, source_parameters.channels(),
+                      source_parameters.sample_rate(),
+                      source_parameters.GetBufferDuration().InSecondsF());
+  }
+
+  const AudioProcessingProperties& properties() const { return properties_; }
+  int channels() const { return channels_; }
+  int sample_rate() const { return sample_rate_; }
+  double latency() const { return latency_; }
+
+ private:
+  SourceInfo(const AudioProcessingProperties& properties,
+             int channels,
+             int sample_rate,
+             double latency)
+      : properties_(properties),
         channels_(std::move(channels)),
         sample_rate_(std::move(sample_rate)),
         latency_(latency) {}
 
-  bool HasActiveSource() { return type_ != SourceType::kNone; }
-
-  SourceType type() { return type_; }
-  const AudioProcessingProperties& properties() { return properties_; }
-  const std::optional<int>& channels() { return channels_; }
-  const std::optional<int>& sample_rate() { return sample_rate_; }
-  const std::optional<double>& latency() { return latency_; }
-
- private:
-  const SourceType type_;
   const AudioProcessingProperties properties_;
-  const std::optional<int> channels_;
-  const std::optional<int> sample_rate_;
-  const std::optional<double> latency_;
+  const int channels_;
+  const int sample_rate_;
+  const double latency_;
 };
 
 // Container for each independent boolean constrainable property.
@@ -394,17 +397,17 @@
         is_device_capture_(true) {}
 
   EchoCancellationContainer(Vector<EchoCancellationType> allowed_values,
-                            bool has_active_source,
+                            std::optional<SourceInfo> source_info,
                             bool is_device_capture,
                             media::AudioParameters device_parameters,
-                            AudioProcessingProperties properties,
                             bool is_reconfiguration_allowed)
       : ec_mode_allowed_values_(
             EchoCancellationTypeSet(std::move(allowed_values))),
         device_parameters_(device_parameters),
         is_device_capture_(is_device_capture) {
-    if (!has_active_source)
+    if (!source_info) {
       return;
+    }
 
     // If HW echo cancellation is used, reconfiguration is not always supported
     // and only the current values are allowed. Otherwise, allow all possible
@@ -421,17 +424,17 @@
         // Allowing it when the system echo cancellation is enforced via flag,
         // for evaluation purposes.
         media::IsSystemEchoCancellationEnforced() ||
-        properties.echo_cancellation_type !=
+        source_info->properties().echo_cancellation_type !=
             EchoCancellationType::kEchoCancellationSystem;
 #endif
     if (is_reconfiguration_allowed && is_aec_reconfiguration_supported) {
       return;
     }
 
-    ec_mode_allowed_values_ =
-        EchoCancellationTypeSet({properties.echo_cancellation_type});
+    ec_mode_allowed_values_ = EchoCancellationTypeSet(
+        {source_info->properties().echo_cancellation_type});
     ec_allowed_values_ =
-        BoolSet({properties.echo_cancellation_type !=
+        BoolSet({source_info->properties().echo_cancellation_type !=
                  EchoCancellationType::kEchoCancellationDisabled});
   }
 
@@ -723,7 +726,7 @@
   // related |parameters.effects()|, and (b) any combination of processing
   // properties settings.
   static ProcessingBasedContainer CreateApmProcessedContainer(
-      const SourceInfo& source_info,
+      std::optional<SourceInfo> source_info,
       mojom::blink::MediaStreamType stream_type,
       bool is_device_capture,
       const media::AudioParameters& device_parameters,
@@ -748,7 +751,7 @@
   // allowed by the |parameters.effects()|, or none, while (b) all other
   // processing properties settings cannot be enabled.
   static ProcessingBasedContainer CreateNoApmProcessedContainer(
-      const SourceInfo& source_info,
+      std::optional<SourceInfo> source_info,
       bool is_device_capture,
       const media::AudioParameters& device_parameters,
       bool is_reconfiguration_allowed) {
@@ -771,7 +774,7 @@
   // allowed by the |parameters.effects()|, or none, while (c) all processing
   // properties settings cannot be enabled.
   static ProcessingBasedContainer CreateUnprocessedContainer(
-      const SourceInfo& source_info,
+      std::optional<SourceInfo> source_info,
       bool is_device_capture,
       const media::AudioParameters& device_parameters,
       bool is_reconfiguration_allowed) {
@@ -946,7 +949,7 @@
                            IntRangeSet sample_size_range,
                            Vector<int> channels_set,
                            IntRangeSet sample_rate_range,
-                           SourceInfo source_info,
+                           std::optional<SourceInfo> source_info,
                            bool is_device_capture,
                            media::AudioParameters device_parameters,
                            bool is_reconfiguration_allowed)
@@ -963,9 +966,8 @@
           EchoCancellationType::kEchoCancellationSystem);
     }
     echo_cancellation_container_ = EchoCancellationContainer(
-        std::move(echo_cancellation_types), source_info.HasActiveSource(),
-        is_device_capture, device_parameters, source_info.properties(),
-        is_reconfiguration_allowed);
+        std::move(echo_cancellation_types), source_info, is_device_capture,
+        device_parameters, is_reconfiguration_allowed);
 
     auto_gain_control_container_ =
         AutoGainControlContainer(auto_gain_control_set);
@@ -977,9 +979,8 @@
     // Allow the full set of supported values when the device is not open or
     // when the candidate settings would open the device using an unprocessed
     // source.
-    if (!source_info.HasActiveSource() ||
-        (is_reconfiguration_allowed &&
-         processing_type_ == ProcessingType::kUnprocessed)) {
+    if (!source_info || (is_reconfiguration_allowed &&
+                         processing_type_ == ProcessingType::kUnprocessed)) {
       return;
     }
 
@@ -988,19 +989,16 @@
     // for this is that opening multiple instances of the APM is costly.
     // TODO(crbug.com/1147928): Consider removing this restriction.
     auto_gain_control_container_ = AutoGainControlContainer(
-        BoolSet({source_info.properties().auto_gain_control}));
+        BoolSet({source_info->properties().auto_gain_control}));
 
-    noise_suppression_container_ =
-        BooleanContainer(BoolSet({source_info.properties().noise_suppression}));
+    noise_suppression_container_ = BooleanContainer(
+        BoolSet({source_info->properties().noise_suppression}));
 
-    DCHECK(source_info.channels());
-    channels_container_ = IntegerDiscreteContainer({*source_info.channels()});
-    DCHECK(source_info.sample_rate() != std::nullopt);
+    channels_container_ = IntegerDiscreteContainer({source_info->channels()});
     sample_rate_container_ = IntegerRangeContainer(
-        IntRangeSet::FromValue(*source_info.sample_rate()));
-    DCHECK(source_info.latency() != std::nullopt);
+        IntRangeSet::FromValue(source_info->sample_rate()));
     latency_container_ =
-        DoubleRangeContainer(DoubleRangeSet::FromValue(*source_info.latency()));
+        DoubleRangeContainer(DoubleRangeSet::FromValue(source_info->latency()));
   }
 
   // The allowed latency is expressed in a range latencies in seconds.
@@ -1078,8 +1076,8 @@
     // must be initialized such that their only supported values correspond to
     // the source settings. Otherwise, the containers are initialized to contain
     // all possible values.
-    SourceInfo source_info =
-        InfoFromSource(capability.source(), device_parameters_.effects());
+    std::optional<SourceInfo> source_info =
+        SourceInfo::FromSource(capability.source());
 
     // Three variations of the processing-based container. Each variant is
     // associated to a different type of audio processing configuration, namely
@@ -1098,8 +1096,9 @@
               is_reconfiguration_allowed));
       DCHECK_EQ(processing_based_containers_.size(), 3u);
 
-    if (source_info.type() == SourceType::kNone)
-      return;
+      if (!source_info) {
+        return;
+      }
 
     blink::MediaStreamAudioSource* source = capability.source();
     boolean_containers_[kDisableLocalEcho] =
@@ -1262,48 +1261,6 @@
           {kDisableLocalEcho, &ConstraintSet::disable_local_echo},
           {kRenderToAssociatedSink, &ConstraintSet::render_to_associated_sink}};
 
-  // Utility function to determine which version of this class should be
-  // allocated depending on the |source| provided.
-  static SourceInfo InfoFromSource(blink::MediaStreamAudioSource* source,
-                                   int effects) {
-    SourceType source_type;
-    AudioProcessingProperties properties;
-    auto* processed_source = ProcessedLocalAudioSource::From(source);
-    std::optional<int> channels;
-    std::optional<int> sample_rate;
-    std::optional<double> latency;
-
-    if (!source) {
-      source_type = SourceType::kNone;
-    } else {
-      media::AudioParameters source_parameters = source->GetAudioParameters();
-      channels = source_parameters.channels();
-      sample_rate = source_parameters.sample_rate();
-      latency = source_parameters.GetBufferDuration().InSecondsF();
-      properties = *(source->GetAudioProcessingProperties());
-
-      if (!processed_source) {
-        source_type = SourceType::kUnprocessed;
-        properties.DisableDefaultProperties();
-
-        // It is possible, however, that the HW echo canceller is enabled. In
-        // such case the property for echo cancellation type should be updated
-        // accordingly.
-        if (effects & media::AudioParameters::ECHO_CANCELLER) {
-          properties.echo_cancellation_type =
-              EchoCancellationType::kEchoCancellationSystem;
-        }
-      } else {
-        source_type = properties.EchoCancellationIsWebRtcProvided()
-                          ? SourceType::kApmProcessed
-                          : SourceType::kNoApmProcessed;
-        properties = processed_source->audio_processing_properties();
-      }
-    }
-
-    return SourceInfo(source_type, properties, channels, sample_rate, latency);
-  }
-
   media::AudioParameters device_parameters_;
   StringContainer device_id_container_;
   StringContainer group_id_container_;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.cc
index 647bca2..27a8cc2b 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame.cc
@@ -61,15 +61,6 @@
        current_metadata->sequenceNumber() != new_metadata->sequenceNumber())) {
     return SetMetadataValidationOutcome{false, "Bad sequenceNumber"};
   }
-  if (RuntimeEnabledFeatures::RTCEncodedAudioFrameAbsCaptureTimeEnabled()) {
-    if (new_metadata->hasAbsCaptureTime() !=
-            current_metadata->hasAbsCaptureTime() ||
-        (new_metadata->hasAbsCaptureTime() &&
-         current_metadata->absCaptureTime() !=
-             new_metadata->absCaptureTime())) {
-      return SetMetadataValidationOutcome{false, "Bad absoluteCaptureTime"};
-    }
-  }
   if (!new_metadata->hasRtpTimestamp()) {
     return SetMetadataValidationOutcome{false, "Bad rtpTimestamp"};
   }
@@ -171,11 +162,6 @@
   if (delegate_->SequenceNumber()) {
     metadata->setSequenceNumber(*delegate_->SequenceNumber());
   }
-  if (RuntimeEnabledFeatures::RTCEncodedAudioFrameAbsCaptureTimeEnabled()) {
-    if (delegate_->AbsCaptureTime()) {
-      metadata->setAbsCaptureTime(*delegate_->AbsCaptureTime());
-    }
-  }
   metadata->setRtpTimestamp(delegate_->RtpTimestamp());
   if (delegate_->MimeType()) {
     metadata->setMimeType(WTF::String::FromUTF8(*delegate_->MimeType()));
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.cc
index 59e7038..dfde719e 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.cc
@@ -106,12 +106,6 @@
   return contributing_sources_;
 }
 
-std::optional<uint64_t> RTCEncodedAudioFrameDelegate::AbsCaptureTime() const {
-  base::AutoLock lock(lock_);
-  return webrtc_frame_ ? webrtc_frame_->AbsoluteCaptureTimestamp()
-                       : std::nullopt;
-}
-
 std::optional<base::TimeTicks> RTCEncodedAudioFrameDelegate::ReceiveTime()
     const {
   base::AutoLock lock(lock_);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.h b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.h
index 682f49f..0adab343 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.h
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_delegate.h
@@ -41,7 +41,6 @@
   std::optional<std::string> MimeType() const;
   std::optional<uint16_t> SequenceNumber() const;
   Vector<uint32_t> ContributingSources() const;
-  std::optional<uint64_t> AbsCaptureTime() const;
   std::optional<base::TimeTicks> ReceiveTime() const;
   std::optional<base::TimeTicks> CaptureTime() const;
   std::optional<base::TimeDelta> SenderCaptureTimeOffset() const;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_metadata.idl b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_metadata.idl
index 16c994d0..ee7eb8d 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_metadata.idl
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_metadata.idl
@@ -11,7 +11,6 @@
     octet payloadType;
     DOMString mimeType;
     unsigned short? sequenceNumber;
-    [RuntimeEnabled=RTCEncodedAudioFrameAbsCaptureTime] unsigned long long absCaptureTime;
     unsigned long rtpTimestamp;
     [RuntimeEnabled=RTCEncodedFrameTimestamps] DOMHighResTimeStamp receiveTime;
     [RuntimeEnabled=RTCEncodedFrameTimestamps] DOMHighResTimeStamp captureTime;
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_test.cc b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_test.cc
index 3e00b76e..9cf2b63 100644
--- a/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_test.cc
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_encoded_audio_frame_test.cc
@@ -50,7 +50,6 @@
   ON_CALL(*frame, GetContributingSources()).WillByDefault(Return(csrcs));
   ON_CALL(*frame, GetPayloadType()).WillByDefault(Return(13));
   ON_CALL(*frame, SequenceNumber()).WillByDefault(Return(20));
-  ON_CALL(*frame, AbsoluteCaptureTimestamp()).WillByDefault(Return(70050));
   ON_CALL(*frame, GetTimestamp()).WillByDefault(Return(17));
   ON_CALL(*frame, GetMimeType()).WillByDefault(Return("image"));
 }
@@ -62,7 +61,6 @@
   ON_CALL(*frame, GetContributingSources()).WillByDefault(Return(csrcs));
   ON_CALL(*frame, GetPayloadType()).WillByDefault(Return(13));
   ON_CALL(*frame, SequenceNumber()).WillByDefault(Return(20));
-  ON_CALL(*frame, AbsoluteCaptureTimestamp()).WillByDefault(Return(70050));
   ON_CALL(*frame, GetTimestamp()).WillByDefault(Return(17));
   ON_CALL(*frame, GetMimeType()).WillByDefault(Return("image"));
   if (window) {
@@ -84,7 +82,6 @@
   new_metadata->setPayloadType(13);
   new_metadata->setMimeType("image");
   new_metadata->setSequenceNumber(20);
-  new_metadata->setAbsCaptureTime(70050);
   new_metadata->setRtpTimestamp(110);
   return new_metadata;
 }
@@ -108,7 +105,6 @@
   EXPECT_EQ(13, retrieved_metadata->payloadType());
   EXPECT_EQ("image", retrieved_metadata->mimeType());
   EXPECT_EQ(20u, retrieved_metadata->sequenceNumber());
-  EXPECT_EQ(70050u, retrieved_metadata->absCaptureTime());
   EXPECT_EQ(17u, retrieved_metadata->rtpTimestamp());
   EXPECT_TRUE(retrieved_metadata->hasReceiveTime());
   // The precision for DOMHighResTimestamp is 0.1ms. Test equality by making
@@ -204,7 +200,6 @@
   EXPECT_FALSE(new_frame->getMetadata(execution_context)->hasPayloadType());
   EXPECT_FALSE(new_frame->getMetadata(execution_context)->hasMimeType());
   EXPECT_FALSE(new_frame->getMetadata(execution_context)->hasSequenceNumber());
-  EXPECT_FALSE(new_frame->getMetadata(execution_context)->hasAbsCaptureTime());
   EXPECT_EQ(new_frame->getMetadata(execution_context)->rtpTimestamp(), 0u);
   EXPECT_FALSE(new_frame->getMetadata(execution_context)->hasReceiveTime());
 }
@@ -329,7 +324,6 @@
   EXPECT_EQ(13, new_frame_metadata->payloadType());
   EXPECT_EQ("image", new_frame_metadata->mimeType());
   EXPECT_EQ(20u, new_frame_metadata->sequenceNumber());
-  EXPECT_EQ(70050u, new_frame_metadata->absCaptureTime());
   EXPECT_EQ(17u, new_frame_metadata->rtpTimestamp());
   EXPECT_FALSE(new_frame_metadata->hasReceiveTime());
 }
@@ -369,8 +363,6 @@
   EXPECT_EQ(new_metadata->mimeType(), new_frame_metadata->mimeType());
   EXPECT_EQ(new_metadata->sequenceNumber(),
             new_frame_metadata->sequenceNumber());
-  EXPECT_EQ(new_metadata->absCaptureTime(),
-            new_frame_metadata->absCaptureTime());
   EXPECT_EQ(new_metadata->rtpTimestamp(), new_frame_metadata->rtpTimestamp());
   EXPECT_FALSE(new_metadata->hasReceiveTime());
 }
diff --git a/third_party/blink/renderer/modules/printing/web_printing_type_converters.cc b/third_party/blink/renderer/modules/printing/web_printing_type_converters.cc
index 64b895d0..44c4fe2 100644
--- a/third_party/blink/renderer/modules/printing/web_printing_type_converters.cc
+++ b/third_party/blink/renderer/modules/printing/web_printing_type_converters.cc
@@ -64,6 +64,10 @@
 using V8ColorMode = blink::V8WebPrintColorMode;
 using MojomColorMode = blink::mojom::blink::WebPrintColorMode;
 
+// print-quality:
+using V8Quality = blink::V8WebPrintQuality;
+using MojomQuality = blink::mojom::blink::WebPrintQuality;
+
 // printer-state:
 using V8PrinterState = blink::V8WebPrinterState;
 using MojomPrinterState = blink::mojom::blink::WebPrinterState;
@@ -272,6 +276,34 @@
 };
 
 template <>
+struct TypeConverter<V8Quality, MojomQuality> {
+  static V8Quality Convert(const MojomQuality& quality) {
+    switch (quality) {
+      case MojomQuality::kDraft:
+        return V8Quality(V8Quality::Enum::kDraft);
+      case MojomQuality::kNormal:
+        return V8Quality(V8Quality::Enum::kNormal);
+      case MojomQuality::kHigh:
+        return V8Quality(V8Quality::Enum::kHigh);
+    }
+  }
+};
+
+template <>
+struct TypeConverter<MojomQuality, V8Quality> {
+  static MojomQuality Convert(const V8Quality& quality) {
+    switch (quality.AsEnum()) {
+      case V8Quality::Enum::kDraft:
+        return MojomQuality::kDraft;
+      case V8Quality::Enum::kNormal:
+        return MojomQuality::kNormal;
+      case V8Quality::Enum::kHigh:
+        return MojomQuality::kHigh;
+    }
+  }
+};
+
+template <>
 struct TypeConverter<V8PrinterState::Enum, MojomPrinterState> {
   static V8PrinterState::Enum Convert(const MojomPrinterState& printer_state) {
     switch (printer_state) {
@@ -466,6 +498,16 @@
           new_attributes.print_color_mode_supported));
 }
 
+void ProcessPrintQuality(
+    const mojom::blink::WebPrinterAttributes& new_attributes,
+    WebPrinterAttributes* current_attributes) {
+  current_attributes->setPrintQualityDefault(
+      mojo::ConvertTo<V8Quality>(new_attributes.print_quality_default));
+  current_attributes->setPrintQualitySupported(
+      mojo::ConvertTo<Vector<V8Quality>>(
+          new_attributes.print_quality_supported));
+}
+
 void ProcessSides(const mojom::blink::WebPrinterAttributes& new_attributes,
                   WebPrinterAttributes* current_attributes) {
   if (new_attributes.sides_default) {
@@ -499,6 +541,7 @@
   blink::ProcessOrientationRequested(*printer_attributes, attributes);
   blink::ProcessPrinterResolution(*printer_attributes, attributes);
   blink::ProcessPrintColorMode(*printer_attributes, attributes);
+  blink::ProcessPrintQuality(*printer_attributes, attributes);
   blink::ProcessSides(*printer_attributes, attributes);
 
   attributes->setPrinterState(
@@ -543,6 +586,10 @@
     attributes->print_color_mode =
         mojo::ConvertTo<MojomColorMode>(pjt_attributes->printColorMode());
   }
+  if (pjt_attributes->hasPrintQuality()) {
+    attributes->print_quality =
+        mojo::ConvertTo<MojomQuality>(pjt_attributes->printQuality());
+  }
   if (pjt_attributes->hasSides()) {
     attributes->sides = mojo::ConvertTo<MojomSides>(pjt_attributes->sides());
   }
diff --git a/third_party/blink/renderer/modules/speech/speech_recognition.cc b/third_party/blink/renderer/modules/speech/speech_recognition.cc
index aad1a2d..22d74e1 100644
--- a/third_party/blink/renderer/modules/speech/speech_recognition.cc
+++ b/third_party/blink/renderer/modules/speech/speech_recognition.cc
@@ -90,8 +90,7 @@
   if (phrases->length() > 0 &&
       mode_ == V8SpeechRecognitionMode::Enum::kCloudOnly) {
     ErrorOccurred(media::mojom::blink::SpeechRecognitionError::New(
-        media::mojom::blink::SpeechRecognitionErrorCode::
-            kRecognitionContextNotSupported,
+        media::mojom::blink::SpeechRecognitionErrorCode::kPhrasesNotSupported,
         media::mojom::blink::SpeechAudioErrorDetails::kNone));
     return;
   }
@@ -123,8 +122,7 @@
   if (phrases_->length() > 0 &&
       mode == V8SpeechRecognitionMode::Enum::kCloudOnly && !started_) {
     ErrorOccurred(media::mojom::blink::SpeechRecognitionError::New(
-        media::mojom::blink::SpeechRecognitionErrorCode::
-            kRecognitionContextNotSupported,
+        media::mojom::blink::SpeechRecognitionErrorCode::kPhrasesNotSupported,
         media::mojom::blink::SpeechAudioErrorDetails::kNone));
     return;
   }
diff --git a/third_party/blink/renderer/modules/speech/speech_recognition_error_event.cc b/third_party/blink/renderer/modules/speech/speech_recognition_error_event.cc
index a45ab59..fe6ec8c 100644
--- a/third_party/blink/renderer/modules/speech/speech_recognition_error_event.cc
+++ b/third_party/blink/renderer/modules/speech/speech_recognition_error_event.cc
@@ -51,9 +51,8 @@
       return "bad-grammar";
     case media::mojom::blink::SpeechRecognitionErrorCode::kLanguageNotSupported:
       return "language-not-supported";
-    case media::mojom::blink::SpeechRecognitionErrorCode::
-        kRecognitionContextNotSupported:
-      return "recognition-context-not-supported";
+    case media::mojom::blink::SpeechRecognitionErrorCode::kPhrasesNotSupported:
+      return "phrases-not-supported";
     case media::mojom::blink::SpeechRecognitionErrorCode::kNoMatch:
       NOTREACHED();
   }
diff --git a/third_party/blink/renderer/platform/fonts/shaping/font_features.cc b/third_party/blink/renderer/platform/fonts/shaping/font_features.cc
index b5f8b64..6d9364b 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/font_features.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/font_features.cc
@@ -39,18 +39,6 @@
   return features.size() == 1 && features[0] == kChws;
 }
 
-const hb_feature_t* FontFeatures::ToHarfBuzzData() const {
-  return reinterpret_cast<const hb_feature_t*>(features_.data());
-}
-
-std::optional<uint32_t> FontFeatures::FindValueForTesting(uint32_t tag) const {
-  for (const FontFeatureRange& feature : features_) {
-    if (feature.tag == tag)
-      return feature.value;
-  }
-  return std::nullopt;
-}
-
 template <wtf_size_t InlineCapacity>
 void FontFeatureRange::FromFontDescription(
     const FontDescription& description,
@@ -261,15 +249,14 @@
   }
 }
 
-void FontFeatures::Initialize(const FontDescription& description) {
-  FontFeatureRange::FromFontDescription(description, features_);
-}
-
 //
 // Explicitly instantiate template functions.
 //
 template PLATFORM_EXPORT void FontFeatureRange::FromFontDescription(
     const FontDescription&,
     Vector<FontFeatureRange, FontFeatureRange::kInitialSize>&);
+template PLATFORM_EXPORT void FontFeatureRange::FromFontDescription(
+    const FontDescription&,
+    FontFeatures&);
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/shaping/font_features.h b/third_party/blink/renderer/platform/fonts/shaping/font_features.h
index a5ad3d3d..8e0bc7c 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/font_features.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/font_features.h
@@ -44,8 +44,6 @@
 //
 // Represents an OpenType font feature with its value and text range.
 //
-// This struct has the same size and layout as `hb_feature_t`.
-//
 struct PLATFORM_EXPORT FontFeatureRange : public FontFeatureValue {
   // The size produced by `FromFontDescription()` for the initial style.
   static constexpr wtf_size_t kInitialSize = 1;
@@ -58,6 +56,11 @@
   // True if the list is for the initial style.
   static bool IsInitial(base::span<const FontFeatureRange>);
 
+  // This struct has the same size and layout as `hb_feature_t`.
+  static const hb_feature_t* ToHarfBuzzData(const FontFeatureRange* features) {
+    return reinterpret_cast<const hb_feature_t*>(features);
+  }
+
   uint32_t start = 0;
   uint32_t end = static_cast<uint32_t>(-1);
 };
@@ -65,49 +68,7 @@
 //
 // Represents a list of `FontFeatureRange`.
 //
-class PLATFORM_EXPORT FontFeatures {
- public:
-  FontFeatures() = default;
-  explicit FontFeatures(base::span<const FontFeatureRange> features)
-      : features_(features) {}
-
-  // Initialize the list from |Font|.
-  void Initialize(const FontDescription&);
-
-  wtf_size_t size() const { return features_.size(); }
-  bool IsEmpty() const { return features_.empty(); }
-
-  const FontFeatureRange& operator[](wtf_size_t i) const {
-    return features_[i];
-  }
-  explicit operator base::span<const FontFeatureRange>() { return features_; }
-  const hb_feature_t* ToHarfBuzzData() const;
-
-  std::optional<uint32_t> FindValueForTesting(uint32_t tag) const;
-
-  void Reserve(wtf_size_t new_capacity) { features_.reserve(new_capacity); }
-
-  void Append(const FontFeatureRange& feature) { features_.push_back(feature); }
-  void Insert(const FontFeatureRange& feature) {
-    features_.push_front(feature);
-  }
-  void AppendVector(const FontFeatures& features) {
-    features_.AppendVector(features.features_);
-  }
-
-  void EraseAt(wtf_size_t position, wtf_size_t length) {
-    features_.EraseAt(position, length);
-  }
-  void Shrink(wtf_size_t size) { features_.Shrink(size); }
-
-  using FeatureArray = Vector<FontFeatureRange, 6>;
-  using const_iterator = FeatureArray::const_iterator;
-  const_iterator begin() const { return features_.begin(); }
-  const_iterator end() const { return features_.end(); }
-
- private:
-  FeatureArray features_;
-};
+using FontFeatures = Vector<FontFeatureRange, 6>;
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/platform/fonts/shaping/font_features_test.cc b/third_party/blink/renderer/platform/fonts/shaping/font_features_test.cc
index 34de94c3..2ce80720 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/font_features_test.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/font_features_test.cc
@@ -20,6 +20,15 @@
 //
 class FontFeaturesTest : public testing::Test {};
 
+std::optional<uint32_t> FindValue(uint32_t tag, const FontFeatures& features) {
+  for (const FontFeatureRange& feature : features) {
+    if (feature.tag == tag) {
+      return feature.value;
+    }
+  }
+  return std::nullopt;
+}
+
 static const FontOrientation orientations[] = {
     FontOrientation::kHorizontal,
     FontOrientation::kVerticalRotated,
@@ -46,13 +55,13 @@
   FontDescription font_description;
   font_description.SetOrientation(GetOrientation());
   FontFeatures features;
-  features.Initialize(font_description);
+  FontFeatureRange::FromFontDescription(font_description, features);
   if (IsHorizontal()) {
-    EXPECT_EQ(features.FindValueForTesting(chws), 1u);
-    EXPECT_EQ(features.FindValueForTesting(vchw), std::nullopt);
+    EXPECT_EQ(FindValue(chws, features), 1u);
+    EXPECT_EQ(FindValue(vchw, features), std::nullopt);
   } else {
-    EXPECT_EQ(features.FindValueForTesting(chws), std::nullopt);
-    EXPECT_EQ(features.FindValueForTesting(vchw), 1u);
+    EXPECT_EQ(FindValue(chws, features), std::nullopt);
+    EXPECT_EQ(FindValue(vchw, features), 1u);
   }
 }
 
@@ -68,13 +77,13 @@
     font_description.SetOrientation(GetOrientation());
     font_description.SetFeatureSettings(settings);
     FontFeatures features;
-    features.Initialize(font_description);
+    FontFeatureRange::FromFontDescription(font_description, features);
     if (IsHorizontal()) {
-      EXPECT_EQ(features.FindValueForTesting(chws), value);
-      EXPECT_EQ(features.FindValueForTesting(vchw), std::nullopt);
+      EXPECT_EQ(FindValue(chws, features), value);
+      EXPECT_EQ(FindValue(vchw, features), std::nullopt);
     } else {
-      EXPECT_EQ(features.FindValueForTesting(chws), std::nullopt);
-      EXPECT_EQ(features.FindValueForTesting(vchw), value);
+      EXPECT_EQ(FindValue(chws, features), std::nullopt);
+      EXPECT_EQ(FindValue(vchw, features), value);
     }
   }
 }
@@ -95,9 +104,9 @@
     font_description.SetOrientation(GetOrientation());
     font_description.SetFeatureSettings(settings);
     FontFeatures features;
-    features.Initialize(font_description);
-    EXPECT_EQ(features.FindValueForTesting(chws), std::nullopt);
-    EXPECT_EQ(features.FindValueForTesting(vchw), std::nullopt);
+    FontFeatureRange::FromFontDescription(font_description, features);
+    EXPECT_EQ(FindValue(chws, features), std::nullopt);
+    EXPECT_EQ(FindValue(vchw, features), std::nullopt);
   }
 }
 
@@ -117,10 +126,10 @@
   font_description.SetOrientation(GetOrientation());
   font_description.SetFeatureSettings(settings);
   FontFeatures features;
-  features.Initialize(font_description);
+  FontFeatureRange::FromFontDescription(font_description, features);
   // Check all features are enabled.
   for (const hb_tag_t tag : tags)
-    EXPECT_EQ(features.FindValueForTesting(tag), 1u);
+    EXPECT_EQ(FindValue(tag, features), 1u);
 }
 
 }  // namespace
diff --git a/third_party/blink/renderer/platform/fonts/shaping/han_kerning.cc b/third_party/blink/renderer/platform/fonts/shaping/han_kerning.cc
index b9cc13e..5dd3e01 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/han_kerning.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/han_kerning.cc
@@ -291,9 +291,9 @@
                                              : HB_TAG('v', 'h', 'a', 'l');
   features_ = features;
   num_features_before_ = features->size();
-  features->Reserve(features->size() + indices.size());
+  features->reserve(features->size() + indices.size());
   for (const wtf_size_t i : indices) {
-    features->Append({{tag, 1}, i, i + 1});
+    features->push_back(FontFeatureRange{{tag, 1}, i, i + 1});
   }
 }
 
diff --git a/third_party/blink/renderer/platform/fonts/shaping/han_kerning.h b/third_party/blink/renderer/platform/fonts/shaping/han_kerning.h
index ac75d99b..468b484 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/han_kerning.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/han_kerning.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SHAPING_HAN_KERNING_H_
 
 #include "base/gtest_prod_util.h"
+#include "third_party/blink/renderer/platform/fonts/shaping/font_features.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/text/han_kerning_char_type.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
@@ -15,7 +16,6 @@
 namespace blink {
 
 class FontDescription;
-class FontFeatures;
 class LayoutLocale;
 class SimpleFontData;
 
diff --git a/third_party/blink/renderer/platform/fonts/shaping/han_kerning_test.cc b/third_party/blink/renderer/platform/fonts/shaping/han_kerning_test.cc
index 47eaa84..0f4a072 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/han_kerning_test.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/han_kerning_test.cc
@@ -173,7 +173,8 @@
   const SimpleFontData* noto_cjk_data = noto_cjk->PrimaryFont();
   EXPECT_TRUE(noto_cjk_data);
   FontFeatures features;
-  features.Append({{{'T', 'E', 'S', 'T'}, 1}, 0, static_cast<unsigned>(-1)});
+  features.push_back(FontFeatureRange{
+      {{'T', 'E', 'S', 'T'}, 1}, 0, static_cast<unsigned>(-1)});
   EXPECT_EQ(features.size(), 1u);
   const String text(u"国)(国");
   {
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.cc
index 56aa7d4..fdd6e695a 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper.cc
@@ -382,9 +382,9 @@
       !resolved_features.empty()) {
     // Insert `resolved_features` before `font_features`.
     variant_features.emplace();
-    variant_features->Reserve(resolved_features.size() + font_features.size());
+    variant_features->reserve(resolved_features.size() + font_features.size());
     for (const FontFeatureValue& feature : resolved_features) {
-      variant_features->Append({feature});
+      variant_features->push_back(FontFeatureRange{feature});
     }
     variant_features->AppendVector(font_features);
   }
@@ -401,7 +401,8 @@
                               ? HarfBuzzFace::kPrepareForVerticalLayout
                               : HarfBuzzFace::kNoVerticalLayout,
                           specified_size);
-  hb_shape(hb_font, buffer, argument_features.ToHarfBuzzData(),
+  hb_shape(hb_font, buffer,
+           FontFeatureRange::ToHarfBuzzData(argument_features.data()),
            argument_features.size());
   if (!face->ShouldSubpixelPosition()) {
     RoundHarfBuzzBufferPositions(buffer);
@@ -897,7 +898,7 @@
 
 void CapsFeatureSettingsScopedOverlay::PrependCounting(
     const FontFeatureRange& feature) {
-  features_->Insert(feature);
+  features_->push_front(feature);
   count_features_++;
 }
 
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
index 4c105e9..67520f0 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result.cc
@@ -72,7 +72,7 @@
     Vector<int> offsets;
   } glyph_data;
   Member<void*> pointer2[2];
-  int integers[6];
+  int integers[5];
 };
 
 ASSERT_SIZE(ShapeResultRun, SameSizeAsRunInfo);
@@ -764,7 +764,7 @@
                                     const ShapeResultRun& run) const {
   auto glyph_offsets = run.glyph_data_.GetOffsets<has_non_zero_glyph_offsets>();
   auto total_advance = InlineLayoutUnit::FromFloatRound(initial_advance);
-  bool is_horizontal = HB_DIRECTION_IS_HORIZONTAL(run.direction_);
+  bool is_horizontal = run.IsHorizontal();
   for (const auto& glyph_data : run.glyph_data_) {
     glyph_callback(context, run.start_index_ + glyph_data.character_index,
                    glyph_data.glyph, *glyph_offsets, total_advance,
@@ -802,7 +802,7 @@
   auto glyph_offsets = run.glyph_data_.GetOffsets<has_non_zero_glyph_offsets>();
   auto total_advance = InlineLayoutUnit::FromFloatRound(initial_advance);
   unsigned run_start = run.start_index_ + index_offset;
-  bool is_horizontal = HB_DIRECTION_IS_HORIZONTAL(run.direction_);
+  bool is_horizontal = run.IsHorizontal();
   const SimpleFontData* font_data = run.font_data_.Get();
 
   if (run.IsLtr()) {  // Left-to-right
@@ -1600,9 +1600,8 @@
     return run->start_index_ > start_index;
   };
 
-  auto it = std::lower_bound(
-      runs_.begin(), runs_.end(), run->start_index_,
-      HB_DIRECTION_IS_FORWARD(run->direction_) ? ltr_comparer : rtl_comparer);
+  auto it = std::lower_bound(runs_.begin(), runs_.end(), run->start_index_,
+                             run->IsLtr() ? ltr_comparer : rtl_comparer);
   if (it != runs_.end()) {
     runs_.insert(static_cast<wtf_size_t>(it - runs_.begin()), run);
   } else {
@@ -2029,7 +2028,7 @@
     output->Append(", #chars=");
     output->AppendNumber(run.num_characters_);
     output->Append(", dir=");
-    output->AppendNumber(static_cast<uint32_t>(run.direction_));
+    output->AppendNumber(run.hb_direction_);
     output->Append(", glyphs[");
     output->AppendNumber(run.glyph_data_.size());
     output->Append("]{");
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_run.h b/third_party/blink/renderer/platform/fonts/shaping/shape_result_run.h
index cca612a..5aa0fc3 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_run.h
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_run.h
@@ -68,7 +68,7 @@
         num_characters_(num_characters),
         width_(0.0f),
         script_(script),
-        direction_(dir),
+        hb_direction_(dir),
         canvas_rotation_(canvas_rotation) {}
 
   ShapeResultRun(const ShapeResultRun& other)
@@ -79,7 +79,7 @@
         num_characters_(other.num_characters_),
         width_(other.width_),
         script_(other.script_),
-        direction_(other.direction_),
+        hb_direction_(other.hb_direction_),
         canvas_rotation_(other.canvas_rotation_) {}
 
   void Trace(Visitor* visitor) const {
@@ -90,9 +90,14 @@
 
   unsigned NumGlyphs() const { return glyph_data_.size(); }
   bool HasLigatures() const { return NumGlyphs() < num_characters_; }
-  bool IsLtr() const { return HB_DIRECTION_IS_FORWARD(direction_); }
-  bool IsRtl() const { return HB_DIRECTION_IS_BACKWARD(direction_); }
-  bool IsHorizontal() const { return HB_DIRECTION_IS_HORIZONTAL(direction_); }
+  hb_direction_t HbDirection() const {
+    return static_cast<hb_direction_t>(hb_direction_);
+  }
+  bool IsLtr() const { return HB_DIRECTION_IS_FORWARD(HbDirection()); }
+  bool IsRtl() const { return HB_DIRECTION_IS_BACKWARD(HbDirection()); }
+  bool IsHorizontal() const {
+    return HB_DIRECTION_IS_HORIZONTAL(HbDirection());
+  }
   CanvasRotationInVertical CanvasRotation() const { return canvas_rotation_; }
   unsigned NextSafeToBreakOffset(unsigned) const;
   unsigned PreviousSafeToBreakOffset(unsigned) const;
@@ -138,7 +143,7 @@
     }
 
     auto* run = MakeGarbageCollected<ShapeResultRun>(
-        font_data_.Get(), direction_, canvas_rotation_, script_,
+        font_data_.Get(), HbDirection(), canvas_rotation_, script_,
         start_index_ + start, number_of_glyphs, number_of_characters);
 
     run->glyph_data_.CopyFromRange(glyphs);
@@ -163,8 +168,8 @@
     }
     DCHECK_LT(start_index_, other.start_index_);
     auto* run = MakeGarbageCollected<ShapeResultRun>(
-        font_data_.Get(), direction_, canvas_rotation_, script_, start_index_,
-        glyph_data_.size() + other.glyph_data_.size(),
+        font_data_.Get(), HbDirection(), canvas_rotation_, script_,
+        start_index_, glyph_data_.size() + other.glyph_data_.size(),
         num_characters_ + other.num_characters_);
     // Note: We populate |graphemes_| on demand, e.g. hit testing.
     const int index_adjust = other.start_index_ - start_index_;
@@ -192,8 +197,8 @@
   bool CanMerge(const ShapeResultRun& other) const {
     return start_index_ + num_characters_ == other.start_index_ &&
            canvas_rotation_ == other.canvas_rotation_ &&
-           font_data_ == other.font_data_ && direction_ == other.direction_ &&
-           script_ == other.script_ &&
+           font_data_ == other.font_data_ &&
+           hb_direction_ == other.hb_direction_ && script_ == other.script_ &&
            glyph_data_.size() + other.glyph_data_.size() <
                HarfBuzzRunGlyphData::kMaxCharacterIndex + 1;
   }
@@ -389,7 +394,7 @@
   float width_;
 
   hb_script_t script_;
-  hb_direction_t direction_;
+  uint8_t hb_direction_;  // hb_direction_t
 
   // For upright-in-vertical we need to tell the ShapeResultBloberizer to rotate
   // the canvas back 90deg for this ShapeResultRun.
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc b/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc
index ea2d258..e56368d 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shape_result_view.cc
@@ -186,7 +186,7 @@
   new_result->runs_.ReserveInitialCapacity(parts_.size());
   for (const auto& part : RunsOrParts()) {
     auto* new_run = MakeGarbageCollected<ShapeResultRun>(
-        part.run_->font_data_.Get(), part.run_->direction_,
+        part.run_->font_data_.Get(), part.run_->HbDirection(),
         part.run_->canvas_rotation_, part.run_->script_, part.start_index_,
         part.NumGlyphs(), part.num_characters_);
     new_run->glyph_data_.CopyFromRange(part.range_);
@@ -394,7 +394,7 @@
   auto glyph_offsets = part.GetGlyphOffsets<has_non_zero_glyph_offsets>();
   const auto& run = part.run_;
   auto total_advance = InlineLayoutUnit::FromFloatRound(initial_advance);
-  bool is_horizontal = HB_DIRECTION_IS_HORIZONTAL(run->direction_);
+  bool is_horizontal = run->IsHorizontal();
   const SimpleFontData* font_data = run->font_data_.Get();
   const unsigned character_index_offset_for_glyph_data =
       CharacterIndexOffsetForGlyphData(part);
@@ -437,7 +437,7 @@
   auto glyph_offsets = part.GetGlyphOffsets<has_non_zero_glyph_offsets>();
   auto total_advance = InlineLayoutUnit::FromFloatRound(initial_advance);
   const auto& run = part.run_;
-  bool is_horizontal = HB_DIRECTION_IS_HORIZONTAL(run->direction_);
+  bool is_horizontal = run->IsHorizontal();
   const SimpleFontData* font_data = run->font_data_.Get();
   const unsigned character_index_offset_for_glyph_data =
       CharacterIndexOffsetForGlyphData(part);
diff --git a/third_party/blink/renderer/platform/fonts/web_font_typeface_factory.cc b/third_party/blink/renderer/platform/fonts/web_font_typeface_factory.cc
index a9bdab7..c4dce0909 100644
--- a/third_party/blink/renderer/platform/fonts/web_font_typeface_factory.cc
+++ b/third_party/blink/renderer/platform/fonts/web_font_typeface_factory.cc
@@ -46,28 +46,21 @@
 }
 
 sk_sp<SkTypeface> MakeTypefaceDefaultFontMgr(sk_sp<SkData> data) {
-#if !(BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE))
-  if (RuntimeEnabledFeatures::FontationsFontBackendEnabled()) {
-    return SkTypeface_Make_Fontations(data, SkFontArguments());
-  }
+#if BUILDFLAG(IS_WIN)
+  return FontCache::Get().FontManager()->makeFromData(data, 0);
 #endif
 
-  sk_sp<SkFontMgr> font_manager;
-#if BUILDFLAG(IS_WIN)
-  font_manager = FontCache::Get().FontManager();
-#else
-  font_manager = skia::DefaultFontMgr();
+#if BUILDFLAG(IS_APPLE)
+  return skia::DefaultFontMgr()->makeFromData(data, 0);
 #endif
-  return font_manager->makeFromData(data, 0);
+
+#if !(BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE))
+  return SkTypeface_Make_Fontations(data, SkFontArguments());
+#endif
 }
 
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE)
 sk_sp<SkTypeface> MakeTypefaceFallback(sk_sp<SkData> data) {
-#if BUILDFLAG(ENABLE_FREETYPE)
-  if (!RuntimeEnabledFeatures::FontationsFontBackendEnabled()) {
-    return SkFontMgr_New_Custom_Empty()->makeFromData(data, 0);
-  }
-#endif
   return SkTypeface_Make_Fontations(data, SkFontArguments());
 }
 #endif
diff --git a/third_party/blink/renderer/platform/fonts/web_font_typeface_factory_test.cc b/third_party/blink/renderer/platform/fonts/web_font_typeface_factory_test.cc
index d124980..cf14ee2 100644
--- a/third_party/blink/renderer/platform/fonts/web_font_typeface_factory_test.cc
+++ b/third_party/blink/renderer/platform/fonts/web_font_typeface_factory_test.cc
@@ -8,7 +8,6 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/fonts/opentype/font_format_check.h"
-#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
 
 #if BUILDFLAG(IS_WIN)
 #include "third_party/blink/renderer/platform/fonts/win/dwrite_font_format_support.h"
@@ -111,24 +110,6 @@
                                          g_expect_fontations);
 }
 
-TEST(WebFontTypefaceFactoryTest, COLRV0FontationsNonWin) {
-  ScopedFontationsFontBackendForTest scoped_fontations(false);
-  sk_sp<SkData> data = SkData::MakeEmpty();
-  MockFontFormatCheck mock_font_format_check(data);
-  EXPECT_CALL(mock_font_format_check, IsColrCpalColorFontV0())
-      .Times(AtLeast(1))
-      .WillRepeatedly(Return(true));
-  sk_sp<SkTypeface> out_typeface;
-  WebFontTypefaceFactory::CreateTypeface(SkData::MakeEmpty(), out_typeface,
-                                         mock_font_format_check,
-#if BUILDFLAG(IS_WIN)
-                                         g_expect_system
-#else
-                                         g_expect_fontations
-#endif
-  );
-}
-
 TEST(WebFontTypefaceFactoryTest, FontationsSelectedVariableSystem) {
   sk_sp<SkData> data = SkData::MakeEmpty();
   MockFontFormatCheck mock_font_format_check(data);
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
index d1db4f4..380d6b87 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_provider.cc
@@ -399,7 +399,6 @@
     RasterInterface()->WritePixels(client_si->mailbox(), x, y,
                                    client_si->GetTextureTarget(),
                                    SkPixmap(orig_info, pixels, row_bytes));
-    resource()->GetSyncToken();
 
     // If the overdraw optimization kicked in, we need to indicate that the
     // pixels do not need to be cleared, otherwise the subsequent
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc
index d9d02f1..9d850e4 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.cc
@@ -272,12 +272,19 @@
   // the current swap buffer's sync token.
   sync_token = current_swap_buffer_->GetSyncToken();
 
-  // This holds a ref on the SwapBuffers that will keep it alive until the
-  // mailbox is released (and while the release callback is running).
-  *out_release_callback =
-      WTF::BindOnce(&WebGPUSwapBufferProvider::MailboxReleased,
-                    scoped_refptr<WebGPUSwapBufferProvider>(this),
-                    std::move(current_swap_buffer_));
+  // We are binding current_swap_buffer_ to callback that can be destroyed on a
+  // different thread, so make sure we don't have any non thread-safe state.
+  CHECK(!current_swap_buffer_->mailbox_texture);
+  // This holds a ref on the current_swap_buffer_ that will keep it alive until
+  // the mailbox is released (and while the release callback is running). Note,
+  // that callback can be invoked only on this thread, but can be destroyed on
+  // any thread in case this thread was terminated. Ref to SwapBuffers is enough
+  // to keep underlying resources alive, so we don't need to hold ref to
+  // WebGPUSwapBufferProvider itself.
+  *out_release_callback = WTF::BindOnce(
+      &WebGPUSwapBufferProvider::MailboxReleased,
+      weak_ptr_factory_.GetWeakPtr(), base::PlatformThread::CurrentRef(),
+      std::move(current_swap_buffer_));
 
   return shared_image;
 }
@@ -351,6 +358,8 @@
 }
 
 void WebGPUSwapBufferProvider::MailboxReleased(
+    base::WeakPtr<WebGPUSwapBufferProvider> provider,
+    base::PlatformThreadRef thread_ref,
     scoped_refptr<SwapBuffer> swap_buffer,
     const gpu::SyncToken& sync_token,
     bool lost_resource) {
@@ -361,7 +370,14 @@
   if (lost_resource)
     return;
 
-  swap_buffer_pool_->ReleaseImage(std::move(swap_buffer));
+  // This callback should never run on different thread. In case our thread was
+  // destroyed, callback should be discarded (it can be discarded on any
+  // thread).
+  CHECK_EQ(thread_ref, base::PlatformThread::CurrentRef());
+
+  if (provider) {
+    provider->swap_buffer_pool_->ReleaseImage(std::move(swap_buffer));
+  }
 }
 
 WebGPUSwapBufferProvider::SwapBuffer::SwapBuffer(
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h
index f01f95c8..3c050d2 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider.h
@@ -147,9 +147,11 @@
     ~SwapBuffer() override;
   };
 
-  void MailboxReleased(scoped_refptr<SwapBuffer> swap_buffer,
-                       const gpu::SyncToken& sync_token,
-                       bool lost_resource);
+  static void MailboxReleased(base::WeakPtr<WebGPUSwapBufferProvider> provider,
+                              base::PlatformThreadRef thread_ref,
+                              scoped_refptr<SwapBuffer> swap_buffer,
+                              const gpu::SyncToken& sync_token,
+                              bool lost_resource);
 
   // This method will dissociate current Dawn Texture (produced by
   // GetNewTexture()) from the mailbox so that the mailbox can be used by other
@@ -178,6 +180,8 @@
 
   scoped_refptr<gpu::ClientSharedImage> front_buffer_shared_image_;
   gpu::SyncToken front_buffer_sync_token_;
+
+  base::WeakPtrFactory<WebGPUSwapBufferProvider> weak_ptr_factory_{this};
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider_test.cc b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider_test.cc
index 6ce4021..261efe3 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider_test.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_swap_buffer_provider_test.cc
@@ -337,17 +337,16 @@
   EXPECT_TRUE(
       provider_->PrepareTransferableResource(&resource3, &release_callback3));
 
-  // Release resources one by one, the provider should only be freed when the
-  // last one is called.
+  // Release resources one by one and expect shared images to be destroyed.
   provider_ = nullptr;
   std::move(release_callback1).Run(gpu::SyncToken(), false /* lostResource */);
-  ASSERT_EQ(provider_alive_, true);
+  EXPECT_EQ(sii_->shared_image_count(), 2u);
 
   std::move(release_callback2).Run(gpu::SyncToken(), false /* lostResource */);
-  ASSERT_EQ(provider_alive_, true);
+  EXPECT_EQ(sii_->shared_image_count(), 1u);
 
   std::move(release_callback3).Run(gpu::SyncToken(), false /* lostResource */);
-  ASSERT_EQ(provider_alive_, false);
+  EXPECT_EQ(sii_->shared_image_count(), 0u);
 }
 
 TEST_F(WebGPUSwapBufferProviderTest, VerifyResizingProperlyAffectsResources) {
diff --git a/third_party/blink/renderer/platform/heap/member.h b/third_party/blink/renderer/platform/heap/member.h
index d078ec7..af76ef0 100644
--- a/third_party/blink/renderer/platform/heap/member.h
+++ b/third_party/blink/renderer/platform/heap/member.h
@@ -14,6 +14,7 @@
 #include "third_party/blink/renderer/platform/wtf/hash_traits.h"
 #include "third_party/blink/renderer/platform/wtf/type_traits.h"
 #include "v8/include/cppgc/member.h"  // IWYU pragma: export
+#include "v8/include/cppgc/tagged-member.h"
 
 namespace blink {
 
@@ -27,8 +28,13 @@
 using UntracedMember = cppgc::UntracedMember<T>;
 
 namespace subtle {
+
 template <typename T>
 using UncompressedMember = cppgc::subtle::UncompressedMember<T>;
+
+template <typename T, typename Tag1, typename Tag2>
+using TaggedUncompressedMember =
+    cppgc::subtle::TaggedUncompressedMember<T, Tag1, Tag2>;
 }
 
 template <typename T>
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index e15c6bc..c710a7e 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1164,11 +1164,6 @@
       status: "experimental",
     },
     {
-      // crbug.com/392927981
-      name: "CssDecoratingBoxPseudoFix",
-      status: "stable",
-    },
-    {
       name: "CSSDynamicRangeLimit",
       status: "stable",
     },
@@ -2289,10 +2284,6 @@
       status: {"Android": "", "default": "stable"},
     },
     {
-      name: "FontationsFontBackend",
-      status: "stable",
-    },
-    {
       name: "FontFamilyPostscriptMatchingCTMigration",
     },
     {
@@ -3918,10 +3909,6 @@
       status: "experimental",
     },
     {
-      name: "RTCEncodedAudioFrameAbsCaptureTime",
-      status: "experimental",
-    },
-    {
       name: "RTCEncodedFrameSetMetadata",
       status: "experimental",
       origin_trial_feature_name: "RTCEncodedFrameSetMetadata",
@@ -4155,6 +4142,15 @@
     },
     {
       // Implements improved accessibility mappings for having an <input>
+      // inside of a <select> by keeping the child <input> in the a11y tree at
+      // the same place that its at in the DOM tree, but automatically setting
+      // up a controls relationship to the menu list popup.
+      name: "SelectAccessibilityNestedInput",
+      status: "test",
+      depends_on: ["CustomizableSelect"],
+    },
+    {
+      // Implements improved accessibility mappings for having an <input>
       // inside of a <select> by reparenting the child <input> in the
       // accessibility tree to be a sibling of the menu list popup.
       name: "SelectAccessibilityReparentInput",
@@ -4648,6 +4644,12 @@
       status: "experimental",
     },
     {
+      // crbug.com/341564372
+      name: "TextareaMultipleIfcs",
+      status: "experimental",
+      depends_on: ["FastSelectionSync", "TextareaLineEndingsAsBr"],
+    },
+    {
       // killswitch for fix in M137
       name: "TextareaStableResizing",
       status: "stable",
@@ -4909,12 +4911,6 @@
       },
     },
     {
-      // This flag uses the original DOM text for input when the password
-      // echo is enabled to create the offset_map.
-      name: "UseOriginalDomOffsetsForOffsetMap",
-      status: "stable",
-    },
-    {
       name: "UsePositionForPointInFlexibleBoxWithSingleChildElement",
       status: "stable",
     },
diff --git a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc
index 7253162..c5b20ad3 100644
--- a/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc
+++ b/third_party/blink/renderer/platform/widget/compositing/layer_tree_view.cc
@@ -339,10 +339,9 @@
   // When the compositor is not visible it would not request a
   // LayerTreeFrameSink so this is a race where it requested one on the
   // compositor thread while becoming non-visible on the main thread. In that
-  // case, we can wait for it to become visible again before replying. If
-  // `kWarmUpCompositor` is enabled and warm-up is triggered, a
-  // LayerTreeFrameSink is requested even if non-visible state. We can ignore
-  // this branch in that case. If not enabled, `ShouldWarmUp()` is always false.
+  // case, we can wait for it to become visible again before replying.
+  // If the warm-up is triggered, a LayerTreeFrameSink is requested even if
+  // non-visible state. We can ignore this branch in that case.
   if (!layer_tree_host_->ShouldWarmUp() && !layer_tree_host_->IsVisible()) {
     frame_sink_state_ = FrameSinkState::kRequestBufferedInvisible;
     return;
@@ -371,10 +370,8 @@
   // LayerTreeFrameSink is being processed, then if it fails we would arrive
   // here. Since the compositor does not request a LayerTreeFrameSink while not
   // visible, we can delay trying again until becoming visible again.
-  // If `kWarmUpCompositor` is enabled and warm-up is
-  // triggered, a LayerTreeFrameSink is requested even if non-visible state. We
-  // can ignore this branch in that case. If not enabled, `ShouldWarmUp()` is
-  // always false.
+  // If the warm-up is triggered, a LayerTreeFrameSink is requested even if
+  // non-visible state. We can ignore this branch in that case.
   if (!layer_tree_host_->ShouldWarmUp() && !layer_tree_host_->IsVisible()) {
     frame_sink_state_ = FrameSinkState::kRequestBufferedInvisible;
     return;
diff --git a/third_party/blink/tools/BUILD.gn b/third_party/blink/tools/BUILD.gn
index e50ed9d..cabc012 100644
--- a/third_party/blink/tools/BUILD.gn
+++ b/third_party/blink/tools/BUILD.gn
@@ -54,7 +54,6 @@
 
       # Common expectation
       "//third_party/blink/web_tests/LeakExpectations",
-      "//third_party/blink/web_tests/MobileTestExpectations",
       "//third_party/blink/web_tests/NeverFixTests",
       "//third_party/blink/web_tests/SlowTests",
       "//third_party/blink/web_tests/StaleTestExpectations",
diff --git a/third_party/blink/tools/blinkpy/w3c/import_notifier.py b/third_party/blink/tools/blinkpy/w3c/import_notifier.py
index 88ef5cd..7d71e34 100644
--- a/third_party/blink/tools/blinkpy/w3c/import_notifier.py
+++ b/third_party/blink/tools/blinkpy/w3c/import_notifier.py
@@ -82,9 +82,6 @@
 
         self.finder = path_finder.PathFinder(host.filesystem)
         self.default_port = host.port_factory.get()
-        self.default_port.set_option_default('additional_expectations', [
-            self.finder.path_from_web_tests('MobileTestExpectations'),
-        ])
         self.default_port.set_option_default('test_types',
                                              typing.get_args(TestType))
         self.owners_extractor = DirectoryOwnersExtractor(host)
diff --git a/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py b/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py
index 7c38cb5..602c3b5 100644
--- a/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py
+++ b/third_party/blink/tools/blinkpy/web_tests/lint_test_expectations.py
@@ -448,7 +448,6 @@
     finder = PathFinder(host.filesystem)
     # Add all extra expectation files to be linted.
     options.additional_expectations.extend([
-        finder.path_from_web_tests('MobileTestExpectations'),
         finder.path_from_web_tests('WebGPUExpectations'),
     ])
     # The checks and list of expectation files are generally not
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/android.py b/third_party/blink/tools/blinkpy/web_tests/port/android.py
index 492cd8f2..f18e199 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/android.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/android.py
@@ -53,8 +53,6 @@
                 self.path_to_generic_test_expectations_file(),
                 self._filesystem.join(self.web_tests_dir(), 'NeverFixTests'),
                 self._filesystem.join(self.web_tests_dir(),
-                                      'MobileTestExpectations'),
-                self._filesystem.join(self.web_tests_dir(),
                                       'StaleTestExpectations'),
                 self._filesystem.join(self.web_tests_dir(), 'SlowTests')
             ]))
diff --git a/third_party/blink/tools/blinkpy/web_tests/port/webview.py b/third_party/blink/tools/blinkpy/web_tests/port/webview.py
index ccc5c83..0f760bd 100644
--- a/third_party/blink/tools/blinkpy/web_tests/port/webview.py
+++ b/third_party/blink/tools/blinkpy/web_tests/port/webview.py
@@ -52,8 +52,6 @@
                 self.path_to_generic_test_expectations_file(),
                 self._filesystem.join(self.web_tests_dir(), 'NeverFixTests'),
                 self._filesystem.join(self.web_tests_dir(),
-                                      'MobileTestExpectations'),
-                self._filesystem.join(self.web_tests_dir(),
                                       'StaleTestExpectations'),
                 self._filesystem.join(self.web_tests_dir(), 'SlowTests')
             ]))
diff --git a/third_party/blink/web_tests/MobileTestExpectations b/third_party/blink/web_tests/MobileTestExpectations
deleted file mode 100644
index 0dd61fb5..0000000
--- a/third_party/blink/web_tests/MobileTestExpectations
+++ /dev/null
@@ -1,209 +0,0 @@
-# tags: [ Webview Android ]
-# tags: [ Release Debug ]
-# results: [ Timeout Crash Pass Failure Skip ]
-# conflict_resolution: Override
-
-# This is the main failure suppression file for ChromeAndroid and Chrome Webview
-# Web Platform Tests.
-
-# pointerevents tests
-crbug.com/349397104 [ Android ] external/wpt/pointerevents/capturing_boundary_event_handler_at_ua_shadowdom.html?mouse [ Pass Timeout ]
-crbug.com/349397104 [ Android ] external/wpt/pointerevents/capturing_boundary_event_handler_at_ua_shadowdom.html?pen [ Pass Timeout ]
-crbug.com/349397104 [ Android ] external/wpt/pointerevents/capturing_boundary_event_handler_at_ua_shadowdom.html?touch [ Failure Pass Timeout ]
-crbug.com/349397104 [ Android ] external/wpt/pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html [ Failure Pass ]
-crbug.com/349397104 [ Android ] external/wpt/pointerevents/pointerevent_after_target_appended.html?pen [ Failure Pass ]
-crbug.com/349397104 [ Android ] external/wpt/pointerevents/pointerevent_attributes.html?mouse-right-nonstandard [ Failure Pass ]
-crbug.com/349397104 [ Android ] external/wpt/pointerevents/pointerevent_attributes.html?pen-right [ Failure Pass ]
-crbug.com/349397104 [ Android ] external/wpt/pointerevents/pointerevent_click_is_a_pointerevent_multiple_clicks.html?mouse [ Pass Timeout ]
-crbug.com/349397104 [ Android ] external/wpt/pointerevents/pointerevent_click_is_a_pointerevent_multiple_clicks.html?touch [ Failure Pass Timeout ]
-crbug.com/349397104 [ Android ] external/wpt/pointerevents/pointerevent_pointerleave_after_pointercancel_touch.html [ Failure Pass ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/capturing_boundary_event_handler_at_ua_shadowdom.html?pen [ Failure Pass ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/capturing_boundary_event_handler_at_ua_shadowdom.html?touch [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/compat/pointerevent_mouseevent_key_pressed.html [ Failure ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/compat/pointerevent_touch-action_two-finger_interaction.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointer-events-none-skip-scroll-will-change-in-iframe.html [ Failure Pass ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_after_target_appended_interleaved.tentative.html?mouse [ Pass Timeout ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_after_target_appended_interleaved.tentative.html?touch [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_after_target_appended.html?touch [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_after_target_removed_interleaved.tentative.html?touch [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_after_target_removed.html?pen [ Pass Timeout ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_after_target_removed.html?touch [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_attributes.html?mouse [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_attributes.html?mouse-nonstandard [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_attributes.html?mouse-right [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_attributes.html?mouse-right-nonstandard [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_attributes.html?pen [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_attributes.html?pen-nonstandard [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_attributes.html?pen-right [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_attributes.html?pen-right-nonstandard [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_attributes.html?touch [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_attributes.html?touch-nonstandard [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_change-touch-action-onpointerdown_touch.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_click_during_capture.html?mouse-auxclick [ Failure ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_click_during_capture.html?mouse-click [ Failure ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_click_is_a_pointerevent_multiple_clicks.html?touch [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_click_is_a_pointerevent.html?touch [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_fractional_coordinates.html?touch [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_iframe-touch-action-none_touch.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_movementxy.html?mouse [ Failure ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_movementxy.html?pen [ Failure ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_movementxy.html?touch [ Failure ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_pointercancel_touch.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_pointerId_scope.html [ Failure ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_pointerleave_after_pointercancel_touch.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_pointerout_after_pointercancel_touch.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_releasepointercapture_onpointercancel_touch.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-auto-css_touch.html [ Failure Timeout ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-button-none-test_touch.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-none_touch.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-inherit_highest-parent-none_touch.html [ Failure Timeout ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-inherit_parent-none_touch.html [ Failure Pass ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-mouse.html [ Timeout ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-pan-down-css_touch.html [ Failure Timeout ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-pan-right-css_touch.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-pan-up-css_touch.html [ Failure Timeout ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-pan-x-css_touch.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-pan-x-pan-y_touch.html [ Failure Timeout ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-pan-x-pan-y-pan-y_touch.html [ Failure Timeout ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-pan-y-css_touch.html [ Skip Timeout ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-svg-none-test_touch.html [ Skip Timeout ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-table-none-test_touch.html [ Skip Timeout ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerevent_touch-adjustment_click_target.html [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerlock/pointerevent_movementxy_with_pointerlock.html [ Failure ]
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/pointerup_after_pointerdown_target_removed.html?touch [ Failure ]  # Flaky output
-crbug.com/349397104 [ Webview ] external/wpt/pointerevents/touch-action-with-swipe-dir-change.html?touch [ Failure ]  # Flaky output
-crbug.com/349397104 external/wpt/pointerevents/coalesced_events_attributes_under_load.https.optional.html?mouse [ Skip Timeout ]
-crbug.com/349397104 external/wpt/pointerevents/coalesced_events_attributes_under_load.https.optional.html?pen [ Skip Timeout ]
-crbug.com/349397104 external/wpt/pointerevents/coalesced_events_attributes_under_load.https.optional.html?touch [ Skip Timeout ]
-crbug.com/349397104 external/wpt/pointerevents/pointer-events-none-skip-scroll-in-iframe.html [ Failure Pass ]
-crbug.com/349397104 external/wpt/pointerevents/pointerevent_contextmenu_is_a_pointerevent.html?touch [ Failure Timeout ]  # Flaky output
-crbug.com/349397104 external/wpt/pointerevents/pointerevent_touch-action-span-none-test_touch.html [ Skip Timeout ]
-crbug.com/349397104 external/wpt/pointerevents/pointerlock/pointerevent_getCoalescedEvents_when_pointerlocked.https.html [ Failure Pass ]
-crbug.com/349397104 external/wpt/pointerevents/pointerlock/pointerevent_pointerlock_after_pointercapture.html [ Failure Pass ]
-
-# Incompatible with both Chrome Android and Webview
-# SharedWorker interface
-crbug.com/40290702 external/wpt/background-fetch/idlharness.https.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/content-index/idlharness.https.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/cookie-store/idlharness.https.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/dom/idlharness.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/encoding/idlharness.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/event-timing/idlharness.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/fetch/api/idlharness.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/hr-time/idlharness.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/IndexedDB/idlharness.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/notifications/idlharness.https.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/payment-handler/idlharness.https.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/performance-timeline/idlharness.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/push-api/idlharness.https.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/service-workers/idlharness.https.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/shape-detection/idlharness.https.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/streams/idlharness.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/user-timing/idlharness.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/webtransport/idlharness.https.any.sharedworker.html [ Failure ]
-crbug.com/40290702 external/wpt/xhr/idlharness.any.sharedworker.html [ Failure ]
-# Compute Pressure
-crbug.com/341537009 external/wpt/compute-pressure/idlharness.https.any.html [ Failure ]
-crbug.com/341537009 external/wpt/compute-pressure/idlharness.https.any.sharedworker.html [ Failure ]
-crbug.com/341537009 external/wpt/compute-pressure/idlharness.https.any.worker.html [ Failure ]
-# MediaDevices.ondevicechange
-crbug.com/671461 external/wpt/mediacapture-streams/idlharness.https.window.html [ Failure ]
-
-# Incompatible with Webview
-# Refer to android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
-crbug.com/40417848 [ Webview ] external/wpt/speech-api/idlharness.window.html [ Failure ]
-crbug.com/40652382 [ WebView ] external/wpt/webxr/* [ Failure ]
-crbug.com/41441927 [ WebView ] external/wpt/webusb/* [ Failure ]
-crbug.com/41492543 [ Webview ] external/wpt/webtransport/idlharness.https.any.html [ Failure ]
-crbug.com/41492543 [ Webview ] external/wpt/webtransport/idlharness.https.any.serviceworker.html [ Failure ]
-crbug.com/41492543 [ Webview ] external/wpt/webtransport/idlharness.https.any.worker.html [ Failure ]
-crbug.com/925997 [ Webview ] external/wpt/mediasession/idlharness.window.html [ Failure ]
-crbug.com/334063352 [ WebView ] external/wpt/picture-in-picture/idlharness.window.html [ Failure ]
-crbug.com/421921 [ Webview ] external/wpt/push-api/idlharness.https.any.serviceworker.html [ Failure ]
-
-# Other failing/timeout tests
-[ Android ] external/wpt/visual-viewport/resize-event-order.html [ Timeout ]
-[ Android ] external/wpt/visual-viewport/viewport-resize-event-on-load-overflowing-page.html [ Failure ]
-[ Webview ] external/wpt/compat/idlharness.window.html [ Failure ]
-[ Webview ] external/wpt/html-media-capture/idlharness.window.html [ Failure ]
-[ Webview ] external/wpt/media-capabilities/idlharness.any.html [ Pass Timeout ]
-[ Webview ] external/wpt/navigation-timing/idlharness.window.html [ Failure Pass ]
-[ Webview ] external/wpt/selection/idlharness.window.html [ Failure ]
-[ Webview ] external/wpt/shape-detection/idlharness.https.any.html [ Failure ]
-[ Webview ] external/wpt/shape-detection/idlharness.https.any.serviceworker.html [ Failure ]
-[ Webview ] external/wpt/shape-detection/idlharness.https.any.worker.html [ Failure ]
-[ Webview ] external/wpt/uievents/idlharness.window.html [ Failure ]
-external/wpt/web-animations/idlharness.window.html [ Failure ]
-external/wpt/webrtc/idlharness.https.window.html [ Skip Timeout ]
-
-# Flaky tests
-[ Android ] external/wpt/visual-viewport/viewport-scrollbars-cause-resize.html [ Failure Pass ]
-[ Android ] external/wpt/visual-viewport/viewport-unscaled-size.html [ Failure Pass ]
-[ Webview ] external/wpt/largest-contentful-paint/idlharness.html [ Pass Timeout ]
-[ Webview ] external/wpt/storage/idlharness.https.any.worker.html [ Failure Pass ]
-[ Webview ] external/wpt/streams/idlharness.any.worker.html [ Failure Pass Timeout ]
-[ Webview ] external/wpt/visual-viewport/viewport-scrollbars-cause-resize-in-iframe.html [ Failure Pass ]
-[ Webview ] external/wpt/webauthn/idlharness.https.window.html [ Failure Pass ]
-[ Webview ] external/wpt/webrtc-encoded-transform/idlharness.https.window.html [ Failure Pass ]
-crbug.com/372119113 [ Webview ] external/wpt/visual-viewport/scroll-event-order.html [ Crash Failure Pass Timeout ]  # Flaky output
-crbug.com/372097205 external/wpt/visual-viewport/page-and-offset-in-iframe.html [ Crash Failure Pass ]
-
-# Stack (with nondeterministic memory addresses) is dumped into `*-expected.txt`.
-[ Webview ] external/wpt/idle-detection/idlharness-worker.https.window.html [ Failure ]
-[ Webview ] external/wpt/idle-detection/idlharness.https.window.html [ Failure Timeout ]
-[ Webview ] external/wpt/screen-wake-lock/idlharness.https.window.html [ Failure ]
-[ Webview ] external/wpt/webmidi/idlharness.https.window.html [ Failure ]
-
-# Webdriver tests timeout on Android
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/browsing_context/context_created/original_opener.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/browsing_context/create/invalid.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/invalid.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/browsing_context/navigate/navigate_beforeunload.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/browsing_context/navigate/navigate.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/browsing_context/navigation_started/navigation_started.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/browsing_context/user_prompt_opened/handler.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/external/bluetooth/simulate_adapter/context.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/external/bluetooth/simulate_adapter/invalid.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/external/bluetooth/simulate_adapter/state.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/external/bluetooth/simulate_preconnected_peripheral/simulate_preconnected_peripheral.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/network/add_intercept/url_patterns.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/network/continue_request/method.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/network/continue_response/status_code.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/network/continue_with_auth/invalid.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/network/provide_response/status_code.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/network/response_completed/response_completed.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/network/response_started/response_started.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/storage/delete_cookies/filter.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/storage/get_cookies/filter.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/web_extension/install/install.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/web_extension/install/invalid.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/web_extension/uninstall/invalid.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/web_extension/uninstall/uninstall.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/back/back.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/dismiss_alert/dismiss.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/element_clear/clear.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/element_clear/disabled.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/element_click/navigate.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/element_send_keys/file_upload.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/element_send_keys/interactability.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/find_element_from_element/find.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/find_element_from_shadow_root/find.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/find_elements_from_element/find.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/find_elements_from_shadow_root/find.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/forward/forward.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/get_element_attribute/get.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/get_element_property/get.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/get_window_rect/user_prompts.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/is_element_enabled/enabled.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/maximize_window/user_prompts.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/new_session/response.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/perform_actions/key_events.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/perform_actions/pointer_mouse_drag.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/perform_actions/pointer_mouse.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/perform_actions/pointer_touch.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/send_alert_text/send.py [ Skip Timeout ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/set_window_rect/user_prompts.py [ Failure ]
-crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/interop/beforeunload_prompt.py [ Skip Timeout ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 688f917..2a22f57 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -2404,16 +2404,16 @@
 
 # `wdspec` tests
 external/wpt/webdriver/tests/classic/element_send_keys/interactability.py [ Failure ]  # Flaky output
-external/wpt/webdriver/tests/classic/element_click/navigate.py [ Failure ]  # Flaky output
+[ Linux ] external/wpt/webdriver/tests/classic/element_click/navigate.py [ Failure ]  # Flaky output
 external/wpt/webdriver/tests/bidi/network/add_intercept/invalid.py [ Timeout ]
 external/wpt/webdriver/tests/bidi/network/add_intercept/phases.py [ Failure Timeout ]
-crbug.com/324436866 external/wpt/webdriver/tests/bidi/network/response_started/response_started.py [ Failure Timeout ]
+crbug.com/324436866 [ Linux ] external/wpt/webdriver/tests/bidi/network/response_started/response_started.py [ Failure Timeout ]
 crbug.com/381127883 external/wpt/webdriver/tests/bidi/network/response_started/response_started_cached.py [ Failure Timeout ]
 crbug.com/1502331 external/wpt/webdriver/tests/classic/perform_actions/pointer_pen.py [ Failure Pass ]
 crbug.com/626703 external/wpt/webdriver/tests/bidi/network/auth_required/auth_required.py [ Failure Timeout ]
-external/wpt/webdriver/tests/bidi/network/add_intercept/url_patterns.py [ Timeout ]
+[ Linux ] external/wpt/webdriver/tests/bidi/network/add_intercept/url_patterns.py [ Timeout ]
 external/wpt/webdriver/tests/bidi/browsing_context/capture_screenshot/frame.py [ Failure ]  # Flaky output
-crbug.com/324436866 external/wpt/webdriver/tests/bidi/browsing_context/navigation_started/navigation_started.py [ Failure Timeout ]
+crbug.com/324436866 [ Linux ] external/wpt/webdriver/tests/bidi/browsing_context/navigation_started/navigation_started.py [ Failure Timeout ]
 external/wpt/webdriver/tests/bidi/browsing_context/print/invalid.py [ Timeout ]
 external/wpt/webdriver/tests/bidi/browsing_context/user_prompt_opened/user_prompt_opened.py [ Failure Pass Timeout ]
 crbug.com/324436866 external/wpt/webdriver/tests/bidi/browser/remove_user_context/user_context.py [ Failure Pass Timeout ]
@@ -2429,7 +2429,7 @@
 crbug.com/1507773 external/wpt/webdriver/tests/classic/get_element_attribute/get.py [ Failure Pass Timeout ]
 crbug.com/1507773 external/wpt/webdriver/tests/classic/is_element_enabled/enabled.py [ Failure Pass Timeout ]
 crbug.com/1499775 external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/context.py [ Failure ]
-crbug.com/1499775 external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/invalid.py [ Pass Timeout ]
+crbug.com/1499775 [ Linux ] external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/invalid.py [ Pass Timeout ]
 crbug.com/1499775 external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/start_nodes.py [ Failure ]
 crbug.com/1499775 external/wpt/webdriver/tests/bidi/input/perform_actions/pointer_mouse_multiclick.py [ Failure Timeout ]
 crbug.com/324436866 external/wpt/webdriver/tests/bidi/browsing_context/print/context.py [ Failure Pass Timeout ]
@@ -2437,7 +2437,7 @@
 crbug.com/626703 external/wpt/webdriver/tests/bidi/input/set_files/invalid.py [ Failure Timeout ]
 external/wpt/webdriver/tests/bidi/input/set_files/set_files.py [ Timeout ]
 [ Debug ] external/wpt/webdriver/tests/bidi/network/add_intercept/contexts.py [ Failure Pass Timeout ]
-[ Debug ] external/wpt/webdriver/tests/bidi/storage/delete_cookies/filter.py [ Timeout ]
+[ Linux Debug ] external/wpt/webdriver/tests/bidi/storage/delete_cookies/filter.py [ Timeout ]
 [ Debug ] external/wpt/webdriver/tests/bidi/storage/delete_cookies/invalid.py [ Timeout ]
 [ Debug ] external/wpt/webdriver/tests/bidi/storage/get_cookies/invalid.py [ Timeout ]
 external/wpt/webdriver/tests/bidi/browsing_context/close/prompt_unload.py [ Timeout ]
@@ -2559,6 +2559,147 @@
 crbug.com/40268415 [ Debug Mac15-arm64 ] external/wpt/webaudio/the-audio-api/the-mediaelementaudiosourcenode-interface/no-cors.https.html [ Crash ]
 crbug.com/40268415 [ Debug Win11-arm64 ] external/wpt/webaudio/the-audio-api/the-mediaelementaudiosourcenode-interface/no-cors.https.html [ Crash ]
 
+# Test Expectations for Chrome Android/WebView
+# Incompatible with both Chrome Android and Webview
+# SharedWorker interface
+crbug.com/40290702 [ Android ] external/wpt/background-fetch/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/content-index/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/cookie-store/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/dom/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/encoding/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/event-timing/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/fetch/api/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/hr-time/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/IndexedDB/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/notifications/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/payment-handler/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/performance-timeline/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/push-api/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/service-workers/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/shape-detection/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/streams/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/user-timing/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/webtransport/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Android ] external/wpt/xhr/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/background-fetch/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/content-index/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/cookie-store/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/dom/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/encoding/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/event-timing/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/fetch/api/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/hr-time/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/IndexedDB/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/notifications/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/payment-handler/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/performance-timeline/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/push-api/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/service-workers/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/shape-detection/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/streams/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/user-timing/idlharness.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/webtransport/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/40290702 [ Webview ] external/wpt/xhr/idlharness.any.sharedworker.html [ Failure ]
+# Compute Pressure
+crbug.com/341537009 [ Android ] external/wpt/compute-pressure/idlharness.https.any.html [ Failure ]
+crbug.com/341537009 [ Android ] external/wpt/compute-pressure/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/341537009 [ Android ] external/wpt/compute-pressure/idlharness.https.any.worker.html [ Failure ]
+crbug.com/341537009 [ Webview ] external/wpt/compute-pressure/idlharness.https.any.html [ Failure ]
+crbug.com/341537009 [ Webview ] external/wpt/compute-pressure/idlharness.https.any.sharedworker.html [ Failure ]
+crbug.com/341537009 [ Webview ] external/wpt/compute-pressure/idlharness.https.any.worker.html [ Failure ]
+# MediaDevices.ondevicechange
+crbug.com/671461 [ Android ] external/wpt/mediacapture-streams/idlharness.https.window.html [ Failure ]
+crbug.com/671461 [ Webview ] external/wpt/mediacapture-streams/idlharness.https.window.html [ Failure ]
+
+# Incompatible with Webview
+# Refer to android_webview/tools/system_webview_shell/test/data/webexposed/not-webview-exposed.txt
+crbug.com/40417848 [ Webview ] external/wpt/speech-api/idlharness.window.html [ Failure ]
+crbug.com/40652382 [ Webview ] external/wpt/webxr/* [ Failure ]
+crbug.com/41441927 [ Webview ] external/wpt/webusb/* [ Failure ]
+crbug.com/41492543 [ Webview ] external/wpt/webtransport/idlharness.https.any.html [ Failure ]
+crbug.com/41492543 [ Webview ] external/wpt/webtransport/idlharness.https.any.serviceworker.html [ Failure ]
+crbug.com/41492543 [ Webview ] external/wpt/webtransport/idlharness.https.any.worker.html [ Failure ]
+crbug.com/925997 [ Webview ] external/wpt/mediasession/idlharness.window.html [ Failure ]
+crbug.com/334063352 [ Webview ] external/wpt/picture-in-picture/idlharness.window.html [ Failure ]
+crbug.com/421921 [ Webview ] external/wpt/push-api/idlharness.https.any.serviceworker.html [ Failure ]
+
+# Other failing/timeout tests
+[ Android ] external/wpt/visual-viewport/resize-event-order.html [ Timeout ]
+[ Android ] external/wpt/visual-viewport/viewport-resize-event-on-load-overflowing-page.html [ Failure ]
+[ Webview ] external/wpt/compat/idlharness.window.html [ Failure ]
+[ Webview ] external/wpt/html-media-capture/idlharness.window.html [ Failure ]
+[ Webview ] external/wpt/media-capabilities/idlharness.any.html [ Pass Timeout ]
+[ Webview ] external/wpt/navigation-timing/idlharness.window.html [ Failure Pass ]
+[ Webview ] external/wpt/selection/idlharness.window.html [ Failure ]
+[ Webview ] external/wpt/shape-detection/idlharness.https.any.html [ Failure ]
+[ Webview ] external/wpt/shape-detection/idlharness.https.any.serviceworker.html [ Failure ]
+[ Webview ] external/wpt/shape-detection/idlharness.https.any.worker.html [ Failure ]
+[ Webview ] external/wpt/uievents/idlharness.window.html [ Failure ]
+[ Webview ] external/wpt/web-animations/idlharness.window.html [ Failure ]
+[ Webview ] external/wpt/webrtc/idlharness.https.window.html [ Skip Timeout ]
+[ Android ] external/wpt/web-animations/idlharness.window.html [ Failure ]
+[ Android ] external/wpt/webrtc/idlharness.https.window.html [ Skip Timeout ]
+
+# Flaky tests
+[ Android ] external/wpt/visual-viewport/viewport-scrollbars-cause-resize.html [ Failure Pass ]
+[ Android ] external/wpt/visual-viewport/viewport-unscaled-size.html [ Failure Pass ]
+[ Webview ] external/wpt/largest-contentful-paint/idlharness.html [ Pass Timeout ]
+[ Webview ] external/wpt/storage/idlharness.https.any.worker.html [ Failure Pass ]
+[ Webview ] external/wpt/streams/idlharness.any.worker.html [ Failure Pass Timeout ]
+[ Webview ] external/wpt/visual-viewport/viewport-scrollbars-cause-resize-in-iframe.html [ Failure Pass ]
+[ Webview ] external/wpt/webauthn/idlharness.https.window.html [ Failure Pass ]
+[ Webview ] external/wpt/webrtc-encoded-transform/idlharness.https.window.html [ Failure Pass ]
+crbug.com/372119113 [ Webview ] external/wpt/visual-viewport/scroll-event-order.html [ Crash Failure Pass Timeout ]  # Flaky output
+crbug.com/403340566 [ Webview ] external/wpt/visual-viewport/page-and-offset-in-iframe.html [ Crash Failure Pass Timeout ]
+crbug.com/372097205 [ Android ] external/wpt/visual-viewport/page-and-offset-in-iframe.html [ Crash Failure Pass ]
+
+# Stack (with nondeterministic memory addresses) is dumped into `*-expected.txt`.
+[ Webview ] external/wpt/idle-detection/idlharness-worker.https.window.html [ Failure ]
+[ Webview ] external/wpt/idle-detection/idlharness.https.window.html [ Failure Timeout ]
+[ Webview ] external/wpt/screen-wake-lock/idlharness.https.window.html [ Failure ]
+[ Webview ] external/wpt/webmidi/idlharness.https.window.html [ Failure ]
+
+# Webdriver tests timeout on Android
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/browsing_context/context_created/original_opener.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/browsing_context/create/invalid.py [ Failure ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/browsing_context/locate_nodes/invalid.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/browsing_context/navigate/navigate_beforeunload.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/browsing_context/navigate/navigate.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/browsing_context/navigation_started/navigation_started.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/browsing_context/user_prompt_opened/handler.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/external/bluetooth/simulate_adapter/context.py [ Failure ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/external/bluetooth/simulate_adapter/invalid.py [ Failure ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/external/bluetooth/simulate_adapter/state.py [ Failure ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/external/bluetooth/simulate_preconnected_peripheral/simulate_preconnected_peripheral.py [ Failure ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/network/add_intercept/url_patterns.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/network/continue_request/method.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/network/continue_response/status_code.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/network/continue_with_auth/invalid.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/network/provide_response/status_code.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/network/response_completed/response_completed.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/network/response_started/response_started.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/storage/delete_cookies/filter.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/storage/get_cookies/filter.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/web_extension/install/install.py [ Failure ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/web_extension/install/invalid.py [ Failure ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/web_extension/uninstall/invalid.py [ Failure ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/bidi/web_extension/uninstall/uninstall.py [ Failure ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/back/back.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/dismiss_alert/dismiss.py [ Failure ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/element_clear/clear.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/element_clear/disabled.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/element_click/navigate.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/element_send_keys/file_upload.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/get_element_property/get.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/get_window_rect/user_prompts.py [ Failure ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/maximize_window/user_prompts.py [ Failure ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/new_session/response.py [ Failure ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/perform_actions/key_events.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/perform_actions/pointer_mouse_drag.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/perform_actions/pointer_mouse.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/send_alert_text/send.py [ Skip Timeout ]
+crbug.com/395962971 [ Android ] external/wpt/webdriver/tests/classic/set_window_rect/user_prompts.py [ Failure ]
+
 # ====== Test expectations added to unblock wpt-importer ======
 crbug.com/413633034 external/wpt/html/webappapis/scripting/event-loops/new-scroll-event-dispatched-at-next-updating-rendering-time.html [ Failure ]
 crbug.com/406036293 [ Mac ] external/wpt/css/css-overflow/keyboard-scroll.html [ Failure Pass ]
@@ -2765,7 +2906,6 @@
 crbug.com/403345125 external/wpt/css/css-writing-modes/available-size-021.html [ Failure ]
 crbug.com/403345125 external/wpt/css/css-writing-modes/available-size-022.html [ Failure ]
 crbug.com/403345125 external/wpt/css/css-writing-modes/available-size-023.html [ Failure ]
-crbug.com/403340566 [ Webview ] external/wpt/visual-viewport/page-and-offset-in-iframe.html [ Timeout ]
 crbug.com/403340566 [ Win11-arm64 ] external/wpt/visual-viewport/page-and-offset-in-iframe.html [ Timeout ]
 crbug.com/402195188 [ Mac14 ] external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html?5-6 [ Crash ]
 crbug.com/401111779 [ Mac15-arm64 ] external/wpt/webrtc-stats/getStats-remote-candidate-ufrag.html [ Timeout ]
@@ -3011,7 +3151,7 @@
 crbug.com/381127882 external/wpt/css/css-rhythm/content-based-height-rounds-up-to-step-unit.html [ Failure ]
 crbug.com/381127882 external/wpt/css/css-rhythm/definite-height-rounds-up-to-next-multiple-of-step-unit.html [ Failure ]
 crbug.com/381127882 external/wpt/css/css-rhythm/definite-height-rounds-up-to-step-unit.html [ Failure ]
-crbug.com/381127883 external/wpt/webdriver/tests/bidi/network/continue_request/method.py [ Failure Timeout ]
+crbug.com/381127883 [ Linux ] external/wpt/webdriver/tests/bidi/network/continue_request/method.py [ Failure Timeout ]
 crbug.com/380409684 [ Mac14 ] external/wpt/html/cross-origin-opener-policy/iframe-popup-same-origin-to-unsafe-none.https.html?7-8 [ Crash ]
 crbug.com/380327161 [ Mac13 ] external/wpt/pointerevents/pointerevent_sequence_at_implicit_release_on_drag.html [ Timeout ]
 crbug.com/380327161 [ Win10.20h2 ] external/wpt/pointerevents/pointerevent_sequence_at_implicit_release_on_drag.html [ Timeout ]
@@ -3142,7 +3282,6 @@
 crbug.com/375262152 external/wpt/html/semantics/embedded-content/the-img-element/update-the-image-data/src-then-lazy-load.html [ Timeout ]
 crbug.com/374812621 external/wpt/encrypted-media/media-element-event-handler-attributes.html [ Timeout ]
 crbug.com/375204273 [ Mac13 ] external/wpt/fs/FileSystemObserver.https.tentative.any.worker.html [ Failure Timeout ]
-crbug.com/372103377 [ Webview ] external/wpt/visual-viewport/scroll-event-order.html [ Crash ]
 crbug.com/371061343 [ Mac ] external/wpt/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment-nosw.https.html [ Skip Timeout ]
 crbug.com/371061343 [ Mac ] external/wpt/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-fragment.https.html [ Skip Timeout ]
 crbug.com/371061343 [ Mac ] external/wpt/html/webappapis/system-state-and-capabilities/the-navigator-object/protocol-handler-path.https.html [ Skip Timeout ]
@@ -3235,11 +3374,7 @@
 crbug.com/360956937 [ Mac15-arm64 ] external/wpt/webrtc-encoded-transform/script-transform-sendKeyFrameRequest.https.html [ Skip Timeout ]
 crbug.com/360956937 [ Mac15 ] external/wpt/webrtc-encoded-transform/script-transform-sendKeyFrameRequest.https.html [ Skip Timeout ]
 [ Win11-arm64 ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch.html [ Crash Failure ]
-[ Webview ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch.html [ Crash Failure ]
 [ Win11-arm64 ] external/wpt/pointerevents/pointerevent_touch-action-none-css_touch.html [ Crash Failure ]
-[ Webview ] external/wpt/pointerevents/pointerevent_touch-action-none-css_touch.html [ Crash Failure ]
-crbug.com/360246424 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-button-none-test_touch.html [ Crash Failure ]
-crbug.com/360246424 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-none_touch.html [ Crash Failure ]
 crbug.com/360246809 [ Mac15 ] external/wpt/fs/FileSystemFileHandle-sync-access-handle-back-forward-cache.https.tentative.window.html [ Skip Timeout ]
 crbug.com/360246809 [ Linux ] external/wpt/fs/FileSystemFileHandle-sync-access-handle-back-forward-cache.https.tentative.window.html [ Skip Timeout ]
 crbug.com/360201083 [ Mac15-arm64 ] external/wpt/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk.https.html [ Timeout ]
@@ -3322,7 +3457,6 @@
 crbug.com/352088182 [ Win11-arm64 ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/going-back.sub.https.html [ Timeout ]
 [ Win11-arm64 ] virtual/fenced-frame-mparch-internal/wpt_internal/fenced_frame/disable-untrusted-network.https.html [ Timeout ]
 crbug.com/351703751 [ Win11-arm64 ] external/wpt/WebCryptoAPI/derive_bits_keys/cfrg_curves_bits.https.any.html [ Failure Timeout ]
-crbug.com/350779641 [ Webview ] external/wpt/pointerevents/pointerevent_touch-action-inherit_child-auto-child-none_touch.html [ Crash Failure ]
 crbug.com/350823140 [ Win11-arm64 ] virtual/no-auto-wpt-origin-isolation/external/wpt/html/browsers/origin/origin-keyed-agent-clusters/2-iframes/parent-yes-child1-yes-subdomain-child2-yes-subdomainport.sub.https.html [ Timeout ]
 crbug.com/350792702 external/wpt/trusted-types/trusted-types-reporting.html [ Skip Timeout ]
 [ Win11-arm64 ] virtual/fenced-frame-mparch-internal/wpt_internal/fenced_frame/revoke-service-workers.https.html [ Failure Timeout ]
@@ -3902,11 +4036,11 @@
 crbug.com/626703 external/wpt/webrtc/RTCPeerConnection-setRemoteDescription-tracks.https.html [ Skip Timeout ]
 crbug.com/626703 external/wpt/webrtc/RTCPeerConnection-remote-track-mute.https.html [ Skip Timeout ]
 crbug.com/626703 external/wpt/fetch/content-type/response.window.html [ Timeout ]
-crbug.com/357931030 external/wpt/webdriver/tests/bidi/browsing_context/navigate/navigate.py [ Timeout ]
+crbug.com/357931030 [ Linux ] external/wpt/webdriver/tests/bidi/browsing_context/navigate/navigate.py [ Timeout ]
 crbug.com/356498699 external/wpt/webdriver/tests/bidi/browsing_context/navigation_failed/navigation_failed.py [ Failure Timeout ]
 crbug.com/626703 [ Release ] external/wpt/webdriver/tests/bidi/browsing_context/user_prompt_closed/user_prompt_closed.py [ Failure ]  # Flaky output
-crbug.com/626703 external/wpt/webdriver/tests/classic/element_clear/clear.py [ Failure Timeout ]
-crbug.com/626703 external/wpt/webdriver/tests/bidi/network/continue_with_auth/invalid.py [ Timeout ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/classic/element_clear/clear.py [ Failure Timeout ]
+crbug.com/626703 [ Linux ] external/wpt/webdriver/tests/bidi/network/continue_with_auth/invalid.py [ Timeout ]
 crbug.com/626703 external/wpt/webdriver/tests/classic/element_send_keys/send_keys.py [ Failure ]
 
 # Some WebXR tests that have become flaky
@@ -4791,8 +4925,8 @@
 # Prefetching inspector protocol test is flaky
 crbug.com/40854799 virtual/prefetch/http/tests/inspector-protocol/prefetch/request-will-be-sent.https.js [ Failure Pass ]
 crbug.com/40854799 virtual/prefetch/http/tests/inspector-protocol/prefetch/request-will-be-sent-redirect.https.js [ Failure Pass ]
-crbug.com/40854799 virtual/prefetch-serviceworker/http/tests/inspector-protocol/prefetch/request-will-be-sent.https.js [ Failure Pass ]
-crbug.com/40854799 virtual/prefetch-serviceworker/http/tests/inspector-protocol/prefetch/request-will-be-sent-redirect.https.js [ Failure Pass ]
+crbug.com/40854799 virtual/prefetch-sw/http/tests/inspector-protocol/prefetch/request-will-be-sent.https.js [ Failure Pass ]
+crbug.com/40854799 virtual/prefetch-sw/http/tests/inspector-protocol/prefetch/request-will-be-sent-redirect.https.js [ Failure Pass ]
 crbug.com/40854799 virtual/prerender2-fallback-prefetch-spec-rules-prefetch/http/tests/inspector-protocol/prefetch/request-will-be-sent.https.js [ Failure Pass ]
 crbug.com/40854799 virtual/prerender2-fallback-prefetch-spec-rules-prefetch/http/tests/inspector-protocol/prefetch/request-will-be-sent-redirect.https.js [ Failure Pass ]
 
@@ -6300,8 +6434,6 @@
 crbug.com/40146374 virtual/select-parser-relaxation-opt-out/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-picker-interactive-element-focus.tentative.html [ Failure ]
 crbug.com/40146374 virtual/select-parser-relaxation-opt-out/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-parsing.tentative.html [ Failure ]
 
-crbug.com/415205686 editing/input/selectall-in-input-with-text-security.html [ Crash Failure ]
-
 # Flaky on headless_shell_wpt_tests
 crbug.com/40146374 external/wpt/html/semantics/forms/the-select-element/customizable-select/select-option-images.tentative.html [ Failure Pass ]
 
@@ -8295,7 +8427,7 @@
 external/wpt/webdriver/tests/bidi/browsing_context/create/background.py [ Failure Pass ]
 external/wpt/webdriver/tests/bidi/browsing_context/set_viewport/viewport.py [ Failure Pass ]
 external/wpt/webdriver/tests/bidi/network/remove_intercept/remove_intercept.py [ Failure Pass Timeout ]
-external/wpt/webdriver/tests/bidi/network/response_completed/response_completed.py [ Failure Pass Timeout ]
+[ Linux ] external/wpt/webdriver/tests/bidi/network/response_completed/response_completed.py [ Failure Pass Timeout ]
 
 # Gardener 2024-03-21 (PST)
 crbug.com/330668788 [ Linux ] external/wpt/html/semantics/embedded-content/media-elements/track/track-element/track-cues-enter-seeking.html [ Failure Pass ]
@@ -8929,9 +9061,9 @@
 crbug.com/377777852 [ Linux ] virtual/private-aggregation-developer-mode/wpt_internal/private-aggregation/shared-storage-filtering-id-sends-report.https.window.html [ Pass Timeout ]
 crbug.com/377927602 external/wpt/selection/caret/collapse-pre-linestart-2.html [ Failure Pass ]
 crbug.com/377927602 external/wpt/selection/caret/editing-host-has-only-invisible-br.html [ Failure Pass Timeout ]
-crbug.com/377938333 external/wpt/webdriver/tests/bidi/browsing_context/context_created/original_opener.py [ Failure Pass Timeout ]
+crbug.com/377938333 [ Linux ] external/wpt/webdriver/tests/bidi/browsing_context/context_created/original_opener.py [ Failure Pass Timeout ]
 crbug.com/377938333 external/wpt/webdriver/tests/bidi/browsing_context/print/page.py [ Failure Pass Timeout ]
-crbug.com/377938333 external/wpt/webdriver/tests/classic/element_clear/disabled.py [ Failure Pass Timeout ]
+crbug.com/377938333 [ Linux ] external/wpt/webdriver/tests/classic/element_clear/disabled.py [ Failure Pass Timeout ]
 crbug.com/377938333 external/wpt/webdriver/tests/classic/execute_async_script/arguments.py [ Failure Pass Timeout ]
 crbug.com/377938333 external/wpt/webdriver/tests/classic/find_element/find.py [ Failure Pass Timeout ]
 crbug.com/377938333 external/wpt/webdriver/tests/classic/navigate_to/file.py [ Failure Pass Timeout ]
@@ -9208,12 +9340,13 @@
 crbug.com/414243950 [ Linux ] virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.sharedworker.html?cpu [ Crash ]
 crbug.com/414243950 [ Linux ] virtual/webnn-service-on-cpu/external/wpt/webnn/conformance_tests/qdq_subgraph.https.any.worker.html?cpu [ Crash ]
 
-# Gardener 2025-04-30
-crbug.com/324536287 external/wpt/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html [ Failure Pass ]
-
 # Gardener 2025-05-01
 crbug.com/411161567 [ Linux ] accessibility/interest-target.html [ Failure Pass ]
 
+crbug.com/407751668 http/tests/devtools/elements/styles-4/styles-overloaded-shorthand.js [ Failure Pass ]
+crbug.com/407751668 http/tests/devtools/elements/styles-4/styles-properties-overload.js [ Failure Pass ]
+crbug.com/407751668 http/tests/devtools/elements/styles-4/styles-update-from-js.js [ Failure Pass ]
+
 # Gardener 2025-05-05
 crbug.com/413289778 [ Mac12 ] wpt_internal/fedcm/fedcm-cache-manifest.https.html [ Failure ]
 crbug.com/415747471 [ Mac ] virtual/text-antialias/international/bidi-CS-after-AN.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index 8d2f75a..82ec477 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -886,18 +886,6 @@
     "args": ["--enable-font-antialiasing",  "--text-contrast=0.5", "--text-gamma=0.0"],
     "expires": "Jan 1, 2026"
   },
-  " Temporary test suite running virtual/text-antialias using FreeType, until   ",
-  " we are ready to remove FreeType and stop rolling FreeType into Chromium.    ",
-  " Until then, we need pixel coverage for font rendering tests running through ",
-  " FreeType.                                                                   ",
-  {
-    "prefix": "text-antialias-freetype",
-    "owners": ["drott@chromium.org"],
-    "platforms": ["Linux"],
-    "bases": [ "virtual/text-antialias" ],
-    "args": ["--enable-font-antialiasing", "--disable-features=FontationsFontBackend"],
-    "expires": "Jul 1, 2025"
-  },
   {
     "prefix": "text-contrast-gamma",
     "owners": ["kschmi@microsoft.com"],
@@ -1673,7 +1661,7 @@
     "expires": "Oct 1, 2025"
   },
   {
-    "prefix": "prefetch-serviceworker",
+    "prefix": "prefetch-sw",
     "owners": ["hiroshige@chromium.org"],
     "platforms": ["Linux", "Mac", "Win"],
     "bases": [
diff --git a/third_party/blink/web_tests/accessibility/inline-text-textarea-expected.txt b/third_party/blink/web_tests/accessibility/inline-text-textarea-expected.txt
index b5226e7..502256ff 100644
--- a/third_party/blink/web_tests/accessibility/inline-text-textarea-expected.txt
+++ b/third_party/blink/web_tests/accessibility/inline-text-textarea-expected.txt
@@ -3,7 +3,7 @@
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 PASS firstInlineTextBoxBefore.isEqual(firstInlineTextBoxAfter) is false
-FAIL lastInlineTextBoxBefore.isEqual(lastInlineTextBoxAfter) should be true. Was false.
+PASS lastInlineTextBoxBefore.isEqual(lastInlineTextBoxAfter) is true
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/editing/input/selectall-in-input-with-text-security.html b/third_party/blink/web_tests/editing/input/selectall-in-input-with-text-security.html
deleted file mode 100644
index e6faa01..0000000
--- a/third_party/blink/web_tests/editing/input/selectall-in-input-with-text-security.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!doctype html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<title>This test should not crash.</title>
-<input type="password" id="testnode">
-<script>
-async_test( t => {
-  window.addEventListener('DOMContentLoaded', () => {
-    internals.settings.setPasswordEchoEnabled(true);
-    const testnode = document.getElementById('testnode');
-    testnode.focus();
-    document.execCommand("InsertHTML", false, "12x&#x305;&#x332;45");
-    document.execCommand("selectAll");
-    t.step_timeout(()=>{
-      t.done();
-    },1100);
-  });
-}, 'The renderer should not crash');
-</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/linear-gradient-calc-em-units-ref.html b/third_party/blink/web_tests/external/wpt/css/css-images/linear-gradient-calc-em-units-ref.html
new file mode 100644
index 0000000..be13be8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/linear-gradient-calc-em-units-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<title>CSS Test Reference</title>
+<style>
+  div { width: 100px; height: 100px; }
+</style>
+<div style="background: linear-gradient(blue 20px, yellow)"></div>
+<div style="background: linear-gradient(blue 60px, yellow)"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-images/linear-gradient-calc-em-units.html b/third_party/blink/web_tests/external/wpt/css/css-images/linear-gradient-calc-em-units.html
new file mode 100644
index 0000000..e764e628
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-images/linear-gradient-calc-em-units.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>CSS Images Test: Linear gradient with em in calc()</title>
+<link rel="help" href="https://drafts.csswg.org/css-images/#linear-gradients">
+<link rel="match" href="linear-gradient-calc-em-units-ref.html">
+<style>
+  div {
+    width: 100px;
+    height: 100px;
+    background: linear-gradient(blue calc(2em), yellow);
+  }
+  #em1 {
+    font-size: 10px;
+  }
+  #em2 {
+    font-size: 30px;
+  }
+</style>
+<div id="em1"></div>
+<div id="em2"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-scroll-anchoring/table-col-and-dead-row-group-crash.html b/third_party/blink/web_tests/external/wpt/css/css-scroll-anchoring/table-col-and-dead-row-group-crash.html
new file mode 100644
index 0000000..e6e90a6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-scroll-anchoring/table-col-and-dead-row-group-crash.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://issues.chromium.org/issues/416061199">
+<div style="display:table;">
+  <div style="display:table-column-group;"></div>
+  <div id="e13" style="display:table-row-group;">
+    <div style="height:200vh;"></div>
+    x
+  </div>
+</div>
+<script>
+  window.scroll(0, 100);
+  e13.style.display = "none";
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/urls/fragment-only-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-values/urls/fragment-only-expected.txt
new file mode 100644
index 0000000..e79da1e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/urls/fragment-only-expected.txt
@@ -0,0 +1,13 @@
+This is a testharness.js-based test.
+[FAIL] empty URL: inline-unquoted
+  assert_equals: expected "url(\\"#foo\\")" but got "url(\\"http://web-platform.test:8001/css/css-values/urls/fragment-only.html#foo\\")"
+[FAIL] empty URL: inline-quoted
+  assert_equals: expected "url(\\"#foo\\")" but got "url(\\"http://web-platform.test:8001/css/css-values/urls/fragment-only.html#foo\\")"
+[FAIL] empty URL: external-unquoted
+  assert_equals: expected "url(\\"#foo\\")" but got "url(\\"http://web-platform.test:8001/css/css-values/urls/support/fragment-only-urls.css#foo\\")"
+[FAIL] empty URL: external-quoted
+  assert_equals: expected "url(\\"#foo\\")" but got "url(\\"http://web-platform.test:8001/css/css-values/urls/support/fragment-only-urls.css#foo\\")"
+[FAIL] empty URL: external-variable
+  assert_equals: expected "url(\\"#foo\\")" but got "url(\\"http://web-platform.test:8001/css/css-values/urls/support/fragment-only-urls.css#foo\\")"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/encoding/OWNERS b/third_party/blink/web_tests/external/wpt/encoding/OWNERS
index 4392ef47..4a32e24 100644
--- a/third_party/blink/web_tests/external/wpt/encoding/OWNERS
+++ b/third_party/blink/web_tests/external/wpt/encoding/OWNERS
@@ -1 +1,2 @@
 ricea@chromium.org
+yyanagisawa@chromium.org
diff --git a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html
index 7b884f2..5961a6e 100644
--- a/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html
@@ -1,73 +1,93 @@
-<!DOCTYPE HTML>
+<!doctype html>
 <html>
-<head>
-<meta charset="utf-8">
-<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 src="resources/soft-navigation-helper.js"></script>
-</head>
-<body>
-  <main id=main>
-    <div>
-      <a id=link><img src="/images/lcp-256x256.png" id="img"></a>
-      <a id=not_nav><img src="/images/lcp-16x16.png"></a>
+  <head>
+    <meta charset="utf-8" />
+    <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>
+  </head>
+  <body>
+    <div id="main">
+      <div id="almost_soft_nav_div">
+        A click will almost initiate a soft nav, by (1) mutating the DOM by adding an image; (2)
+        causing the image to be painted.
+      </div>
+      <div id="soft_nav_div">
+        A click will initiate a soft nav, by (1) mutating the DOM by adding an image; (2) causing
+        the image to be painted; (3) updating the URL via history.back(), but without causing a hard
+        navigation.
+      </div>
     </div>
-  </main>
-  <script>
-    // Push state a couple of times
-    history.pushState({}, "", "foobar.html");
-    history.pushState({}, "", "anotherOne.html");
-
-    (async () => {
-      const link = document.getElementById("link");
-      // Trigger a user interaction that doesn't result in a soft navigation, but
-      // does paint.
-      await (async () => {
-        const not_nav = document.getElementById("not_nav");
-        let non_soft_nav_click;
-        const non_soft_nav_click_promise =
-          new Promise(r => { non_soft_nav_click = r; });
-        not_nav.addEventListener("click", () => {
-          addImageToMain("lcp-133x106.png", "not_soft_nav_image");
-          (new PerformanceObserver(non_soft_nav_click)).observe({type: "element"});
-        });
-        if (test_driver) {
-          test_driver.click(not_nav);
-        }
-        await non_soft_nav_click_promise;
-      })();
-      const url = URL + "?" + counter;
-      link.addEventListener("click", () => {
-        // Add an LCP element.
-        const img = new Image();
-        img.src = '/images/lcp-100x500.png' + "?" + Math.random();
-        document.getElementById("main").appendChild(img);
-        history.back();
-      });
-      promise_test(async t => {
-        const soft_nav_promise = waitOnSoftNav();
-        if (test_driver) {
-          test_driver.click(link);
-        }
-        await soft_nav_promise;
-        assert_equals(
-            document.softNavigations, 1,
-            'Single Soft Navigation detected');
-        const [entries, options] = await new Promise(resolve => {
-          (new PerformanceObserver((list, obs, options) => resolve(
-            [list.getEntries(), options]))).observe(
-            {type: 'soft-navigation', buffered: true});
+    <script>
+      promise_test(
+        async (t) => {
+          // Setup a click handler for 'almost_soft_nav_div', which adds a
+          // specific, identifiable image 'almost_soft_nav_img' to the document.
+          document.getElementById("almost_soft_nav_div").addEventListener("click", () => {
+            const img = new Image();
+            img.src = "/images/lcp-133x106.png";
+            img.elementTiming = "almost_soft_nav_img";
+            document.getElementById("main").appendChild(img);
           });
 
-        assert_equals(entries.length, 1,
-                      "Performance observer got an entry");
-      }, "Ensure that soft navigation entry emitted through a synchronous " +
-         "event that modified DOM and committed a same document navigation, " +
-         "and that was preceded by a user intreaction that resulted in a " +
-         "contentful paint is properly detected.");
-    })();
-  </script>
-</body>
+          // Also set up a click handler for 'soft_nav_div', for adding
+          // another image, but in addition to what we do for the almost soft nav
+          // above, also let the handler change the URL, by invoking
+          // history.back(). We prepare with pushState for the history.back()
+          // invocation, to avoid triggering a hard navigation.
+          const test_origin = new URL(location.href).origin;
+          history.pushState({}, "", "/foo.html"); // We will observe this below.
+          history.pushState({}, "", "/bar.html"); // Prep for history.back().
+          document.getElementById("soft_nav_div").addEventListener("click", () => {
+            const img = new Image();
+            img.src = "/images/lcp-133x106.png";
+            document.getElementById("main").appendChild(img);
+            history.back(); // URL change triggering the soft nav
+          });
+
+          // Now, click almost_soft_nav_div, and observe that time image
+          // was rendered but no soft nav was triggered.
+          {
+            const element_timing_promise = new Promise((resolve) => {
+              new PerformanceObserver(resolve).observe({ type: "element", buffered: true });
+            });
+            if (test_driver) {
+              test_driver.click(almost_soft_nav_div);
+            }
+            // Returns entries of type PerformanceElementTiming, see
+            // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceElementTiming.
+            const entries = (await element_timing_promise).getEntries();
+            assert_equals(entries.length, 1);
+            assert_equals(
+              entries[0].identifier,
+              "almost_soft_nav_img",
+              "Image based on the user interaction was painted.",
+            );
+            assert_equals(document.softNavigations, 0, "No soft navigation detected.");
+          }
+
+          // Now, click soft_nav_div, and observe the detected soft navigation.
+          {
+            const soft_nav_promise = new Promise((resolve) => {
+              new PerformanceObserver(resolve).observe({ type: "soft-navigation", buffered: true });
+            });
+            if (test_driver) {
+              test_driver.click(soft_nav_div);
+            }
+            // Returns entries of type SoftNavigationEntry, see
+            // https://github.com/WICG/soft-navigations/
+            const entries = (await soft_nav_promise).getEntries();
+            assert_equals(document.softNavigations, 1, "Single soft navigation detected");
+            assert_equals(entries.length, 1, "Performance observer got an entry");
+            assert_equals(entries[0].name, test_origin + "/foo.html");
+          }
+        },
+        "Ensure that soft navigation entry emitted through a synchronous " +
+          "event that modified DOM and committed a same document navigation, " +
+          "and that was preceded by a user interaction that resulted in a " +
+          "contentful paint is properly detected.",
+      );
+    </script>
+  </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/log.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/log.https.any.js
index 011beef..8ed807b 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/log.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/log.https.any.js
@@ -14,11 +14,8 @@
 // MLOperand log(MLOperand input);
 
 
-const getLogPrecisionTolerance = (graphResources) => {
-  const toleranceValueDict = {float32: 1 / 1024, float16: 1 / 1024};
-  const expectedDataType =
-      getExpectedDataTypeOfSingleOutput(graphResources.expectedOutputs);
-  return {metricType: 'ATOL', value: toleranceValueDict[expectedDataType]};
+const getLogPrecisionTolerance = () => {
+  return {metricType: 'ULP', value: 8};
 };
 
 const logTests = [
diff --git a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/sign.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/sign.https.any.js
index 4c3a330..004c03b 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/sign.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/sign.https.any.js
@@ -122,7 +122,8 @@
     'graph': {
       'inputs': {
         'signInput': {
-          'data': [-1, 0, 1, 2],
+          // int32 range: [/* -(2**31) */ -2147483648, /* 2**31 - 1 */ 2147483647]
+          'data': [-2147483648, 0, 2147483646, 2147483647],
           'descriptor': {shape: [2, 2], dataType: 'int32'}
         }
       },
@@ -166,7 +167,8 @@
     'graph': {
       'inputs': {
         'signInput': {
-          'data': [-1, 0, 1, 2, -2, -1, 0, 1],
+          // int8 range: [/* -(2**7) */ -128, /* 2**7 - 1 */ 127]
+          'data': [-128, 0, 1, 2, -2, -1, 0, 127],
           'descriptor': {shape: [1, 2, 2, 2], dataType: 'int8'}
         }
       },
diff --git a/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-clone.https.html b/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-clone.https.html
index 9f07713..c93f8b3 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-clone.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-clone.https.html
@@ -37,7 +37,7 @@
       const original = result.value;
       let clone = structuredClone(original);
       assert_equals(original.timestamp, clone.timestamp);
-      assert_equals(original.getMetadata().absCaptureTime, clone.getMetadata().absCaptureTime);
+      assert_equals(original.getMetadata().captureTime, clone.getMetadata().captureTime);
       assert_array_equals(Array.from(original.data), Array.from(clone.data));
       await writer2.write(clone);
       resolve();
diff --git a/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-metadata.https.html b/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-metadata.https.html
index 435e1c0..df4577c5 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-metadata.https.html
+++ b/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-metadata.https.html
@@ -39,7 +39,7 @@
       assert_true(original.getMetadata().hasOwnProperty('receiveTime'));
       assert_true(original.getMetadata().receiveTime > 0);
       assert_equals(original.getMetadata().rtpTimestamp, newFrame.getMetadata().rtpTimestamp);
-      assert_equals(original.getMetadata().absCaptureTime, newFrame.getMetadata().absCaptureTime);
+      assert_equals(original.getMetadata().captureTime, newFrame.getMetadata().captureTime);
       assert_equals(original.getMetadata().receiveTime, newFrame.getMetadata().receiveTime);
       assert_array_equals(Array.from(original.data), Array.from(newFrame.data));
       await writer2.write(newFrame);
@@ -83,7 +83,7 @@
       assert_not_equals(original.getMetadata().rtpTimestamp, newFrame.getMetadata().rtpTimestamp);
       assert_equals(newMetadata.rtpTimestamp, newFrame.getMetadata().rtpTimestamp);
       assert_equals(original.getMetadata().receiveTime, newFrame.getMetadata().receiveTime);
-      assert_equals(original.getMetadata().absCaptureTime, newFrame.getMetadata().absCaptureTime);
+      assert_equals(original.getMetadata().captureTime, newFrame.getMetadata().captureTime);
       assert_array_equals(Array.from(original.data), Array.from(newFrame.data));
       await writer2.write(newFrame);
       resolve();
diff --git a/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCPeerConnection-insertable-streams.js b/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCPeerConnection-insertable-streams.js
index 0bf820ac..f3873e1 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCPeerConnection-insertable-streams.js
+++ b/third_party/blink/web_tests/external/wpt/webrtc-encoded-transform/tentative/RTCPeerConnection-insertable-streams.js
@@ -30,7 +30,7 @@
           metadata1.payloadType == metadata2.payloadType &&
           areArraysEqual(
               metadata1.contributingSources, metadata2.contributingSources) &&
-          metadata1.absCaptureTime == metadata2.absCaptureTime &&
+          metadata1.captureTime == metadata2.captureTime &&
           metadata1.frameId === metadata2.frameId &&
           areArraysEqual(metadata1.dependencies, metadata2.dependencies) &&
           metadata1.spatialIndex === metadata2.spatialIndex &&
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/ellipsis-platform-font-change-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/ellipsis-platform-font-change-expected.png
deleted file mode 100644
index 9970a54..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/ellipsis-platform-font-change-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/emphasis-complex-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/emphasis-complex-expected.png
deleted file mode 100644
index b007456..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/emphasis-complex-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/fake-italic-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/fake-italic-expected.png
deleted file mode 100644
index 98438466..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/fake-italic-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/font-features/caps-native-synthesis-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/font-features/caps-native-synthesis-expected.png
deleted file mode 100644
index 383456b..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/font-features/caps-native-synthesis-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/font-size-adjust-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/font-size-adjust-expected.png
deleted file mode 100644
index 44108e26..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/font-size-adjust-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/international/arabic-vertical-offset-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/international/arabic-vertical-offset-expected.png
deleted file mode 100644
index 512b78f..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/international/arabic-vertical-offset-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/midword-break-before-surrogate-pair-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/midword-break-before-surrogate-pair-expected.png
deleted file mode 100644
index 71fc612..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/midword-break-before-surrogate-pair-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/shadow-translucent-fill-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/shadow-translucent-fill-expected.png
deleted file mode 100644
index da6f277..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/shadow-translucent-fill-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/shaping/same-script-different-lang-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/shaping/same-script-different-lang-expected.png
deleted file mode 100644
index 38e3b71..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/shaping/same-script-different-lang-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/webfont-synthetic-bold-expected.png b/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/webfont-synthetic-bold-expected.png
deleted file mode 100644
index d0a2638..0000000
--- a/third_party/blink/web_tests/platform/linux/virtual/text-antialias-freetype/virtual/text-antialias/webfont-synthetic-bold-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/log.https.any_gpu-expected.txt b/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/log.https.any_gpu-expected.txt
deleted file mode 100644
index fd19bb8..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/webnn-service-with-gpu/external/wpt/webnn/conformance_tests/log.https.any_gpu-expected.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-This is a testharness.js-based test.
-[FAIL] log float16 positive 0D scalar
-  assert_array_approx_equals: test log float16 property 0, expected 4.15625 +/- 0.0009765625, expected 4.15625 but got 4.15234375
-[FAIL] log float16 positive 1D constant tensor
-  assert_array_approx_equals: test log float16 property 0, expected 4.15625 +/- 0.0009765625, expected 4.15625 but got 4.15234375
-[FAIL] log float16 positive 1D tensor
-  assert_array_approx_equals: test log float16 property 0, expected 4.15625 +/- 0.0009765625, expected 4.15625 but got 4.15234375
-[FAIL] log float16 positive 2D tensor
-  assert_array_approx_equals: test log float16 property 0, expected 4.15625 +/- 0.0009765625, expected 4.15625 but got 4.15234375
-[FAIL] log float16 positive 3D tensor
-  assert_array_approx_equals: test log float16 property 0, expected 4.15625 +/- 0.0009765625, expected 4.15625 but got 4.15234375
-[FAIL] log float16 positive 4D tensor
-  assert_array_approx_equals: test log float16 property 0, expected 4.15625 +/- 0.0009765625, expected 4.15625 but got 4.15234375
-[FAIL] log float16 positive 5D tensor
-  assert_array_approx_equals: test log float16 property 0, expected 4.15625 +/- 0.0009765625, expected 4.15625 but got 4.15234375
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/printing/print-color-adjust-001-expected.html b/third_party/blink/web_tests/printing/print-color-adjust-001-expected.html
new file mode 100644
index 0000000..efcbef834
--- /dev/null
+++ b/third_party/blink/web_tests/printing/print-color-adjust-001-expected.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script>
+  if (window.testRunner) {
+    testRunner.setPrinting();
+    if (window.internals)
+      internals.settings.setShouldPrintBackgrounds(false);
+  }
+</script>
+<p>Test passes if there is nothing below.</p>
diff --git a/third_party/blink/web_tests/printing/print-color-adjust-001.html b/third_party/blink/web_tests/printing/print-color-adjust-001.html
new file mode 100644
index 0000000..be21172f
--- /dev/null
+++ b/third_party/blink/web_tests/printing/print-color-adjust-001.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<title>print-color-adjust:economy is initial value</title>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-color-adjust-1/#print-color-adjust">
+<script>
+  if (window.testRunner) {
+    testRunner.setPrinting();
+    if (window.internals)
+      internals.settings.setShouldPrintBackgrounds(false);
+  }
+</script>
+<p>Test passes if there is nothing below.</p>
+<div style="height:100px; background:red;"></div>
diff --git a/third_party/blink/web_tests/printing/print-color-adjust-002-expected.html b/third_party/blink/web_tests/printing/print-color-adjust-002-expected.html
new file mode 100644
index 0000000..b3d8a40
--- /dev/null
+++ b/third_party/blink/web_tests/printing/print-color-adjust-002-expected.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script>
+  if (window.testRunner) {
+    testRunner.setPrinting();
+    if (window.internals)
+      internals.settings.setShouldPrintBackgrounds(false);
+  }
+</script>
+<p>Test passes if there is no red on this page.</p>
diff --git a/third_party/blink/web_tests/printing/print-color-adjust-002.html b/third_party/blink/web_tests/printing/print-color-adjust-002.html
new file mode 100644
index 0000000..6e4ba2a5
--- /dev/null
+++ b/third_party/blink/web_tests/printing/print-color-adjust-002.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>print-color-adjust:economy is initial value</title>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-color-adjust-1/#print-color-adjust">
+<script>
+  if (window.testRunner) {
+    testRunner.setPrinting();
+    if (window.internals)
+      internals.settings.setShouldPrintBackgrounds(false);
+  }
+</script>
+<style>
+  body {
+    background: red;
+  }
+</style>
+<p>Test passes if there is no red on this page.</p>
diff --git a/third_party/blink/web_tests/printing/print-color-adjust-003-expected.html b/third_party/blink/web_tests/printing/print-color-adjust-003-expected.html
new file mode 100644
index 0000000..05e500a
--- /dev/null
+++ b/third_party/blink/web_tests/printing/print-color-adjust-003-expected.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<script>
+  if (window.testRunner) {
+    testRunner.setPrinting();
+    if (window.internals)
+      internals.settings.setShouldPrintBackgrounds(false);
+  }
+</script>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="width:100px; height:100px; print-color-adjust:exact; background-color:green;"></div>
diff --git a/third_party/blink/web_tests/printing/print-color-adjust-003.html b/third_party/blink/web_tests/printing/print-color-adjust-003.html
new file mode 100644
index 0000000..2c064e7
--- /dev/null
+++ b/third_party/blink/web_tests/printing/print-color-adjust-003.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>print-color-adjust inheritance and nesting</title>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-color-adjust-1/#print-color-adjust">
+<script>
+  if (window.testRunner) {
+    testRunner.setPrinting();
+    if (window.internals)
+      internals.settings.setShouldPrintBackgrounds(false);
+  }
+</script>
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="width:110px; height:110px; print-color-adjust:economy; background:red;">
+  <div style="float:left; width:50px; height:100px; print-color-adjust:exact; background:green;"></div>
+  <div style="display:flow-root; width:50px; print-color-adjust:exact;">
+    <div style="width:50px; height:100px; background:green;"></div>
+    <div style="height:10px; print-color-adjust:economy; background:red;"></div>
+  </div>
+</div>
diff --git a/third_party/blink/web_tests/virtual/prefetch-serviceworker/DIR_METADATA b/third_party/blink/web_tests/virtual/prefetch-sw/DIR_METADATA
similarity index 100%
rename from third_party/blink/web_tests/virtual/prefetch-serviceworker/DIR_METADATA
rename to third_party/blink/web_tests/virtual/prefetch-sw/DIR_METADATA
diff --git a/third_party/blink/web_tests/virtual/prefetch-serviceworker/README.md b/third_party/blink/web_tests/virtual/prefetch-sw/README.md
similarity index 100%
rename from third_party/blink/web_tests/virtual/prefetch-serviceworker/README.md
rename to third_party/blink/web_tests/virtual/prefetch-sw/README.md
diff --git a/third_party/blink/web_tests/virtual/text-antialias-freetype/README.md b/third_party/blink/web_tests/virtual/text-antialias-freetype/README.md
deleted file mode 100644
index df99b69..0000000
--- a/third_party/blink/web_tests/virtual/text-antialias-freetype/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Temporary test suite running virtual/text-antialias using FreeType, until
-we are ready to remove FreeType and stop rolling FreeType into Chromium.
-Until then, we need pixel coverage for font rendering tests running through
-FreeType.
diff --git a/third_party/boringssl/src b/third_party/boringssl/src
index 0f1d0df..864a235 160000
--- a/third_party/boringssl/src
+++ b/third_party/boringssl/src
@@ -1 +1 @@
-Subproject commit 0f1d0df6183d6ddf0b4d7a10bf80122c7ec260e6
+Subproject commit 864a235afcf4d2575b1eab8de96fbf0d84f6cda9
diff --git a/third_party/catapult b/third_party/catapult
index 52ad7cf..64c31ff 160000
--- a/third_party/catapult
+++ b/third_party/catapult
@@ -1 +1 @@
-Subproject commit 52ad7cf544050f01f47c070716b9dae8eb9fab2b
+Subproject commit 64c31ffa4d735add4a7e7520a52e0e3160216132
diff --git a/third_party/chromite b/third_party/chromite
index 73e7b77..96096c3 160000
--- a/third_party/chromite
+++ b/third_party/chromite
@@ -1 +1 @@
-Subproject commit 73e7b77d6878e58c3d26d5fe0d5877cf9ac44597
+Subproject commit 96096c3c83cbe537782e84dfc12981f146723f6a
diff --git a/third_party/crossbench b/third_party/crossbench
index 35e8177a..73e0cfe 160000
--- a/third_party/crossbench
+++ b/third_party/crossbench
@@ -1 +1 @@
-Subproject commit 35e8177a7d7594203c543fe3875fd587b949fca2
+Subproject commit 73e0cfe22e2699bf14bedb489c0b39ba1bc03702
diff --git a/third_party/dawn b/third_party/dawn
index 5b595fd..dfe3855 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit 5b595fdbcfc1d0d79354ab74f675c564273874c7
+Subproject commit dfe3855e5d0b5367a598e60674766ffa1c894c71
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index cd65015..e5e1d2e 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit cd650159ff9210d83ed1e1aaf02b1c071d5123b3
+Subproject commit e5e1d2ed56cb0ad0c82a0b36295cae4578d8a226
diff --git a/third_party/eigen3/README.chromium b/third_party/eigen3/README.chromium
index 337dbcdc..386b8ba6 100644
--- a/third_party/eigen3/README.chromium
+++ b/third_party/eigen3/README.chromium
@@ -1,8 +1,8 @@
 Name: Eigen
 Short Name: eigen3
 URL: https://gitlab.com/libeigen/eigen
-Version: 464c1d097891a1462ab28bf8bb763c1683883892
-Date: 2025-03-13
+Version: 729443409942a1816ddf74b95224003b83f4925c
+Date: 2025-05-07
 License: MPL-2.0
 License File: LICENSE
 Security Critical: Yes
diff --git a/third_party/eigen3/src b/third_party/eigen3/src
index 464c1d0..7294434 160000
--- a/third_party/eigen3/src
+++ b/third_party/eigen3/src
@@ -1 +1 @@
-Subproject commit 464c1d097891a1462ab28bf8bb763c1683883892
+Subproject commit 729443409942a1816ddf74b95224003b83f4925c
diff --git a/third_party/glslang/src b/third_party/glslang/src
index fc9889c..9635880 160000
--- a/third_party/glslang/src
+++ b/third_party/glslang/src
@@ -1 +1 @@
-Subproject commit fc9889c889561c5882e83819dcaffef5ed45529b
+Subproject commit 963588074b26326ff0426c8953c1235213309bdb
diff --git a/third_party/llvm-libc/src b/third_party/llvm-libc/src
index d7bdad4..e3e030e 160000
--- a/third_party/llvm-libc/src
+++ b/third_party/llvm-libc/src
@@ -1 +1 @@
-Subproject commit d7bdad4ef86b827a96469b1dfdfcfa1218930e59
+Subproject commit e3e030ec6ee1674bf2195d0cfd0a4bf5fee16537
diff --git a/third_party/pdfium b/third_party/pdfium
index e265bde6..b8e8a35 160000
--- a/third_party/pdfium
+++ b/third_party/pdfium
@@ -1 +1 @@
-Subproject commit e265bde6fee233a96f87ca46464dd662531e0f90
+Subproject commit b8e8a35d09143db3342b94ddef076c03aa46159f
diff --git a/third_party/rust/font_types/OWNERS b/third_party/rust/font_types/OWNERS
new file mode 100644
index 0000000..8dd30b61
--- /dev/null
+++ b/third_party/rust/font_types/OWNERS
@@ -0,0 +1 @@
+file://third_party/rust/skrifa/OWNERS
diff --git a/third_party/rust/read_fonts/OWNERS b/third_party/rust/read_fonts/OWNERS
new file mode 100644
index 0000000..8dd30b61
--- /dev/null
+++ b/third_party/rust/read_fonts/OWNERS
@@ -0,0 +1 @@
+file://third_party/rust/skrifa/OWNERS
diff --git a/third_party/rust/skrifa/OWNERS b/third_party/rust/skrifa/OWNERS
new file mode 100644
index 0000000..e6c414c4
--- /dev/null
+++ b/third_party/rust/skrifa/OWNERS
@@ -0,0 +1,2 @@
+bungeman@google.com
+drott@chromium.org
diff --git a/third_party/spirv-headers/src b/third_party/spirv-headers/src
index bab63ff..6d0784e 160000
--- a/third_party/spirv-headers/src
+++ b/third_party/spirv-headers/src
@@ -1 +1 @@
-Subproject commit bab63ff679c41eb75fc67dac76e1dc44426101e1
+Subproject commit 6d0784e9f1ab92c17eeea94821b2465c14a52be9
diff --git a/third_party/spirv-tools/src b/third_party/spirv-tools/src
index 8e9165a..e8864ed 160000
--- a/third_party/spirv-tools/src
+++ b/third_party/spirv-tools/src
@@ -1 +1 @@
-Subproject commit 8e9165a3d162967a424dcf2ff645a98b50381cce
+Subproject commit e8864edbebe9fb9872c6c95b2363b490c6105a15
diff --git a/third_party/swiftshader b/third_party/swiftshader
index 093b4d8..930d46d 160000
--- a/third_party/swiftshader
+++ b/third_party/swiftshader
@@ -1 +1 @@
-Subproject commit 093b4d82a49affdcc5e6d68cc17aa0c33c82e9a2
+Subproject commit 930d46d31b5d637f313fd5ef55da2bbf053c26c1
diff --git a/third_party/tflite/BUILD.gn b/third_party/tflite/BUILD.gn
index 561d69f..1ccc957a 100644
--- a/third_party/tflite/BUILD.gn
+++ b/third_party/tflite/BUILD.gn
@@ -11,6 +11,7 @@
 import("//third_party/mediapipe/features.gni")
 import("//third_party/protobuf/proto_library.gni")
 import("//third_party/tflite/features.gni")
+import("//third_party/tflite/tf_version.gni")
 import("//third_party/tflite/tflite_target.gni")
 
 proto_library("tflite_proto") {
@@ -48,7 +49,13 @@
     "src/third_party/xla/third_party/tsl",
     "//third_party/pthreadpool/src/include",
   ]
-  defines = [ "TFL_STATIC_LIBRARY_BUILD" ]
+  defines = [
+    "TFL_STATIC_LIBRARY_BUILD",
+    "TF_MAJOR_VERSION=" + tf_version_major,
+    "TF_MINOR_VERSION=" + tf_version_minor,
+    "TF_PATCH_VERSION=" + tf_version_patch,
+    "TF_VERSION_SUFFIX",
+  ]
   if (is_android) {
     libs = [ "log" ]
   }
diff --git a/third_party/tflite/README.chromium b/third_party/tflite/README.chromium
index ee4b5f9..b5c84b7 100644
--- a/third_party/tflite/README.chromium
+++ b/third_party/tflite/README.chromium
@@ -1,8 +1,8 @@
 Name: TensorFlow Lite
 Short Name: tflite
 URL: https://github.com/tensorflow/tensorflow
-Version: 829a168408c4422b4290eba063287542a608382e
-Date: 2025-04-28
+Version: c4aa8ff91256b43fe0cecc246c77a00e2145b3bd
+Date: 2025-05-07
 License: Caffe, Apache-2.0
 License File: LICENSE
 Security Critical: Yes
diff --git a/third_party/tflite/generate_tf_version_gni.py b/third_party/tflite/generate_tf_version_gni.py
new file mode 100755
index 0000000..e7570da
--- /dev/null
+++ b/third_party/tflite/generate_tf_version_gni.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+# 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.
+"""Convert tf_version.bzl to tf_version.gni."""
+
+import os
+import re
+import sys
+
+_TMPL = '''
+# 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.
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+#
+#     THIS FILE IS AUTO-GENERATED. DO NOT EDIT.
+#
+#     See //third_party/tflite/generate_tf_version_gni.py
+#
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+tf_version_major = %TF_VERSION_MAJOR%
+tf_version_minor = %TF_VERSION_MINOR%
+tf_version_patch = %TF_VERSION_PATCH%
+'''.lstrip()
+
+
+def _tflite_dir() -> str:
+    """Returns the absolute path of //third_party/tflite/."""
+    return os.path.dirname(os.path.realpath(__file__))
+
+
+def _tensorflow_dir() -> str:
+    """Returns the absolute path of //third_party/tflite/src/tensorflow/."""
+    return os.path.join(_tflite_dir(), "src", "tensorflow")
+
+
+def main():
+  with open(os.path.join(_tensorflow_dir(), 'tf_version.bzl'), 'r') as f:
+    content = f.read()
+
+  match = re.search(r'TF_VERSION = "(\d+).(\d+).(\d+)"', content)
+  if not match:
+    print("Error: Could not find TF_VERSION in tf_version.bzl")
+    sys.exit(1)
+
+  with open(os.path.join(_tflite_dir(), 'tf_version.gni'), 'w') as f:
+    f.write(_TMPL.replace('%TF_VERSION_MAJOR%', match.group(1)) \
+                 .replace('%TF_VERSION_MINOR%', match.group(2)) \
+                 .replace('%TF_VERSION_PATCH%', match.group(3)))
+
+
+if __name__ == '__main__':
+  main()
diff --git a/third_party/tflite/src b/third_party/tflite/src
index 829a168..c4aa8ff 160000
--- a/third_party/tflite/src
+++ b/third_party/tflite/src
@@ -1 +1 @@
-Subproject commit 829a168408c4422b4290eba063287542a608382e
+Subproject commit c4aa8ff91256b43fe0cecc246c77a00e2145b3bd
diff --git a/third_party/tflite/tf_version.gni b/third_party/tflite/tf_version.gni
new file mode 100644
index 0000000..f381ee0
--- /dev/null
+++ b/third_party/tflite/tf_version.gni
@@ -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.
+
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+#
+#     THIS FILE IS AUTO-GENERATED. DO NOT EDIT.
+#
+#     See //third_party/tflite/generate_tf_version_gni.py
+#
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
+
+tf_version_major = 2
+tf_version_minor = 20
+tf_version_patch = 0
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps
index 96793fb0..0b78635 160000
--- a/third_party/vulkan-deps
+++ b/third_party/vulkan-deps
@@ -1 +1 @@
-Subproject commit 96793fb0ff6fb5d4328cc6f71d84f5cb2d835daf
+Subproject commit 0b7863549d960381696d3cd7485ac6a284122e15
diff --git a/third_party/vulkan-headers/src b/third_party/vulkan-headers/src
index e2e53a7..9c77de5c 160000
--- a/third_party/vulkan-headers/src
+++ b/third_party/vulkan-headers/src
@@ -1 +1 @@
-Subproject commit e2e53a724677f6eba8ff0ce1ccb64ee321785cbd
+Subproject commit 9c77de5c3dd216f28e407eec65ed9c0a296c1f74
diff --git a/third_party/vulkan-loader/src b/third_party/vulkan-loader/src
index fb78607..fefd7ed 160000
--- a/third_party/vulkan-loader/src
+++ b/third_party/vulkan-loader/src
@@ -1 +1 @@
-Subproject commit fb78607414e154c7a5c01b23177ba719c8a44909
+Subproject commit fefd7ed96ef9994f0080dbd078822b07d8637918
diff --git a/third_party/vulkan-tools/src b/third_party/vulkan-tools/src
index 0b81967..ba13d38 160000
--- a/third_party/vulkan-tools/src
+++ b/third_party/vulkan-tools/src
@@ -1 +1 @@
-Subproject commit 0b8196724e4ad28cc7459b82a9b75f252c08cb3e
+Subproject commit ba13d38d06830f714a93c5bb159e6e4bacacf0bc
diff --git a/third_party/vulkan-utility-libraries/src b/third_party/vulkan-utility-libraries/src
index 4e246c5..be40e67 160000
--- a/third_party/vulkan-utility-libraries/src
+++ b/third_party/vulkan-utility-libraries/src
@@ -1 +1 @@
-Subproject commit 4e246c56ec5afb5ad66b9b04374d39ac04675c8e
+Subproject commit be40e67892c83d4752ccfbee7ce690ea88087d2b
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src
index cea6ec1..17cf818 160000
--- a/third_party/vulkan-validation-layers/src
+++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@
-Subproject commit cea6ec1cdd37494c1f0fc5619c6c356ac33372fb
+Subproject commit 17cf8188d8b49bafb6c2f8ecede238f8d2dec5bf
diff --git a/third_party/webgpu-cts/src b/third_party/webgpu-cts/src
index e5e17d8..8b48ab0 160000
--- a/third_party/webgpu-cts/src
+++ b/third_party/webgpu-cts/src
@@ -1 +1 @@
-Subproject commit e5e17d8bb639438e1c99cbf367928a242c3bdef6
+Subproject commit 8b48ab0e88b1c6716598ffa1218783acb0691771
diff --git a/third_party/webrtc b/third_party/webrtc
index d69d080..8ae8263 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit d69d0808c37b4ea338e59a58837ef180023316a6
+Subproject commit 8ae8263ecc04b6fd78886e9906194e71a574f88c
diff --git a/third_party/webxr_test_pages/update_bucket.py b/third_party/webxr_test_pages/update_bucket.py
index 53749d1..a9d7ccd 100755
--- a/third_party/webxr_test_pages/update_bucket.py
+++ b/third_party/webxr_test_pages/update_bucket.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 # Copyright 2019 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
diff --git a/third_party/webxr_test_pages/webxr-samples/gamepad.html b/third_party/webxr_test_pages/webxr-samples/gamepad.html
index 8d9190d..c2e4600 100644
--- a/third_party/webxr_test_pages/webxr-samples/gamepad.html
+++ b/third_party/webxr_test_pages/webxr-samples/gamepad.html
@@ -153,7 +153,7 @@
       class GamepadBoxSet {
         constructor(button_count, y) {
           this.button_y = y;
-          ensure_gamepad_boxes(button_count);
+          this.ensure_gamepad_boxes(button_count);
         }
 
         ensure_gamepad_boxes(button_count) {
@@ -170,7 +170,7 @@
         }
 
         update_state(gamepad) {
-          ensure_gamepad_boxes(gamepad.buttons.length);
+          this.ensure_gamepad_boxes(gamepad.buttons.length);
 
           // A button press (pressing and then releasing a button) will change
           // the associated box's color from red to green (or from green to red
diff --git a/third_party/xnnpack/BUILD.gn b/third_party/xnnpack/BUILD.gn
index 351b7d25..89cc476 100644
--- a/third_party/xnnpack/BUILD.gn
+++ b/third_party/xnnpack/BUILD.gn
@@ -109,6 +109,8 @@
     ":f16-vunary_sse2-no-sse3",
     ":f32-argmaxpool_sse2-no-sse3",
     ":f32-argmaxpool_x64",
+    ":f32-avgpool_avx-no-avx2-no-f16c-no-fma",
+    ":f32-avgpool_avx512f",
     ":f32-avgpool_sse2-no-sse3",
     ":f32-avgpool_x64",
     ":f32-conv-hwc2chw_sse-no-sse2",
@@ -200,7 +202,7 @@
     ":f32-vbinary_x64",
     ":f32-vclamp_avx-no-avx2-no-f16c-no-fma",
     ":f32-vclamp_avx512f",
-    ":f32-vclamp_sse-no-sse2",
+    ":f32-vclamp_sse2-no-sse3",
     ":f32-vclamp_x64",
     ":f32-vcmul_avx512f",
     ":f32-vcmul_f16c-fma-no-avx2",
@@ -233,7 +235,7 @@
     ":f32-vhswish_avx-no-avx2-no-f16c-no-fma",
     ":f32-vhswish_avx512f",
     ":f32-vhswish_f16c-fma-no-avx2",
-    ":f32-vhswish_sse-no-sse2",
+    ":f32-vhswish_sse2-no-sse3",
     ":f32-vhswish_x64",
     ":f32-vlog_avx512f",
     ":f32-vlog_f16c-fma-avx2",
@@ -247,6 +249,9 @@
     ":f32-vlrelu_x64",
     ":f32-vmulcaddc_sse-no-sse2",
     ":f32-vmulcaddc_x64",
+    ":f32-vrelu_avx-no-avx2-no-f16c-no-fma",
+    ":f32-vrelu_avx512f",
+    ":f32-vrelu_sse2-no-sse3",
     ":f32-vrelu_x64",
     ":f32-vrnd_avx-no-avx2-no-f16c-no-fma",
     ":f32-vrnd_avx512f",
@@ -419,6 +424,12 @@
     ":qs8-vmulc_sse2-no-sse3",
     ":qs8-vmulc_sse4.1-no-sse4.2",
     ":qs8-vmulc_x64",
+    ":qs8-vprelu_f16c-fma-avx2",
+    ":qs8-vprelu_x64",
+    ":qs8-vpreluc_f16c-fma-avx2",
+    ":qs8-vpreluc_x64",
+    ":qs8-vrpreluc_f16c-fma-avx2",
+    ":qs8-vrpreluc_x64",
     ":qu8-dwconv_avx-no-avx2-no-f16c-no-fma",
     ":qu8-dwconv_f16c-fma-avx2",
     ":qu8-dwconv_f16c-fma-avx512f-avx512cd-avx512bw-avx512dq-avx512vl",
@@ -480,6 +491,12 @@
     ":qu8-vmulc_sse2-no-sse3",
     ":qu8-vmulc_sse4.1-no-sse4.2",
     ":qu8-vmulc_x64",
+    ":qu8-vprelu_f16c-fma-avx2",
+    ":qu8-vprelu_x64",
+    ":qu8-vpreluc_f16c-fma-avx2",
+    ":qu8-vpreluc_x64",
+    ":qu8-vrpreluc_f16c-fma-avx2",
+    ":qu8-vrpreluc_x64",
     ":reference_x64",
     ":s8-ibilinear_sse2-no-sse3",
     ":s8-ibilinear_sse4.1-no-sse4.2",
@@ -598,6 +615,8 @@
     ":f16-vunary_sse2-no-sse3_standalone",
     ":f32-argmaxpool_sse2-no-sse3_standalone",
     ":f32-argmaxpool_x64_standalone",
+    ":f32-avgpool_avx-no-avx2-no-f16c-no-fma_standalone",
+    ":f32-avgpool_avx512f_standalone",
     ":f32-avgpool_sse2-no-sse3_standalone",
     ":f32-avgpool_x64_standalone",
     ":f32-conv-hwc2chw_sse-no-sse2_standalone",
@@ -689,7 +708,7 @@
     ":f32-vbinary_x64_standalone",
     ":f32-vclamp_avx-no-avx2-no-f16c-no-fma_standalone",
     ":f32-vclamp_avx512f_standalone",
-    ":f32-vclamp_sse-no-sse2_standalone",
+    ":f32-vclamp_sse2-no-sse3_standalone",
     ":f32-vclamp_x64_standalone",
     ":f32-vcmul_avx512f_standalone",
     ":f32-vcmul_f16c-fma-no-avx2_standalone",
@@ -722,7 +741,7 @@
     ":f32-vhswish_avx-no-avx2-no-f16c-no-fma_standalone",
     ":f32-vhswish_avx512f_standalone",
     ":f32-vhswish_f16c-fma-no-avx2_standalone",
-    ":f32-vhswish_sse-no-sse2_standalone",
+    ":f32-vhswish_sse2-no-sse3_standalone",
     ":f32-vhswish_x64_standalone",
     ":f32-vlog_avx512f_standalone",
     ":f32-vlog_f16c-fma-avx2_standalone",
@@ -736,6 +755,9 @@
     ":f32-vlrelu_x64_standalone",
     ":f32-vmulcaddc_sse-no-sse2_standalone",
     ":f32-vmulcaddc_x64_standalone",
+    ":f32-vrelu_avx-no-avx2-no-f16c-no-fma_standalone",
+    ":f32-vrelu_avx512f_standalone",
+    ":f32-vrelu_sse2-no-sse3_standalone",
     ":f32-vrelu_x64_standalone",
     ":f32-vrnd_avx-no-avx2-no-f16c-no-fma_standalone",
     ":f32-vrnd_avx512f_standalone",
@@ -908,6 +930,12 @@
     ":qs8-vmulc_sse2-no-sse3_standalone",
     ":qs8-vmulc_sse4.1-no-sse4.2_standalone",
     ":qs8-vmulc_x64_standalone",
+    ":qs8-vprelu_f16c-fma-avx2_standalone",
+    ":qs8-vprelu_x64_standalone",
+    ":qs8-vpreluc_f16c-fma-avx2_standalone",
+    ":qs8-vpreluc_x64_standalone",
+    ":qs8-vrpreluc_f16c-fma-avx2_standalone",
+    ":qs8-vrpreluc_x64_standalone",
     ":qu8-dwconv_avx-no-avx2-no-f16c-no-fma_standalone",
     ":qu8-dwconv_f16c-fma-avx2_standalone",
     ":qu8-dwconv_f16c-fma-avx512f-avx512cd-avx512bw-avx512dq-avx512vl_standalone",
@@ -969,6 +997,12 @@
     ":qu8-vmulc_sse2-no-sse3_standalone",
     ":qu8-vmulc_sse4.1-no-sse4.2_standalone",
     ":qu8-vmulc_x64_standalone",
+    ":qu8-vprelu_f16c-fma-avx2_standalone",
+    ":qu8-vprelu_x64_standalone",
+    ":qu8-vpreluc_f16c-fma-avx2_standalone",
+    ":qu8-vpreluc_x64_standalone",
+    ":qu8-vrpreluc_f16c-fma-avx2_standalone",
+    ":qu8-vrpreluc_x64_standalone",
     ":reference_x64_standalone",
     ":s8-ibilinear_sse2-no-sse3_standalone",
     ":s8-ibilinear_sse4.1-no-sse4.2_standalone",
@@ -1133,6 +1167,9 @@
     ":f32-vtanh_arm64",
     ":f32-vunary_arm64",
     ":operators_arm64",
+    ":pf16-gemm_arch=armv8.2-a+sve+sve2",
+    ":pf32-gemm_arch=armv8.2-a+sve+sve2",
+    ":pqs8-qc8w-gemm_arch=armv8.2-a+sve+sve2",
     ":qd8-f16-qb4w-gemm_arch=armv8.2-a+dotprod+fp16",
     ":qd8-f16-qb4w-gemm_arch=armv8.2-a+fp16",
     ":qd8-f16-qb4w-gemm_arch=armv8.2-a+i8mm+fp16",
@@ -1165,6 +1202,7 @@
     ":qp8-f32-qb4w-gemm_arch=armv8.2-a+i8mm+fp16",
     ":qp8-f32-qc4w-gemm_arch=armv8.2-a+dotprod",
     ":qp8-f32-qc4w-gemm_arch=armv8.2-a+i8mm+fp16",
+    ":qp8-f32-qc4w-gemm_arch=armv8.2-a+sve+sve2",
     ":qp8-f32-qc8w-gemm_arch=armv8.2-a+dotprod",
     ":qp8-f32-qc8w-gemm_arch=armv8.2-a+i8mm+fp16",
     ":qs8-dwconv_arm64",
@@ -1192,6 +1230,9 @@
     ":qs8-vlrelu_arm64",
     ":qs8-vmul_arm64",
     ":qs8-vmulc_arm64",
+    ":qs8-vprelu_arm64",
+    ":qs8-vpreluc_arm64",
+    ":qs8-vrpreluc_arm64",
     ":qu8-dwconv_arm64",
     ":qu8-f32-vcvt_arm64",
     ":qu8-gemm_arch=armv8.2-a+fp16+dotprod",
@@ -1206,6 +1247,9 @@
     ":qu8-vlrelu_arm64",
     ":qu8-vmul_arm64",
     ":qu8-vmulc_arm64",
+    ":qu8-vprelu_arm64",
+    ":qu8-vpreluc_arm64",
+    ":qu8-vrpreluc_arm64",
     ":reference_arm64",
     ":s8-ibilinear_arm64",
     ":s8-maxpool_arm64",
@@ -1220,15 +1264,18 @@
     ":u8-rdminmax_arm64",
     ":u8-rminmax_arm64",
     ":u8-vclamp_arm64",
+    ":x16-pack-lh_arch=armv8.2-a+sve+sve2",
     ":x16-packw_arm64",
     ":x16-transposec_arm64",
     ":x16-x32-packw_arm64",
     ":x24-transposec_arm64",
+    ":x32-pack-lh_arch=armv8.2-a+sve+sve2",
     ":x32-packw_arm64",
     ":x32-transposec_arm64",
     ":x32-unpool_arm64",
     ":x64-transposec_arm64",
     ":x8-lut_arm64",
+    ":x8-pack-lh_arch=armv8.2-a+sve+sve2",
     ":x8-packq_arm64",
     ":x8-packw_arm64",
     ":x8-transposec_arm64",
@@ -1334,6 +1381,9 @@
     ":f32-vtanh_arm64_standalone",
     ":f32-vunary_arm64_standalone",
     ":operators_arm64_standalone",
+    ":pf16-gemm_arch=armv8.2-a+sve+sve2_standalone",
+    ":pf32-gemm_arch=armv8.2-a+sve+sve2_standalone",
+    ":pqs8-qc8w-gemm_arch=armv8.2-a+sve+sve2_standalone",
     ":qd8-f16-qb4w-gemm_arch=armv8.2-a+dotprod+fp16_standalone",
     ":qd8-f16-qb4w-gemm_arch=armv8.2-a+fp16_standalone",
     ":qd8-f16-qb4w-gemm_arch=armv8.2-a+i8mm+fp16_standalone",
@@ -1366,6 +1416,7 @@
     ":qp8-f32-qb4w-gemm_arch=armv8.2-a+i8mm+fp16_standalone",
     ":qp8-f32-qc4w-gemm_arch=armv8.2-a+dotprod_standalone",
     ":qp8-f32-qc4w-gemm_arch=armv8.2-a+i8mm+fp16_standalone",
+    ":qp8-f32-qc4w-gemm_arch=armv8.2-a+sve+sve2_standalone",
     ":qp8-f32-qc8w-gemm_arch=armv8.2-a+dotprod_standalone",
     ":qp8-f32-qc8w-gemm_arch=armv8.2-a+i8mm+fp16_standalone",
     ":qs8-dwconv_arm64_standalone",
@@ -1393,6 +1444,9 @@
     ":qs8-vlrelu_arm64_standalone",
     ":qs8-vmul_arm64_standalone",
     ":qs8-vmulc_arm64_standalone",
+    ":qs8-vprelu_arm64_standalone",
+    ":qs8-vpreluc_arm64_standalone",
+    ":qs8-vrpreluc_arm64_standalone",
     ":qu8-dwconv_arm64_standalone",
     ":qu8-f32-vcvt_arm64_standalone",
     ":qu8-gemm_arch=armv8.2-a+fp16+dotprod_standalone",
@@ -1407,6 +1461,9 @@
     ":qu8-vlrelu_arm64_standalone",
     ":qu8-vmul_arm64_standalone",
     ":qu8-vmulc_arm64_standalone",
+    ":qu8-vprelu_arm64_standalone",
+    ":qu8-vpreluc_arm64_standalone",
+    ":qu8-vrpreluc_arm64_standalone",
     ":reference_arm64_standalone",
     ":s8-ibilinear_arm64_standalone",
     ":s8-maxpool_arm64_standalone",
@@ -1421,15 +1478,18 @@
     ":u8-rdminmax_arm64_standalone",
     ":u8-rminmax_arm64_standalone",
     ":u8-vclamp_arm64_standalone",
+    ":x16-pack-lh_arch=armv8.2-a+sve+sve2_standalone",
     ":x16-packw_arm64_standalone",
     ":x16-transposec_arm64_standalone",
     ":x16-x32-packw_arm64_standalone",
     ":x24-transposec_arm64_standalone",
+    ":x32-pack-lh_arch=armv8.2-a+sve+sve2_standalone",
     ":x32-packw_arm64_standalone",
     ":x32-transposec_arm64_standalone",
     ":x32-unpool_arm64_standalone",
     ":x64-transposec_arm64_standalone",
     ":x8-lut_arm64_standalone",
+    ":x8-pack-lh_arch=armv8.2-a+sve+sve2_standalone",
     ":x8-packq_arm64_standalone",
     ":x8-packw_arm64_standalone",
     ":x8-transposec_arm64_standalone",
@@ -2403,7 +2463,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f16-f32acc-rsum/gen/f16-f32acc-rsum-avx512skx-u64-acc4.c",
+      "src/src/f16-f32acc-rsum/gen/f16-f32acc-rsum-avx512skx-u32-acc2.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -2435,7 +2495,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f16-f32acc-rsum/gen/f16-f32acc-rsum-avx512skx-u64-acc4.c",
+      "src/src/f16-f32acc-rsum/gen/f16-f32acc-rsum-avx512skx-u32-acc2.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -4444,6 +4504,114 @@
     }
   }
 
+  source_set("f32-avgpool_avx-no-avx2-no-f16c-no-fma") {
+    cflags = [
+      "-mavx",
+      "-mno-avx2",
+      "-mno-f16c",
+      "-mno-fma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/f32-avgpool/gen/f32-avgpool-9p-minmax-avx-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("f32-avgpool_avx-no-avx2-no-f16c-no-fma_standalone") {
+    cflags = [
+      "-mavx",
+      "-mno-avx2",
+      "-mno-f16c",
+      "-mno-fma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/f32-avgpool/gen/f32-avgpool-9p-minmax-avx-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("f32-avgpool_avx512f") {
+    cflags = [ "-mavx512f" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/f32-avgpool/gen/f32-avgpool-9p-minmax-avx512f-u16.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("f32-avgpool_avx512f_standalone") {
+    cflags = [ "-mavx512f" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/f32-avgpool/gen/f32-avgpool-9p-minmax-avx512f-u16.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("f32-avgpool_sse2-no-sse3") {
     cflags = [
       "-mno-sse3",
@@ -8719,7 +8887,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-rsum/gen/f32-rsum-avx512f-u64-acc4.c",
+      "src/src/f32-rsum/gen/f32-rsum-avx512f-u32-acc2.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -8742,7 +8910,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-rsum/gen/f32-rsum-avx512f-u64-acc4.c",
+      "src/src/f32-rsum/gen/f32-rsum-avx512f-u32-acc2.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -9039,7 +9207,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vapproxgelu/gen/f32-vapproxgelu-avx512f-rational-12-10-nr.c",
+      "src/src/f32-vapproxgelu/gen/f32-vapproxgelu-avx512f-rational-12-10-div.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -9062,7 +9230,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vapproxgelu/gen/f32-vapproxgelu-avx512f-rational-12-10-nr.c",
+      "src/src/f32-vapproxgelu/gen/f32-vapproxgelu-avx512f-rational-12-10-div.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -9663,7 +9831,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vclamp/gen/f32-vclamp-avx-u16.c",
+      "src/src/f32-vclamp/gen/f32-vclamp-avx.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -9691,7 +9859,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vclamp/gen/f32-vclamp-avx-u16.c",
+      "src/src/f32-vclamp/gen/f32-vclamp-avx.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -9717,7 +9885,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vclamp/gen/f32-vclamp-avx512f-u16.c",
+      "src/src/f32-vclamp/gen/f32-vclamp-avx512f.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -9740,7 +9908,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vclamp/gen/f32-vclamp-avx512f-u16.c",
+      "src/src/f32-vclamp/gen/f32-vclamp-avx512f.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -9761,15 +9929,15 @@
     }
   }
 
-  source_set("f32-vclamp_sse-no-sse2") {
+  source_set("f32-vclamp_sse2-no-sse3") {
     cflags = [
-      "-mno-sse2",
-      "-msse",
+      "-mno-sse3",
+      "-msse2",
     ]
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vclamp/gen/f32-vclamp-sse-u8.c",
+      "src/src/f32-vclamp/gen/f32-vclamp-sse2.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -9787,15 +9955,15 @@
   }
 
   # This is a target that cannot depend on //base.
-  source_set("f32-vclamp_sse-no-sse2_standalone") {
+  source_set("f32-vclamp_sse2-no-sse3_standalone") {
     cflags = [
-      "-mno-sse2",
-      "-msse",
+      "-mno-sse3",
+      "-msse2",
     ]
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vclamp/gen/f32-vclamp-sse-u8.c",
+      "src/src/f32-vclamp/gen/f32-vclamp-sse2.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -9821,7 +9989,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vclamp/gen/f32-vclamp-scalar-u4.c",
+      "src/src/f32-vclamp/gen/f32-vclamp-scalar.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -9844,7 +10012,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vclamp/gen/f32-vclamp-scalar-u4.c",
+      "src/src/f32-vclamp/gen/f32-vclamp-scalar.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -10367,7 +10535,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vcos/gen/f32-vcos-avx512f-rational-5-4-nr.c",
+      "src/src/f32-vcos/gen/f32-vcos-avx512f-rational-5-4-div.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -10390,7 +10558,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vcos/gen/f32-vcos-avx512f-rational-5-4-nr.c",
+      "src/src/f32-vcos/gen/f32-vcos-avx512f-rational-5-4-div.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -11176,7 +11344,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vgelu/gen/f32-vgelu-avx512f-rational-12-10-nr.c",
+      "src/src/f32-vgelu/gen/f32-vgelu-avx512f-rational-12-10-div.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -11199,7 +11367,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vgelu/gen/f32-vgelu-avx512f-rational-12-10-nr.c",
+      "src/src/f32-vgelu/gen/f32-vgelu-avx512f-rational-12-10-div.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -11391,7 +11559,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vhswish/gen/f32-vhswish-avx-u16.c",
+      "src/src/f32-vhswish/gen/f32-vhswish-avx.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -11419,7 +11587,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vhswish/gen/f32-vhswish-avx-u16.c",
+      "src/src/f32-vhswish/gen/f32-vhswish-avx.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -11445,7 +11613,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vhswish/gen/f32-vhswish-avx512f-u16.c",
+      "src/src/f32-vhswish/gen/f32-vhswish-avx512f.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -11468,7 +11636,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vhswish/gen/f32-vhswish-avx512f-u16.c",
+      "src/src/f32-vhswish/gen/f32-vhswish-avx512f.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -11498,7 +11666,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vhswish/gen/f32-vhswish-fma3-u16.c",
+      "src/src/f32-vhswish/gen/f32-vhswish-fma3.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -11525,7 +11693,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vhswish/gen/f32-vhswish-fma3-u16.c",
+      "src/src/f32-vhswish/gen/f32-vhswish-fma3.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -11546,15 +11714,15 @@
     }
   }
 
-  source_set("f32-vhswish_sse-no-sse2") {
+  source_set("f32-vhswish_sse2-no-sse3") {
     cflags = [
-      "-mno-sse2",
-      "-msse",
+      "-mno-sse3",
+      "-msse2",
     ]
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vhswish/gen/f32-vhswish-sse-u8.c",
+      "src/src/f32-vhswish/gen/f32-vhswish-sse2.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -11572,15 +11740,15 @@
   }
 
   # This is a target that cannot depend on //base.
-  source_set("f32-vhswish_sse-no-sse2_standalone") {
+  source_set("f32-vhswish_sse2-no-sse3_standalone") {
     cflags = [
-      "-mno-sse2",
-      "-msse",
+      "-mno-sse3",
+      "-msse2",
     ]
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vhswish/gen/f32-vhswish-sse-u8.c",
+      "src/src/f32-vhswish/gen/f32-vhswish-sse2.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -11606,7 +11774,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vhswish/gen/f32-vhswish-scalar-u4.c",
+      "src/src/f32-vhswish/gen/f32-vhswish-scalar.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -11629,7 +11797,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vhswish/gen/f32-vhswish-scalar-u4.c",
+      "src/src/f32-vhswish/gen/f32-vhswish-scalar.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -12288,12 +12456,175 @@
     }
   }
 
+  source_set("f32-vrelu_avx-no-avx2-no-f16c-no-fma") {
+    cflags = [
+      "-mavx",
+      "-mno-avx2",
+      "-mno-f16c",
+      "-mno-fma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/f32-vrelu/gen/f32-vrelu-avx.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("f32-vrelu_avx-no-avx2-no-f16c-no-fma_standalone") {
+    cflags = [
+      "-mavx",
+      "-mno-avx2",
+      "-mno-f16c",
+      "-mno-fma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/f32-vrelu/gen/f32-vrelu-avx.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("f32-vrelu_avx512f") {
+    cflags = [ "-mavx512f" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/f32-vrelu/gen/f32-vrelu-avx512f.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("f32-vrelu_avx512f_standalone") {
+    cflags = [ "-mavx512f" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/f32-vrelu/gen/f32-vrelu-avx512f.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("f32-vrelu_sse2-no-sse3") {
+    cflags = [
+      "-mno-sse3",
+      "-msse2",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/f32-vrelu/gen/f32-vrelu-sse2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("f32-vrelu_sse2-no-sse3_standalone") {
+    cflags = [
+      "-mno-sse3",
+      "-msse2",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/f32-vrelu/gen/f32-vrelu-sse2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("f32-vrelu_x64") {
     cflags = []
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-scalar-u8.c",
+      "src/src/f32-vrelu/gen/f32-vrelu-scalar.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -12316,7 +12647,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-scalar-u8.c",
+      "src/src/f32-vrelu/gen/f32-vrelu-scalar.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -13301,7 +13632,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vsin/gen/f32-vsin-avx512f-rational-5-4-nr.c",
+      "src/src/f32-vsin/gen/f32-vsin-avx512f-rational-5-4-div.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -13324,7 +13655,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vsin/gen/f32-vsin-avx512f-rational-5-4-nr.c",
+      "src/src/f32-vsin/gen/f32-vsin-avx512f-rational-5-4-div.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -13839,7 +14170,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vtanh/gen/f32-vtanh-avx512f-rational-9-8-nr.c",
+      "src/src/f32-vtanh/gen/f32-vtanh-avx512f-rational-9-8-div.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -13862,7 +14193,7 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vtanh/gen/f32-vtanh-avx512f-rational-9-8-nr.c",
+      "src/src/f32-vtanh/gen/f32-vtanh-avx512f-rational-9-8-div.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -22566,6 +22897,324 @@
     }
   }
 
+  source_set("qs8-vprelu_f16c-fma-avx2") {
+    cflags = [
+      "-mavx2",
+      "-mf16c",
+      "-mfma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vprelu/gen/qs8-vprelu-avx2-u16.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qs8-vprelu_f16c-fma-avx2_standalone") {
+    cflags = [
+      "-mavx2",
+      "-mf16c",
+      "-mfma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vprelu/gen/qs8-vprelu-avx2-u16.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qs8-vprelu_x64") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vprelu/gen/qs8-vprelu-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qs8-vprelu_x64_standalone") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vprelu/gen/qs8-vprelu-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qs8-vpreluc_f16c-fma-avx2") {
+    cflags = [
+      "-mavx2",
+      "-mf16c",
+      "-mfma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vpreluc/gen/qs8-vpreluc-avx2-u16.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qs8-vpreluc_f16c-fma-avx2_standalone") {
+    cflags = [
+      "-mavx2",
+      "-mf16c",
+      "-mfma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vpreluc/gen/qs8-vpreluc-avx2-u16.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qs8-vpreluc_x64") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vpreluc/gen/qs8-vpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qs8-vpreluc_x64_standalone") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vpreluc/gen/qs8-vpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qs8-vrpreluc_f16c-fma-avx2") {
+    cflags = [
+      "-mavx2",
+      "-mf16c",
+      "-mfma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vrpreluc/gen/qs8-vrpreluc-avx2-u16.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qs8-vrpreluc_f16c-fma-avx2_standalone") {
+    cflags = [
+      "-mavx2",
+      "-mf16c",
+      "-mfma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vrpreluc/gen/qs8-vrpreluc-avx2-u16.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qs8-vrpreluc_x64") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vrpreluc/gen/qs8-vrpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qs8-vrpreluc_x64_standalone") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vrpreluc/gen/qs8-vrpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("qu8-dwconv_avx-no-avx2-no-f16c-no-fma") {
     cflags = [
       "-mavx",
@@ -26037,6 +26686,324 @@
     }
   }
 
+  source_set("qu8-vprelu_f16c-fma-avx2") {
+    cflags = [
+      "-mavx2",
+      "-mf16c",
+      "-mfma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vprelu/gen/qu8-vprelu-avx2-u16.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qu8-vprelu_f16c-fma-avx2_standalone") {
+    cflags = [
+      "-mavx2",
+      "-mf16c",
+      "-mfma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vprelu/gen/qu8-vprelu-avx2-u16.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qu8-vprelu_x64") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vprelu/gen/qu8-vprelu-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qu8-vprelu_x64_standalone") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vprelu/gen/qu8-vprelu-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qu8-vpreluc_f16c-fma-avx2") {
+    cflags = [
+      "-mavx2",
+      "-mf16c",
+      "-mfma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vpreluc/gen/qu8-vpreluc-avx2-u16.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qu8-vpreluc_f16c-fma-avx2_standalone") {
+    cflags = [
+      "-mavx2",
+      "-mf16c",
+      "-mfma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vpreluc/gen/qu8-vpreluc-avx2-u16.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qu8-vpreluc_x64") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vpreluc/gen/qu8-vpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qu8-vpreluc_x64_standalone") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vpreluc/gen/qu8-vpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qu8-vrpreluc_f16c-fma-avx2") {
+    cflags = [
+      "-mavx2",
+      "-mf16c",
+      "-mfma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vrpreluc/gen/qu8-vrpreluc-avx2-u16.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qu8-vrpreluc_f16c-fma-avx2_standalone") {
+    cflags = [
+      "-mavx2",
+      "-mf16c",
+      "-mfma",
+    ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vrpreluc/gen/qu8-vrpreluc-avx2-u16.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qu8-vrpreluc_x64") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vrpreluc/gen/qu8-vrpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qu8-vrpreluc_x64_standalone") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vrpreluc/gen/qu8-vrpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("reference_x64") {
     cflags = []
 
@@ -33958,8 +34925,8 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vclamp/gen/f32-vclamp-neon-u16.c",
-      "src/src/f32-vclamp/gen/f32-vclamp-scalar-u4.c",
+      "src/src/f32-vclamp/gen/f32-vclamp-neon.c",
+      "src/src/f32-vclamp/gen/f32-vclamp-scalar.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -33982,8 +34949,8 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vclamp/gen/f32-vclamp-neon-u16.c",
-      "src/src/f32-vclamp/gen/f32-vclamp-scalar-u4.c",
+      "src/src/f32-vclamp/gen/f32-vclamp-neon.c",
+      "src/src/f32-vclamp/gen/f32-vclamp-scalar.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -34329,8 +35296,8 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vhswish/gen/f32-vhswish-neon-u16.c",
-      "src/src/f32-vhswish/gen/f32-vhswish-scalar-u4.c",
+      "src/src/f32-vhswish/gen/f32-vhswish-neon.c",
+      "src/src/f32-vhswish/gen/f32-vhswish-scalar.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -34353,8 +35320,8 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vhswish/gen/f32-vhswish-neon-u16.c",
-      "src/src/f32-vhswish/gen/f32-vhswish-scalar-u4.c",
+      "src/src/f32-vhswish/gen/f32-vhswish-neon.c",
+      "src/src/f32-vhswish/gen/f32-vhswish-scalar.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -34535,7 +35502,8 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-scalar-u8.c",
+      "src/src/f32-vrelu/gen/f32-vrelu-neon.c",
+      "src/src/f32-vrelu/gen/f32-vrelu-scalar.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -34558,7 +35526,8 @@
 
     sources = [
       "src/include/xnnpack.h",
-      "src/src/f32-vrelu/gen/f32-vrelu-scalar-u8.c",
+      "src/src/f32-vrelu/gen/f32-vrelu-neon.c",
+      "src/src/f32-vrelu/gen/f32-vrelu-scalar.c",
     ]
 
     configs -= [ "//build/config/compiler:chromium_code" ]
@@ -35065,6 +36034,157 @@
     }
   }
 
+  source_set("pf16-gemm_arch=armv8.2-a+sve+sve2") {
+    cflags = [ "-march=armv8.2-a+sve+sve2" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/pf16-gemm/pf16-gemm-32x32-minmax-neonsme2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("pf16-gemm_arch=armv8.2-a+sve+sve2_standalone") {
+    cflags = [ "-march=armv8.2-a+sve+sve2" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/pf16-gemm/pf16-gemm-32x32-minmax-neonsme2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("pf32-gemm_arch=armv8.2-a+sve+sve2") {
+    cflags = [ "-march=armv8.2-a+sve+sve2" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/pf32-gemm/pf32-gemm-1x32-minmax-neonsme2.c",
+      "src/src/pf32-gemm/pf32-gemm-32x32-minmax-neonsme2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("pf32-gemm_arch=armv8.2-a+sve+sve2_standalone") {
+    cflags = [ "-march=armv8.2-a+sve+sve2" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/pf32-gemm/pf32-gemm-1x32-minmax-neonsme2.c",
+      "src/src/pf32-gemm/pf32-gemm-32x32-minmax-neonsme2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("pqs8-qc8w-gemm_arch=armv8.2-a+sve+sve2") {
+    cflags = [ "-march=armv8.2-a+sve+sve2" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/pqs8-qc8w-gemm/pqs8-qc8w-gemm-1x32c4-minmax-neonsme2.c",
+      "src/src/pqs8-qc8w-gemm/pqs8-qc8w-gemm-32x32c4-minmax-neonsme2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("pqs8-qc8w-gemm_arch=armv8.2-a+sve+sve2_standalone") {
+    cflags = [ "-march=armv8.2-a+sve+sve2" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/pqs8-qc8w-gemm/pqs8-qc8w-gemm-1x32c4-minmax-neonsme2.c",
+      "src/src/pqs8-qc8w-gemm/pqs8-qc8w-gemm-32x32c4-minmax-neonsme2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("qd8-f16-qb4w-gemm_arch=armv8.2-a+dotprod+fp16") {
     cflags = [ "-march=armv8.2-a+dotprod+fp16" ]
 
@@ -36755,6 +37875,57 @@
     }
   }
 
+  source_set("qp8-f32-qc4w-gemm_arch=armv8.2-a+sve+sve2") {
+    cflags = [ "-march=armv8.2-a+sve+sve2" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qp8-f32-qc4w-gemm/qp8-f32-qc4w-gemm-minmax-1x128c4-neonsme2.c",
+      "src/src/qp8-f32-qc4w-gemm/qp8-f32-qc4w-gemm-minmax-32x128c4-neonsme2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qp8-f32-qc4w-gemm_arch=armv8.2-a+sve+sve2_standalone") {
+    cflags = [ "-march=armv8.2-a+sve+sve2" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qp8-f32-qc4w-gemm/qp8-f32-qc4w-gemm-minmax-1x128c4-neonsme2.c",
+      "src/src/qp8-f32-qc4w-gemm/qp8-f32-qc4w-gemm-minmax-32x128c4-neonsme2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("qp8-f32-qc8w-gemm_arch=armv8.2-a+dotprod") {
     cflags = [ "-march=armv8.2-a+dotprod" ]
 
@@ -38260,6 +39431,153 @@
     }
   }
 
+  source_set("qs8-vprelu_arm64") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vprelu/gen/qs8-vprelu-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qs8-vprelu_arm64_standalone") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vprelu/gen/qs8-vprelu-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qs8-vpreluc_arm64") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vpreluc/gen/qs8-vpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qs8-vpreluc_arm64_standalone") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vpreluc/gen/qs8-vpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qs8-vrpreluc_arm64") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vrpreluc/gen/qs8-vrpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qs8-vrpreluc_arm64_standalone") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qs8-vrpreluc/gen/qs8-vrpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("qu8-dwconv_arm64") {
     cflags = []
 
@@ -39046,6 +40364,153 @@
     }
   }
 
+  source_set("qu8-vprelu_arm64") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vprelu/gen/qu8-vprelu-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qu8-vprelu_arm64_standalone") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vprelu/gen/qu8-vprelu-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qu8-vpreluc_arm64") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vpreluc/gen/qu8-vpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qu8-vpreluc_arm64_standalone") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vpreluc/gen/qu8-vpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
+  source_set("qu8-vrpreluc_arm64") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vrpreluc/gen/qu8-vrpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("qu8-vrpreluc_arm64_standalone") {
+    cflags = []
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/qu8-vrpreluc/gen/qu8-vrpreluc-scalar-u8.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("reference_arm64") {
     cflags = []
 
@@ -39854,6 +41319,55 @@
     }
   }
 
+  source_set("x16-pack-lh_arch=armv8.2-a+sve+sve2") {
+    cflags = [ "-march=armv8.2-a+sve+sve2" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/x16-pack-lh/x16-packlh-neonsme2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("x16-pack-lh_arch=armv8.2-a+sve+sve2_standalone") {
+    cflags = [ "-march=armv8.2-a+sve+sve2" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/x16-pack-lh/x16-packlh-neonsme2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("x16-packw_arm64") {
     cflags = []
 
@@ -40060,6 +41574,55 @@
     }
   }
 
+  source_set("x32-pack-lh_arch=armv8.2-a+sve+sve2") {
+    cflags = [ "-march=armv8.2-a+sve+sve2" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/x32-pack-lh/x32-packlh-neonsme2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("x32-pack-lh_arch=armv8.2-a+sve+sve2_standalone") {
+    cflags = [ "-march=armv8.2-a+sve+sve2" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/x32-pack-lh/x32-packlh-neonsme2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("x32-packw_arm64") {
     cflags = []
 
@@ -40333,6 +41896,55 @@
     }
   }
 
+  source_set("x8-pack-lh_arch=armv8.2-a+sve+sve2") {
+    cflags = [ "-march=armv8.2-a+sve+sve2" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/x8-pack-lh/x8--packlh-neonsme2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+  }
+
+  # This is a target that cannot depend on //base.
+  source_set("x8-pack-lh_arch=armv8.2-a+sve+sve2_standalone") {
+    cflags = [ "-march=armv8.2-a+sve+sve2" ]
+
+    sources = [
+      "src/include/xnnpack.h",
+      "src/src/x8-pack-lh/x8--packlh-neonsme2.c",
+    ]
+
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+    configs += [ "//build/config/sanitizers:cfi_icall_generalize_pointers" ]
+
+    deps = [
+      "//third_party/cpuinfo",
+      "//third_party/fp16",
+      "//third_party/fxdiv",
+      "//third_party/pthreadpool:pthreadpool_standalone",
+    ]
+
+    public_configs = [ ":xnnpack_config" ]
+
+    if (!(is_android && use_order_profiling)) {
+      assert_no_deps = [ "//base" ]
+    }
+  }
+
   source_set("x8-packq_arm64") {
     cflags = []
 
diff --git a/third_party/xnnpack/README.chromium b/third_party/xnnpack/README.chromium
index 6c543e9..daa3c43 100644
--- a/third_party/xnnpack/README.chromium
+++ b/third_party/xnnpack/README.chromium
@@ -1,8 +1,8 @@
 Name: XNNPACK
 Short Name: xnnpack
 URL: https://github.com/google/xnnpack
-Version: 05eec89fee573a08d841fbaf11db0357586fc6ae
-Date: 2025-04-28
+Version: 1b2beba83092bed68775b6e2433596627988c74b
+Date: 2025-05-07
 License: BSD-3-Clause
 License File: src/LICENSE
 Security Critical: Yes
diff --git a/third_party/xnnpack/build_identifier.c b/third_party/xnnpack/build_identifier.c
index 05fb2b9..58a24d3 100644
--- a/third_party/xnnpack/build_identifier.c
+++ b/third_party/xnnpack/build_identifier.c
@@ -34,7 +34,7 @@
 // - external/xnnpack+/src/f16-f32acc-igemm/gen/f16-f32acc-igemm-4x16-minmax-avx2-broadcast.c
 // - external/xnnpack+/src/f16-f32acc-rdsum/gen/f16-f32acc-rdsum-7p7x-avx512skx-c64.c
 // - external/xnnpack+/src/f16-f32acc-rdsum/gen/f16-f32acc-rdsum-7p7x-f16c-c32.c
-// - external/xnnpack+/src/f16-f32acc-rsum/gen/f16-f32acc-rsum-avx512skx-u64-acc4.c
+// - external/xnnpack+/src/f16-f32acc-rsum/gen/f16-f32acc-rsum-avx512skx-u32-acc2.c
 // - external/xnnpack+/src/f16-f32acc-rsum/gen/f16-f32acc-rsum-f16c-u32-acc4.c
 // - external/xnnpack+/src/f16-ibilinear/gen/f16-ibilinear-fma3-c8.c
 // - external/xnnpack+/src/f16-maxpool/gen/f16-maxpool-9p-minmax-avx2-u16.c
@@ -116,6 +116,8 @@
 // - external/xnnpack+/src/f16-vunary/gen/f16-vsqr-f16c-u16.c
 // - external/xnnpack+/src/f32-argmaxpool/f32-argmaxpool-9p8x-scalar-c1.c
 // - external/xnnpack+/src/f32-argmaxpool/f32-argmaxpool-9p8x-sse2-c4.c
+// - external/xnnpack+/src/f32-avgpool/gen/f32-avgpool-9p-minmax-avx-u8.c
+// - external/xnnpack+/src/f32-avgpool/gen/f32-avgpool-9p-minmax-avx512f-u16.c
 // - external/xnnpack+/src/f32-avgpool/gen/f32-avgpool-9p-minmax-scalar-u1.c
 // - external/xnnpack+/src/f32-avgpool/gen/f32-avgpool-9p-minmax-sse2-u4.c
 // - external/xnnpack+/src/f32-conv-hwc2chw/f32-conv-hwc2chw-3x3s2p1c3x4-scalar-1x1.c
@@ -304,7 +306,7 @@
 // - external/xnnpack+/src/f32-rminmax/gen/f32-rminmax-scalar-u4-acc4.c
 // - external/xnnpack+/src/f32-rminmax/gen/f32-rminmax-sse-u16-acc4.c
 // - external/xnnpack+/src/f32-rsum/gen/f32-rsum-avx-u32-acc4.c
-// - external/xnnpack+/src/f32-rsum/gen/f32-rsum-avx512f-u64-acc4.c
+// - external/xnnpack+/src/f32-rsum/gen/f32-rsum-avx512f-u32-acc2.c
 // - external/xnnpack+/src/f32-rsum/gen/f32-rsum-scalar-u4-acc4.c
 // - external/xnnpack+/src/f32-rsum/gen/f32-rsum-sse-u16-acc4.c
 // - external/xnnpack+/src/f32-spmm/gen/f32-spmm-32x1-minmax-sse.c
@@ -312,7 +314,7 @@
 // - external/xnnpack+/src/f32-spmm/gen/f32-spmm-8x2-minmax-scalar.c
 // - external/xnnpack+/src/f32-spmm/gen/f32-spmm-8x4-minmax-scalar.c
 // - external/xnnpack+/src/f32-vapproxgelu/gen/f32-vapproxgelu-avx-rational-12-10-div.c
-// - external/xnnpack+/src/f32-vapproxgelu/gen/f32-vapproxgelu-avx512f-rational-12-10-nr.c
+// - external/xnnpack+/src/f32-vapproxgelu/gen/f32-vapproxgelu-avx512f-rational-12-10-div.c
 // - external/xnnpack+/src/f32-vapproxgelu/gen/f32-vapproxgelu-fma3-rational-12-10-div.c
 // - external/xnnpack+/src/f32-vapproxgelu/gen/f32-vapproxgelu-scalar-rational-12-10-div.c
 // - external/xnnpack+/src/f32-vapproxgelu/gen/f32-vapproxgelu-sse2-rational-12-10-div.c
@@ -392,10 +394,10 @@
 // - external/xnnpack+/src/f32-vbinary/gen/f32-vsubc-avx512f-u32.c
 // - external/xnnpack+/src/f32-vbinary/gen/f32-vsubc-scalar-u8.c
 // - external/xnnpack+/src/f32-vbinary/gen/f32-vsubc-sse-u8.c
-// - external/xnnpack+/src/f32-vclamp/gen/f32-vclamp-avx-u16.c
-// - external/xnnpack+/src/f32-vclamp/gen/f32-vclamp-avx512f-u16.c
-// - external/xnnpack+/src/f32-vclamp/gen/f32-vclamp-scalar-u4.c
-// - external/xnnpack+/src/f32-vclamp/gen/f32-vclamp-sse-u8.c
+// - external/xnnpack+/src/f32-vclamp/gen/f32-vclamp-avx.c
+// - external/xnnpack+/src/f32-vclamp/gen/f32-vclamp-avx512f.c
+// - external/xnnpack+/src/f32-vclamp/gen/f32-vclamp-scalar.c
+// - external/xnnpack+/src/f32-vclamp/gen/f32-vclamp-sse2.c
 // - external/xnnpack+/src/f32-vcmul/gen/f32-vcmul-avx512f-u32.c
 // - external/xnnpack+/src/f32-vcmul/gen/f32-vcmul-fma3-u16.c
 // - external/xnnpack+/src/f32-vcmul/gen/f32-vcmul-scalar-u4.c
@@ -413,7 +415,7 @@
 // - external/xnnpack+/src/f32-vcopysign/gen/f32-vrcopysignc-scalar.c
 // - external/xnnpack+/src/f32-vcopysign/gen/f32-vrcopysignc-sse2.c
 // - external/xnnpack+/src/f32-vcos/gen/f32-vcos-avx-rational-5-4-div.c
-// - external/xnnpack+/src/f32-vcos/gen/f32-vcos-avx512f-rational-5-4-nr.c
+// - external/xnnpack+/src/f32-vcos/gen/f32-vcos-avx512f-rational-5-4-div.c
 // - external/xnnpack+/src/f32-vcos/gen/f32-vcos-fma3-rational-5-4-div.c
 // - external/xnnpack+/src/f32-vcos/gen/f32-vcos-scalar-rational-5-4-div.c
 // - external/xnnpack+/src/f32-vcos/gen/f32-vcos-sse2-rational-5-4-div.c
@@ -429,15 +431,15 @@
 // - external/xnnpack+/src/f32-vexp/gen/f32-vexp-scalar-rational-3-2-div.c
 // - external/xnnpack+/src/f32-vexp/gen/f32-vexp-sse2-rational-3-2-div.c
 // - external/xnnpack+/src/f32-vgelu/gen/f32-vgelu-avx-rational-12-10-div.c
-// - external/xnnpack+/src/f32-vgelu/gen/f32-vgelu-avx512f-rational-12-10-nr.c
+// - external/xnnpack+/src/f32-vgelu/gen/f32-vgelu-avx512f-rational-12-10-div.c
 // - external/xnnpack+/src/f32-vgelu/gen/f32-vgelu-fma3-rational-12-10-div.c
 // - external/xnnpack+/src/f32-vgelu/gen/f32-vgelu-scalar-rational-12-10-div.c
 // - external/xnnpack+/src/f32-vgelu/gen/f32-vgelu-sse2-rational-12-10-div.c
-// - external/xnnpack+/src/f32-vhswish/gen/f32-vhswish-avx-u16.c
-// - external/xnnpack+/src/f32-vhswish/gen/f32-vhswish-avx512f-u16.c
-// - external/xnnpack+/src/f32-vhswish/gen/f32-vhswish-fma3-u16.c
-// - external/xnnpack+/src/f32-vhswish/gen/f32-vhswish-scalar-u4.c
-// - external/xnnpack+/src/f32-vhswish/gen/f32-vhswish-sse-u8.c
+// - external/xnnpack+/src/f32-vhswish/gen/f32-vhswish-avx.c
+// - external/xnnpack+/src/f32-vhswish/gen/f32-vhswish-avx512f.c
+// - external/xnnpack+/src/f32-vhswish/gen/f32-vhswish-fma3.c
+// - external/xnnpack+/src/f32-vhswish/gen/f32-vhswish-scalar.c
+// - external/xnnpack+/src/f32-vhswish/gen/f32-vhswish-sse2.c
 // - external/xnnpack+/src/f32-vlog/gen/f32-vlog-avx2-rational-3-3-div.c
 // - external/xnnpack+/src/f32-vlog/gen/f32-vlog-avx512f-rational-3-3-div.c
 // - external/xnnpack+/src/f32-vlog/gen/f32-vlog-fma3-rational-3-3-div.c
@@ -450,7 +452,10 @@
 // - external/xnnpack+/src/f32-vlrelu/gen/f32-vlrelu-sse41-u8.c
 // - external/xnnpack+/src/f32-vmulcaddc/gen/f32-vmulcaddc-c1-minmax-scalar-2x.c
 // - external/xnnpack+/src/f32-vmulcaddc/gen/f32-vmulcaddc-c4-minmax-sse-2x.c
-// - external/xnnpack+/src/f32-vrelu/gen/f32-vrelu-scalar-u8.c
+// - external/xnnpack+/src/f32-vrelu/gen/f32-vrelu-avx.c
+// - external/xnnpack+/src/f32-vrelu/gen/f32-vrelu-avx512f.c
+// - external/xnnpack+/src/f32-vrelu/gen/f32-vrelu-scalar.c
+// - external/xnnpack+/src/f32-vrelu/gen/f32-vrelu-sse2.c
 // - external/xnnpack+/src/f32-vrnd/gen/f32-vrndd-avx-u16.c
 // - external/xnnpack+/src/f32-vrnd/gen/f32-vrndd-avx512f-u16.c
 // - external/xnnpack+/src/f32-vrnd/gen/f32-vrndd-scalar-libm-u1.c
@@ -488,7 +493,7 @@
 // - external/xnnpack+/src/f32-vsigmoid/gen/f32-vsigmoid-sse2-rr2-lut64-p2-div-u8.c
 // - external/xnnpack+/src/f32-vsigmoid/gen/f32-vsigmoid-sse41-rr2-lut64-p2-div-u8.c
 // - external/xnnpack+/src/f32-vsin/gen/f32-vsin-avx-rational-5-4-div.c
-// - external/xnnpack+/src/f32-vsin/gen/f32-vsin-avx512f-rational-5-4-nr.c
+// - external/xnnpack+/src/f32-vsin/gen/f32-vsin-avx512f-rational-5-4-div.c
 // - external/xnnpack+/src/f32-vsin/gen/f32-vsin-fma3-rational-5-4-div.c
 // - external/xnnpack+/src/f32-vsin/gen/f32-vsin-scalar-rational-5-4-div.c
 // - external/xnnpack+/src/f32-vsin/gen/f32-vsin-sse2-rational-5-4-div.c
@@ -498,7 +503,7 @@
 // - external/xnnpack+/src/f32-vsqrt/gen/f32-vsqrt-scalar-sqrt-u1.c
 // - external/xnnpack+/src/f32-vsqrt/gen/f32-vsqrt-sse-rsqrt-u12.c
 // - external/xnnpack+/src/f32-vtanh/gen/f32-vtanh-avx-rational-9-8-div.c
-// - external/xnnpack+/src/f32-vtanh/gen/f32-vtanh-avx512f-rational-9-8-nr.c
+// - external/xnnpack+/src/f32-vtanh/gen/f32-vtanh-avx512f-rational-9-8-div.c
 // - external/xnnpack+/src/f32-vtanh/gen/f32-vtanh-fma3-rational-9-8-div.c
 // - external/xnnpack+/src/f32-vtanh/gen/f32-vtanh-scalar-rational-9-8-div.c
 // - external/xnnpack+/src/f32-vtanh/gen/f32-vtanh-sse2-rational-9-8-div.c
@@ -778,6 +783,12 @@
 // - external/xnnpack+/src/qs8-vmulc/gen/qs8-vmulc-minmax-fp32-scalar-u4.c
 // - external/xnnpack+/src/qs8-vmulc/gen/qs8-vmulc-minmax-fp32-sse2-mul16-ld64-u8.c
 // - external/xnnpack+/src/qs8-vmulc/gen/qs8-vmulc-minmax-fp32-sse41-mul16-ld64-u16.c
+// - external/xnnpack+/src/qs8-vprelu/gen/qs8-vprelu-avx2-u16.c
+// - external/xnnpack+/src/qs8-vprelu/gen/qs8-vprelu-scalar-u8.c
+// - external/xnnpack+/src/qs8-vpreluc/gen/qs8-vpreluc-avx2-u16.c
+// - external/xnnpack+/src/qs8-vpreluc/gen/qs8-vpreluc-scalar-u8.c
+// - external/xnnpack+/src/qs8-vrpreluc/gen/qs8-vrpreluc-avx2-u16.c
+// - external/xnnpack+/src/qs8-vrpreluc/gen/qs8-vrpreluc-scalar-u8.c
 // - external/xnnpack+/src/qu8-dwconv/gen/qu8-dwconv-25p16c-minmax-fp32-avx-mul16.c
 // - external/xnnpack+/src/qu8-dwconv/gen/qu8-dwconv-25p16c-minmax-fp32-avx2-mul32.c
 // - external/xnnpack+/src/qu8-dwconv/gen/qu8-dwconv-25p1c-minmax-fp32-scalar-fmagic.c
@@ -870,6 +881,12 @@
 // - external/xnnpack+/src/qu8-vmulc/gen/qu8-vmulc-minmax-fp32-scalar-u4.c
 // - external/xnnpack+/src/qu8-vmulc/gen/qu8-vmulc-minmax-fp32-sse2-mul16-ld64-u8.c
 // - external/xnnpack+/src/qu8-vmulc/gen/qu8-vmulc-minmax-fp32-sse41-mul16-ld64-u16.c
+// - external/xnnpack+/src/qu8-vprelu/gen/qu8-vprelu-avx2-u16.c
+// - external/xnnpack+/src/qu8-vprelu/gen/qu8-vprelu-scalar-u8.c
+// - external/xnnpack+/src/qu8-vpreluc/gen/qu8-vpreluc-avx2-u16.c
+// - external/xnnpack+/src/qu8-vpreluc/gen/qu8-vpreluc-scalar-u8.c
+// - external/xnnpack+/src/qu8-vrpreluc/gen/qu8-vrpreluc-avx2-u16.c
+// - external/xnnpack+/src/qu8-vrpreluc/gen/qu8-vrpreluc-scalar-u8.c
 // - external/xnnpack+/src/reference/packing.cc
 // - external/xnnpack+/src/s8-ibilinear/gen/s8-ibilinear-scalar-c1.c
 // - external/xnnpack+/src/s8-ibilinear/gen/s8-ibilinear-sse2-c8.c
@@ -980,10 +997,10 @@
 #include <string.h>
 
 static const uint8_t xnn_build_identifier[] = {
-  130,  58, 183,  43, 114, 118, 244,  22,
-  152,  70, 241, 236, 226, 135,  69,  64,
-  197, 128,   0, 148,  83,  67, 102,  82,
-   19, 124,  87,  75, 153, 251,  85, 233
+  111, 182, 175,  28,  10,  59, 117, 199,
+  161,   4,  44,  68,  47, 182,   3, 207,
+  101, 198,  29, 180, 128, 137, 252,  57,
+   60,  17,  99, 112, 242, 192, 175, 208
 };
 
 size_t xnn_experimental_get_build_identifier_size() {
diff --git a/third_party/xnnpack/src b/third_party/xnnpack/src
index 05eec89..1b2beba 160000
--- a/third_party/xnnpack/src
+++ b/third_party/xnnpack/src
@@ -1 +1 @@
-Subproject commit 05eec89fee573a08d841fbaf11db0357586fc6ae
+Subproject commit 1b2beba83092bed68775b6e2433596627988c74b
diff --git a/tools/android/nullaway/null_mark.py b/tools/android/nullaway/null_mark.py
index a998c2e..0b677e2a 100755
--- a/tools/android/nullaway/null_mark.py
+++ b/tools/android/nullaway/null_mark.py
@@ -9,7 +9,7 @@
 import re
 import subprocess
 
-_SRC_ROOT = (pathlib.Path(__file__).parents[3])
+_SRC_ROOT = pathlib.Path(__file__).parents[3]
 
 _GOOGLE_JAVA_FORMAT = (_SRC_ROOT / 'third_party' / 'google-java-format' /
                        'google-java-format')
diff --git a/tools/android/nullaway/run_annotator_on_chrome_java.py b/tools/android/nullaway/run_annotator_on_chrome_java.py
new file mode 100755
index 0000000..ee7e4e1
--- /dev/null
+++ b/tools/android/nullaway/run_annotator_on_chrome_java.py
@@ -0,0 +1,199 @@
+#!/usr/bin/env python3
+# 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 argparse
+import logging
+import os
+import pathlib
+import re
+import shlex
+import shutil
+import subprocess
+import sys
+
+_SRC_ROOT = pathlib.Path(__file__).parents[3]
+sys.path.insert(1, str(_SRC_ROOT / 'build/android/gyp'))
+
+from util import build_utils
+import action_helpers
+
+_ANNOTATOR_JAR = ('../NullAwayAnnotator/annotator-core/build/libs/'
+                  'annotator-core-1.3.16-SNAPSHOT.jar')
+_CHROME_JAVA_TURBINE_JAR = 'obj/chrome/android/chrome_java.turbine.jar'
+
+
+def _read_file(path):
+    return pathlib.Path(path).read_text()
+
+
+def _write_file(path, data):
+    return pathlib.Path(path).write_text(data)
+
+
+def _find_unmarked(java_files):
+    ret = set()
+    for path in java_files:
+        data = _read_file(path)
+        if '@NullUnmarked' in data or '@SuppressWarnings("NullAway' in data:
+            ret.add(path)
+    return sorted(ret)
+
+
+def _read_build_config_value(path, key):
+    value = build_utils.ExpandFileArgs([f'@FileArg({path}:{key})'])[0]
+    return action_helpers.parse_gn_list(value)
+
+
+def main():
+    logging.basicConfig(format='%(message)s', level=logging.INFO)
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--loud',
+                        action='store_true',
+                        help='Print compiler while annotating output')
+    args = parser.parse_args()
+
+    if not os.path.exists('args.gn'):
+        parser.error('Must be run from within output directory.')
+    if not os.path.exists(_ANNOTATOR_JAR):
+        parser.error('Annotator .jar not found. Follow steps to build it.')
+    if not os.path.exists(_CHROME_JAVA_TURBINE_JAR):
+        parser.error('Run "autoninja chrome/android:chrome_java" first.')
+
+    # Compile only files that are @NullMarked (to speed things up).
+    java_files = _read_file(
+        'gen/chrome/android/chrome_java.sources').splitlines()
+    java_files = [p for p in java_files if '@NullMarked' in _read_file(p)]
+    sources_path = 'null-away-chrome-java-sources.txt'
+    _write_file(sources_path, '\n'.join(java_files))
+    logging.info(
+        'Running annotator over %d @NullMarked files within chrome_java',
+        len(java_files))
+    logging.info('This will probably take 3-5 minutes 🐢🐢🐢')
+
+    classpath = [
+        'obj/third_party/android_sdk/android_sdk_java.ijar.jar',
+        _CHROME_JAVA_TURBINE_JAR,
+    ]
+    classpath += _read_build_config_value(
+        'gen/chrome/android/chrome_java.build_config.json',
+        'javac_full_interface_classpath')
+
+    processor_path = _read_build_config_value(
+        'gen/tools/android/errorprone_plugin/errorprone_plugin.build_config.json',
+        'classpath')
+    processor_path.append(_ANNOTATOR_JAR)
+
+    contract_annotations = [
+        'org.chromium.build.annotations.Contract',
+        'org.chromium.support_lib_boundary.util.Contract',
+    ]
+    init_methods = [
+        'android.app.Application.onCreate',
+        'android.app.Activity.onCreate',
+        'android.app.Service.onCreate',
+        'android.app.backup.BackupAgent.onCreate',
+        'android.content.ContentProvider.attachInfo',
+        'android.content.ContentProvider.onCreate',
+        'android.content.ContextWrapper.attachBaseContext',
+        'androidx.preference.PreferenceFragmentCompat.onCreatePreferences',
+    ]
+    errorprone_args = [
+        '-Xplugin:ErrorProne',
+        '-XepDisableAllChecks',
+        '-Xep:NullAway:ERROR',
+        '-Xep:AnnotatorScanner:ERROR',
+        '-XepOpt:NullAway:OnlyNullMarked',
+        '-XepOpt:NullAway:CustomContractAnnotations=' +
+        ','.join(contract_annotations),
+        '-XepOpt:NullAway:CastToNonNullMethod=org.chromium.build.NullUtil.assumeNonNull',
+        '-XepOpt:NullAway:AssertsEnabled=true',
+        '-XepOpt:NullAway:AcknowledgeRestrictiveAnnotations=true',
+        '-XepOpt:Nullaway:AcknowledgeAndroidRecent=true',
+        '-XepOpt:NullAway:JSpecifyMode=true',
+        '-XepOpt:NullAway:KnownInitializers=' + ','.join(init_methods),
+        '-XepOpt:AnnotatorScanner:ConfigPath=../nullaway_scanner.xml',
+        '-XepOpt:NullAway:SerializeFixMetadata=true',
+        '-XepOpt:NullAway:FixSerializationConfigPath=../nullaway_config.xml',
+    ]
+    javac_cmd = [
+        '../../third_party/jdk/current/bin/javac', '-g', '-parameters',
+        '--release', '17', '-encoding', 'UTF-8', '-sourcepath', ':',
+        '-Xlint:-dep-ann', '-Xlint:-removal', '-J-XX:+PerfDisableSharedMem',
+        '-Xmaxerrs', '100000', '-Xmaxwarns', '100000',
+        '-J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED',
+        '-J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED',
+        '-J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED',
+        '-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED',
+        '-J--add-exports=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED',
+        '-J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED',
+        '-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED',
+        '-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED',
+        '-J--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED',
+        '-J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED',
+        ' '.join(errorprone_args), '-XDcompilePolicy=simple',
+        '-XDshould-stop.ifError=FLOW', '-XDshould-stop.ifNoError=FLOW',
+        '-processorpath', ':'.join(processor_path), '-d',
+        'nullaway-annotator-output', '-classpath', ':'.join(classpath),
+        f'@{sources_path}'
+    ]
+
+    outdir = os.path.abspath('annotator-out')
+    if os.path.exists(outdir):
+        shutil.rmtree(outdir)
+
+    compile_script = f'nullaway-annotator-compile.sh'
+    compile_logs = f'nullaway-annotator-compile.log'
+    if os.path.exists(compile_logs):
+        os.unlink(compile_logs)
+
+    _write_file(
+        compile_script, f"""\
+#!/bin/bash
+set -e
+echo -e "\n\n============= START OF COMPILE =============" >> {compile_logs}
+{shlex.join(javac_cmd)} 2>&1 | tee -a {compile_logs}
+""")
+    os.chmod(compile_script, 0o744)
+
+    cmd = build_utils.JavaCmd() + [
+        '-jar', _ANNOTATOR_JAR, '-bc', f'./{compile_script}', '-n',
+        'org.chromium.build.annotations.Nullable', '-d', outdir, '-cp',
+        '../nullaway_config.tsv', '-cn', 'NULLAWAY', '-sre',
+        'org.chromium.build.annotations.NullUnmarked', '-i',
+        'org.chromium.build.annotations.Initializer'
+    ]
+    if args.loud:
+        cmd += ['--redirect-build-output-stderr']
+
+    result = subprocess.run(cmd).returncode
+    logging.warning('🪵 Error Prone output (find warnings here):\n%s',
+                    os.path.abspath(compile_logs))
+
+    if result:
+        print('😰 Command failed.')
+        sys.exit(result)
+
+    unmarked_files = _find_unmarked(java_files)
+
+    if unmarked_files:
+        for path in unmarked_files:
+            data = _read_file(path)
+            # Leave a whitespace change as a hint it was nullunmarked.
+            data = data.replace('@NullUnmarked ', '\t')
+            data = data.replace('@SuppressWarnings("NullAway.Init")', '\t')
+            _write_file(path, data)
+
+        print("""
+The following files have unresolved warnings, which have been marked by \\t \
+characters.
+
+Files:""")
+        print('\n'.join(unmarked_files))
+    else:
+        print('No suppressions remained after auto-annotating.')
+
+
+if __name__ == '__main__':
+    main()
diff --git a/tools/crates/gnrt/lib/condition.rs b/tools/crates/gnrt/lib/condition.rs
index e0fae30..5a417711 100644
--- a/tools/crates/gnrt/lib/condition.rs
+++ b/tools/crates/gnrt/lib/condition.rs
@@ -14,99 +14,91 @@
 /// Representation of a `Condition` associated with a conditional/optional
 /// dependency.
 #[derive(Clone, Debug, Eq, PartialEq)]
-pub enum Condition {
-    /// The condition is always false.  In other words, supported Chromium
-    /// builds never meet this condition.
-    ///
-    /// Example: `#[cfg(target_arch = "powerpc")]`.
-    AlwaysFalse,
-    /// The condition is always true.
-    ///
-    /// Example: `#[cfg(not(target_arch = "powerpc"))]`.
-    AlwaysTrue,
-    /// The conditional dependency applies to a subset of target triples
-    ///
-    /// For example `#[cfg(target_os = "windows")]` translates into
-    /// `Condition::TripleSet(...)`.
-    TripleSet(HashSet<RustTargetTriple>),
-    /// Some of the [conditional
-    /// compilation](https://doc.rust-lang.org/reference/conditional-compilation.html) directives
-    /// weren't recognized by `gnrt`.
-    ///
-    /// The `String` is an error message.
-    ///
-    /// In some cases such terms will "disappear" - e.g. `unknown_cfg &&
-    /// always_false` is the same as `always_false`.  When these terms do
-    /// not disappear, then it may mean that supporting a new crate would
-    /// require teaching `gnrt` about the new kinds of configuration.
-    Unsupported(String),
-}
+pub struct Condition(Result<HashSet<RustTargetTriple>, String>);
 
 impl Condition {
     pub fn is_always_false(&self) -> bool {
-        *self == Condition::AlwaysFalse
+        self.0.as_ref().is_ok_and(|triple_set| triple_set.is_empty())
+    }
+
+    /// Creates a `Condition` that is never met - target platforms supported in
+    /// Chromium builds never meet this condition.  For example
+    /// `#[cfg(target_arch = "powerpc")]` is effectively equivalent to
+    /// `Condition::always_false()`.
+    pub fn always_false() -> Self {
+        Condition(Ok(HashSet::new()))
+    }
+
+    fn is_always_true(&self) -> bool {
+        self.0.as_ref().is_ok_and(|triple_set| *triple_set == *RustTargetTriple::all())
+    }
+
+    /// Creates a `Condition` that is always true - *all* target platforms
+    /// supported in Chromium builds meet this condition.  For example
+    /// `#[cfg(not(target_arch = "powerpc"))]` is effectively equivalent to
+    /// `Condition::always_true()`.
+    pub fn always_true() -> Self {
+        Condition(Ok(RustTargetTriple::all().clone()))
     }
 
     fn from_triple(triple: RustTargetTriple) -> Self {
-        Condition::TripleSet([triple].into())
+        Self::from_triple_set([triple].into())
     }
 
     fn from_triple_set(set: HashSet<RustTargetTriple>) -> Self {
-        if set.is_empty() {
-            Condition::AlwaysFalse
-        } else if set == *RustTargetTriple::all() {
-            Condition::AlwaysTrue
-        } else {
-            Condition::TripleSet(set)
-        }
+        Condition(Ok(set))
     }
 
     pub fn or(lhs: Condition, rhs: Condition) -> Self {
+        // First check if one of the operands (potentially an `Err` variant!)
+        // can be ignored (when the other operand `is_always_true`).
+        if lhs.is_always_true() {
+            return lhs;
+        }
+        if rhs.is_always_true() {
+            return rhs;
+        }
         match (lhs, rhs) {
-            (Condition::AlwaysFalse, other) | (other, Condition::AlwaysFalse) => other.clone(),
-            (Condition::AlwaysTrue, _) | (_, Condition::AlwaysTrue) => Condition::AlwaysTrue,
-            (err @ Condition::Unsupported(_), _) | (_, err @ Condition::Unsupported(_)) => {
-                err.clone()
-            }
-            (Condition::TripleSet(lhs), Condition::TripleSet(rhs)) => {
-                Condition::from_triple_set(&lhs | &rhs)
-            }
+            (err @ Condition(Err(_)), _) | (_, err @ Condition(Err(_))) => err.clone(),
+            (Condition(Ok(lhs)), Condition(Ok(rhs))) => Condition::from_triple_set(&lhs | &rhs),
         }
     }
 
     pub fn and(lhs: Condition, rhs: Condition) -> Self {
+        // First check if one of the operands (potentially an `Err` variant!)
+        // can be ignored (when the other operand `is_always_false`).
+        if lhs.is_always_false() {
+            return lhs;
+        }
+        if rhs.is_always_false() {
+            return rhs;
+        }
         match (lhs, rhs) {
-            (Condition::AlwaysFalse, _) | (_, Condition::AlwaysFalse) => Condition::AlwaysFalse,
-            (Condition::AlwaysTrue, other) | (other, Condition::AlwaysTrue) => other,
-            (err @ Condition::Unsupported(_), _) | (_, err @ Condition::Unsupported(_)) => err,
-            (Condition::TripleSet(lhs), Condition::TripleSet(rhs)) => {
-                Condition::from_triple_set(&lhs & &rhs)
-            }
+            (err @ Condition(Err(_)), _) | (_, err @ Condition(Err(_))) => err.clone(),
+            (Condition(Ok(lhs)), Condition(Ok(rhs))) => Condition::from_triple_set(&lhs & &rhs),
         }
     }
 
     fn not(other: Condition) -> Self {
         match other {
-            Condition::AlwaysFalse => Condition::AlwaysTrue,
-            Condition::AlwaysTrue => Condition::AlwaysFalse,
-            err @ Condition::Unsupported(_) => err,
-            Condition::TripleSet(value) => Condition::TripleSet(negate_triple_set(&value)),
+            err @ Condition(Err(_)) => err,
+            Condition(Ok(triple_set)) => Condition(Ok(negate_triple_set(&triple_set))),
         }
     }
 
     pub fn to_handlebars_value(&self) -> Result<Option<String>> {
-        match self {
-            Condition::AlwaysTrue => Ok(None),
-            Condition::TripleSet(set) => Ok(Some(format_as_gn_expr::format(set))),
-            Condition::AlwaysFalse => unreachable!(
-                "AlwaysFalse dependencies should be filtered out \
-                              by `fn collect_dependencies` from `deps.rs`"
-            ),
-            Condition::Unsupported(err) => {
-                Err(anyhow!("{err}")
-                    .context("Failed to translate `#[cfg(...)]` into a GN condition"))
+        assert!(
+            !self.is_always_false(),
+            "'always false' dependencies should be filtered out \
+             by `fn collect_dependencies` from `deps.rs`"
+        );
+        self.0.as_ref().map_err(|msg| anyhow!("{msg}")).map(|triple_set| {
+            if *triple_set == *RustTargetTriple::all() {
+                None
+            } else {
+                Some(format_as_gn_expr::format(triple_set))
             }
-        }
+        })
     }
 
     pub fn from_target_spec(spec: &target_spec::TargetSpec) -> Self {
@@ -313,7 +305,7 @@
             exprs
                 .iter()
                 .map(cfg_expr_to_condition)
-                .fold(Condition::AlwaysTrue, |accumulated, condition| {
+                .fold(Condition::always_true(), |accumulated, condition| {
                     Condition::and(accumulated, condition)
                 })
         }
@@ -324,7 +316,7 @@
             exprs
                 .iter()
                 .map(cfg_expr_to_condition)
-                .fold(Condition::AlwaysFalse, |accumulated, condition| {
+                .fold(Condition::always_false(), |accumulated, condition| {
                     Condition::or(accumulated, condition)
                 })
         }
@@ -364,8 +356,8 @@
 
     if let Some(value_to_triple_set_map) = PROP_NAME_TO_PROP_VALUE_TO_TRIPLE_SET.get(key) {
         match value_to_triple_set_map.get(value) {
-            None => return Condition::AlwaysFalse,
-            Some(set) => return Condition::TripleSet(set.clone()),
+            None => return Condition::always_false(),
+            Some(set) => return Condition::from_triple_set(set.clone()),
         }
     }
 
@@ -376,7 +368,7 @@
     // therefore we treat this as `AlwaysFalse`.  See also
     // https://crbug.com/404598090#comment4.
     log::warn!("Treating unrecogized `#[cfg({key} = \"{value}\")]` as `AlwaysFalse");
-    Condition::AlwaysFalse
+    Condition::always_false()
 }
 
 /// `name` should correspond to https://doc.rust-lang.org/reference/conditional-compilation.html#r-cfg.option-name
@@ -390,24 +382,24 @@
     // We don't support `windows_raw_dylib` in Chromium.  See also
     // https://github.com/rust-lang/rust/issues/58713
     if ["windows_raw_dylib"].contains(&name) {
-        return Condition::AlwaysFalse;
+        return Condition::always_false();
     }
 
     // See https://doc.rust-lang.org/reference/conditional-compilation.html#debug_assertions
     if name == "debug_assertions" {
-        // Returning `AlwaysTrue` is not 100% correct and may bring in unnecessary
+        // Returning "always true" is not 100% correct and may bring in unnecessary
         // dependencies. But this conservative behavior shouldn't cause any
         // major issues.
         //
         // TODO(https://crbug.com/402096443): Handle this by tracking not only a set of
         // `RustTargetTriple` but also a parallel set/bitflag of `RustDebugConfig` (with
         // just two bits - on and off).
-        return Condition::AlwaysTrue;
+        return Condition::always_true();
     }
 
     // See https://doc.rust-lang.org/reference/conditional-compilation.html#test
     if name == "test" {
-        // Returning `AlwaysTrue` is not 100% correct and may bring in unnecessary
+        // Returning "always true" is not 100% correct and may bring in unnecessary
         // dependencies. But this seems unlikely, given that test-only
         // dependencies should be listed in the `[dev-dependencies]` section of
         // `Cargo.toml` and reported as `guppy::DependencyKind::Development`.
@@ -415,7 +407,7 @@
         // issues.
         //
         // TODO(https://crbug.com/402096443): Handle this better.
-        return Condition::AlwaysTrue;
+        return Condition::always_true();
     }
 
     // `name` is not something that is documented in
@@ -424,7 +416,7 @@
     // And therefore we treat this as `AlwaysFalse`.  See also
     // https://crbug.com/404598090#comment4.
     log::warn!("Treating unrecogized `#[cfg({name})]` as `AlwaysFalse");
-    Condition::AlwaysFalse
+    Condition::always_false()
 }
 
 /// `value` should correspond to https://doc.rust-lang.org/reference/conditional-compilation.html#r-cfg.panic.values
@@ -432,19 +424,21 @@
     // `//build/config/compiler/BUILD.gn` always hardcodes `-Cpanic=abort` into
     // `rustflags`.
     match value {
-        "abort" => Condition::AlwaysTrue,
-        "unwind" => Condition::AlwaysFalse,
-        _ => Condition::Unsupported(format!(
+        "abort" => Condition::always_true(),
+        "unwind" => Condition::always_false(),
+        _ => Condition(Err(format!(
             "Unrecognized panic configuration: `#[cfg(panic = \"{value}\")]`"
-        )),
+        ))),
     }
 }
 
 fn triple_to_condition(triple: &str) -> Condition {
-    match triple.parse() {
-        Ok(triple) => Condition::from_triple(triple),
-        Err(_) => Condition::AlwaysFalse,
-    }
+    triple.parse().map(Condition::from_triple).unwrap_or_else(
+        // Triples outside of `//build/rust/known-target-triples.txt` won't parse.
+        // Such target triples are never used in Chromium builds and therefore we
+        // represent tham as "always false".
+        |_err| Condition::always_false(),
+    )
 }
 
 #[cfg(test)]
@@ -470,7 +464,7 @@
         let condition = condition_from_test_expr(expr);
         match condition.to_handlebars_value() {
             Ok(Some(s)) => s,
-            Ok(None) => panic!("Got `AlwaysTrue` / `None` when formatting `{expr}`"),
+            Ok(None) => panic!("Got 'always true' / `None` when formatting `{expr}`"),
             Err(err) => panic!("Error when formatting `{expr}`: `{err}`"),
         }
     }
@@ -605,7 +599,7 @@
                 // Simplification of one of the real expressions below.
                 "all(target_os = \"linux\", target_env = \"\")",
             ),
-            Condition::AlwaysFalse,
+            Condition::always_false(),
         );
         assert_eq!(
             gn_condition_from_test_expr(
@@ -621,4 +615,21 @@
             "(is_linux || is_chromeos) || is_android",
         );
     }
+
+    /// Test that unsupported terms disappear when possible
+    /// (i.e. that we don't needlessly propagate an error).
+    #[test]
+    fn test_err_suppression() {
+        let err = Condition(Err("some err msg".to_string()));
+        assert_eq!(Condition::always_true(), Condition::or(Condition::always_true(), err.clone()),);
+        assert_eq!(Condition::always_true(), Condition::or(err.clone(), Condition::always_true()),);
+        assert_eq!(
+            Condition::always_false(),
+            Condition::and(Condition::always_false(), err.clone()),
+        );
+        assert_eq!(
+            Condition::always_false(),
+            Condition::and(err.clone(), Condition::always_false()),
+        );
+    }
 }
diff --git a/tools/crates/gnrt/lib/deps.rs b/tools/crates/gnrt/lib/deps.rs
index eae2413..7f90722 100644
--- a/tools/crates/gnrt/lib/deps.rs
+++ b/tools/crates/gnrt/lib/deps.rs
@@ -282,7 +282,7 @@
         |link: &PackageLink, dep_kind: DependencyKind| -> Condition {
             let key = get_link_key(link);
             if !cargo_set_links.contains(&key) {
-                return Condition::AlwaysFalse;
+                return Condition::always_false();
             }
             let dep_kind = match dep_kind {
                 DependencyKind::Normal => guppy::DependencyKind::Normal,
@@ -354,7 +354,7 @@
 }
 
 /// Graph traversal resolver that rejects dependency links that would have been
-/// `AlwaysFalse` on Chromium platforms.
+/// `Condition::is_always_false` on Chromium platforms.
 struct PackageResolver<'a> {
     extra_config: &'a BuildConfig,
     memoization_tables: &'a mut MemoizationTables,
@@ -390,7 +390,7 @@
                 ]
             })
             .reduce(Condition::or)
-            .unwrap_or(Condition::AlwaysTrue);
+            .unwrap_or_else(Condition::always_true);
         self.package_conditions.insert(package.into(), condition.clone());
         condition
     }
@@ -402,7 +402,7 @@
     ) -> Condition {
         let req = link.req_for_kind(dep_kind);
         if !req.is_present() {
-            Condition::AlwaysFalse
+            Condition::always_false()
         } else {
             let baseline_condition = self.get_package_condition(&link.from());
             Condition::and(
@@ -457,7 +457,7 @@
     let mut result = HashMap::new();
     let mut insert_if_present = |link: PackageLink, kind: DependencyKind| {
         let condition = condition_getter(&link, kind);
-        if condition != Condition::AlwaysFalse {
+        if !condition.is_always_false() {
             let features = match kind {
                 // ... => `build.rs` deps only care about host-side features.
                 DependencyKind::Build => get_features(cargo_set.host_features()),
@@ -490,13 +490,13 @@
 fn get_condition(platform_status: PlatformStatus) -> Condition {
     use PlatformStatus::*;
     match platform_status {
-        Never => Condition::AlwaysFalse,
-        Always => Condition::AlwaysTrue,
+        Never => Condition::always_false(),
+        Always => Condition::always_true(),
         PlatformDependent { eval } => eval
             .target_specs()
             .iter()
             .map(Condition::from_target_spec)
-            .fold(Condition::AlwaysFalse, Condition::or),
+            .fold(Condition::always_false(), Condition::or),
     }
 }
 
@@ -506,10 +506,8 @@
 ) -> Vec<DepOfDep> {
     package
         .direct_links()
-        .filter_map(|link| match condition_getter(&link) {
-            Condition::AlwaysFalse => None,
-            other_condition => Some((link, other_condition)),
-        })
+        .map(|link| (link, condition_getter(&link)))
+        .filter(|&(_link, ref condition)| !condition.is_always_false())
         .map(|(link, condition)| DepOfDep {
             package_name: link.to().name().to_string(),
             use_name: link.resolved_name().to_string(),
@@ -678,7 +676,7 @@
                 package_name: "bar".to_string(),
                 use_name: "baz".to_string(),
                 version: Version::new(0, 1, 0),
-                condition: Condition::AlwaysTrue,
+                condition: Condition::always_true(),
             }
         );
         assert_eq!(
@@ -687,7 +685,7 @@
                 package_name: "time".to_string(),
                 use_name: "time".to_string(),
                 version: Version::new(0, 3, 14),
-                condition: Condition::AlwaysTrue,
+                condition: Condition::always_true(),
             }
         );
 
@@ -717,7 +715,7 @@
                 package_name: "autocfg".to_string(),
                 use_name: "autocfg".to_string(),
                 version: Version::new(1, 1, 0),
-                condition: Condition::AlwaysTrue,
+                condition: Condition::always_true(),
             }
         );
         assert!(dependencies[i].build_script.as_ref().is_some_and(|path| {
@@ -784,7 +782,7 @@
                 package_name: "serde_derive".to_string(),
                 use_name: "serde_derive".to_string(),
                 version: Version::new(1, 0, 139),
-                condition: Condition::AlwaysTrue,
+                condition: Condition::always_true(),
             }
         );
 
@@ -806,7 +804,7 @@
                 package_name: "proc-macro2".to_string(),
                 use_name: "proc_macro2".to_string(),
                 version: Version::new(1, 0, 40),
-                condition: Condition::AlwaysTrue,
+                condition: Condition::always_true(),
             }
         );
         assert_eq!(
@@ -815,7 +813,7 @@
                 package_name: "quote".to_string(),
                 use_name: "quote".to_string(),
                 version: Version::new(1, 0, 20),
-                condition: Condition::AlwaysTrue,
+                condition: Condition::always_true(),
             }
         );
         assert_eq!(
@@ -824,7 +822,7 @@
                 package_name: "syn".to_string(),
                 use_name: "syn".to_string(),
                 version: Version::new(1, 0, 98),
-                condition: Condition::AlwaysTrue,
+                condition: Condition::always_true(),
             }
         );
 
@@ -845,7 +843,7 @@
                 package_name: "proc-macro2".to_string(),
                 use_name: "proc_macro2".to_string(),
                 version: Version::new(1, 0, 40),
-                condition: Condition::AlwaysTrue,
+                condition: Condition::always_true(),
             }
         );
         assert_eq!(
@@ -854,7 +852,7 @@
                 package_name: "quote".to_string(),
                 use_name: "quote".to_string(),
                 version: Version::new(1, 0, 20),
-                condition: Condition::AlwaysTrue,
+                condition: Condition::always_true(),
             }
         );
         assert_eq!(
@@ -863,7 +861,7 @@
                 package_name: "unicode-ident".to_string(),
                 use_name: "unicode_ident".to_string(),
                 version: Version::new(1, 0, 1),
-                condition: Condition::AlwaysTrue,
+                condition: Condition::always_true(),
             }
         );
 
diff --git a/tools/cygprofile/generate_orderfile.pydeps b/tools/cygprofile/generate_orderfile.pydeps
index 7555de0..08b16e8 100644
--- a/tools/cygprofile/generate_orderfile.pydeps
+++ b/tools/cygprofile/generate_orderfile.pydeps
@@ -54,7 +54,6 @@
 //third_party/catapult/devil/devil/devil_env.py
 //third_party/catapult/devil/devil/utils/__init__.py
 //third_party/catapult/devil/devil/utils/cmd_helper.py
-//third_party/catapult/devil/devil/utils/host_utils.py
 //third_party/catapult/devil/devil/utils/lazy/__init__.py
 //third_party/catapult/devil/devil/utils/lazy/weak_constant.py
 //third_party/catapult/devil/devil/utils/logging_common.py
diff --git a/tools/mb/mb.py b/tools/mb/mb.py
index 47fb735..5f6a92c 100755
--- a/tools/mb/mb.py
+++ b/tools/mb/mb.py
@@ -1709,6 +1709,7 @@
                or 'is_chromeos_device=true' in vals['gn_args'])
     is_cros_device = 'is_chromeos_device=true' in vals['gn_args']
     is_ios = 'target_os="ios"' in vals['gn_args']
+    is_linux = 'target_os="linux"' in vals['gn_args']
     # pylint: disable=consider-using-ternary
     is_mac = ((self.platform == 'darwin' and not is_ios)
               or 'target_os="mac"' in vals['gn_args'])
@@ -1721,8 +1722,10 @@
     # that one Ozone build can be used to run different backends. Currently,
     # tests are executed for the headless and X11 backends and both can run
     # under Xvfb on Linux.
-    use_xvfb = (self.platform.startswith('linux') and not is_android
-                and not is_fuchsia and not is_cros_device)
+    if 'target_os' not in vals['gn_args']:
+      use_xvfb = self.platform.startswith('linux')
+    else:
+      use_xvfb = is_linux or (is_cros and not is_cros_device)
 
     asan = 'is_asan=true' in vals['gn_args']
     lsan = 'is_lsan=true' in vals['gn_args']
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 76e40bed..50f1eba 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -48605,6 +48605,8 @@
   <suffix name="ExtensionsMenu" label="For Extensions menu opening."/>
   <suffix name="ExtensionsRequestAccessButton"
       label="For Extensions request access button feature."/>
+  <suffix name="ExtensionsZeroStatePromo"
+      label="For promoting extensions to users with no extensions."/>
   <suffix name="FeatureNotificationGuideDefaultBrowserNotificationShown"
       label="For feature notification guide default browser notification
              feature. (obsolete)"/>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 6cb8d3fc..ba81a8a9 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -12276,6 +12276,7 @@
   <int value="-668114930" label="WindowsFollowCursor:disabled"/>
   <int value="-667952041" label="CCTRealTimeEngagementSignals:enabled"/>
   <int value="-667517406" label="overscroll-history-navigation"/>
+  <int value="-667213800" label="PinnedTabToastOnClose:disabled"/>
   <int value="-667018797"
       label="OmniboxUIExperimentBlueTitlesAndGrayUrlsOnPageSuggestions:disabled"/>
   <int value="-666712735" label="FeedShare:disabled"/>
@@ -15100,6 +15101,7 @@
   <int value="408190863" label="OmniboxDisableInstantExtendedLimit:disabled"/>
   <int value="408469366"
       label="NtpHistoryClustersModuleSuggestionChipHeader:disabled"/>
+  <int value="409097421" label="EnablePixAccountLinking:enabled"/>
   <int value="409174264" label="camera-super-res-override"/>
   <int value="409566604" label="IntentPickerPWAPersistence:enabled"/>
   <int value="409622437" label="disable-buffer-bw-compression"/>
@@ -15629,6 +15631,7 @@
   <int value="625725485" label="FilesTrash:enabled"/>
   <int value="625916211" label="ConchSystemAudioFromMic:disabled"/>
   <int value="626349796" label="ArcVmMemorySize:disabled"/>
+  <int value="626413969" label="PinnedTabToastOnClose:enabled"/>
   <int value="626662478" label="Canvas2DHibernation:disabled"/>
   <int value="628302973" label="NTPSnippets:enabled"/>
   <int value="628438173" label="AccessibilityPerformanceFiltering:enabled"/>
@@ -19575,6 +19578,7 @@
   <int value="2094335682" label="StreamlinedUsbPrinterSetup:enabled"/>
   <int value="2095250358"
       label="EnableBluetoothSerialPortProfileInSerialApi:disabled"/>
+  <int value="2095569369" label="EnablePixAccountLinking:disabled"/>
   <int value="2095740699" label="OmniboxPedalsBatch3:disabled"/>
   <int value="2096688830" label="RequestDesktopSitePerSiteIph:disabled"/>
   <int value="2096736155" label="BrowsingDataLifetimeManager:enabled"/>
diff --git a/tools/metrics/histograms/metadata/bookmarks/enums.xml b/tools/metrics/histograms/metadata/bookmarks/enums.xml
index b5ba6cd..9247c73 100644
--- a/tools/metrics/histograms/metadata/bookmarks/enums.xml
+++ b/tools/metrics/histograms/metadata/bookmarks/enums.xml
@@ -110,7 +110,6 @@
   <int value="20" label="Help center"/>
   <int value="21" label="Open bookmark (via double-click / enter)"/>
   <int value="22" label="Open folder (via double-click / enter)"/>
-  <int value="23" label="Open in split view"/>
 </enum>
 
 <!-- LINT.IfChange(BookmarkReorderDropTarget) -->
diff --git a/tools/metrics/histograms/metadata/custom_tabs/enums.xml b/tools/metrics/histograms/metadata/custom_tabs/enums.xml
index a6fdaca..87d08f0 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/enums.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/enums.xml
@@ -267,6 +267,25 @@
   <int value="3" label="High-end and Pip"/>
 </enum>
 
+<enum name="TwaLaunchHandlerClientMode">
+  <int value="0" label="Initial intent (client mode is ignored)"/>
+  <int value="1" label="Client mode navigate_existing"/>
+  <int value="2" label="Client mode navigate_new"/>
+  <int value="3" label="Client mode focus_existing"/>
+  <int value="4" label="Client mode auto"/>
+</enum>
+
+<enum name="TwaLaunchHandlerFailureReason">
+  <int value="0" label="Target URL verification failed"/>
+  <int value="1" label="Current page URL verification failed"/>
+</enum>
+
+<enum name="TwaLaunchHandlerFileHandling">
+  <int value="0" label="No files"/>
+  <int value="1" label="Single file"/>
+  <int value="2" label="Multiple files"/>
+</enum>
+
 <enum name="VisibleTab">
   <int value="0" label="Custom Tab"/>
   <int value="1" label="Chrome Tab"/>
diff --git a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
index e224b082..ba9264ec 100644
--- a/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
+++ b/tools/metrics/histograms/metadata/custom_tabs/histograms.xml
@@ -932,6 +932,43 @@
   </token>
 </histogram>
 
+<histogram name="TrustedWebActivity.LaunchHandler.ClientMode"
+    enum="TwaLaunchHandlerClientMode" expires_after="2026-05-01">
+  <owner>tkachenkoo@google.com</owner>
+  <owner>sselyvon@google.com</owner>
+  <owner>cros-web-apps-team@google.com</owner>
+  <summary>
+    Records the client mode of a web app launched through Trusted Web Activity.
+    Client mode is a part of Launch Handler API. Recorded on Trusted Web
+    Activity startup if the Launch Handler API is enabled.
+  </summary>
+</histogram>
+
+<histogram name="TrustedWebActivity.LaunchHandler.FailureReason"
+    enum="TwaLaunchHandlerFailureReason" expires_after="2026-05-01">
+  <owner>tkachenkoo@google.com</owner>
+  <owner>sselyvon@google.com</owner>
+  <owner>cros-web-apps-team@google.com</owner>
+  <summary>
+    Records the verification failure that caused launch queue to not be notified
+    in Trusted Web Activity, if any. Recorded on Trusted Web Activity startup if
+    the Launch Handler API is enabled.
+  </summary>
+</histogram>
+
+<histogram name="TrustedWebActivity.LaunchHandler.FileHandling"
+    enum="TwaLaunchHandlerFileHandling" expires_after="2026-05-01">
+  <owner>tkachenkoo@google.com</owner>
+  <owner>sselyvon@google.com</owner>
+  <owner>cros-web-apps-team@google.com</owner>
+  <summary>
+    Records the usage of file handling API in Trusted Web Activities, as part of
+    Launch Handler API. Recorded on Trusted Web Activity startup if the Launch
+    Handler API is enabled. If file handling is not used, NO_FILES will be
+    reported.
+  </summary>
+</histogram>
+
 <histogram name="TrustedWebActivity.Notification.PermissionRequestResult"
     enum="ContentSetting" expires_after="2025-10-01">
   <owner>peconn@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml
index 88bb75e..71663ca 100644
--- a/tools/metrics/histograms/metadata/enterprise/histograms.xml
+++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -429,6 +429,20 @@
   </summary>
 </histogram>
 
+<histogram name="Enterprise.CertificateStore.LevelDB.InitStatus.{Retry}"
+    units="code" expires_after="2026-02-10">
+  <owner>seblalancette@chromium.org</owner>
+  <owner>cbe-device-trust-eng@google.com</owner>
+  <summary>
+    Captures the initialization status returned after initializing {Retry} a
+    LevelDB database instance used by the managed client certificates store.
+  </summary>
+  <token key="Retry">
+    <variant name="NoRetry" summary="without retry"/>
+    <variant name="WithRetry" summary="with retry"/>
+  </token>
+</histogram>
+
 <histogram
     name="Enterprise.ClientCertificate.{Level}.CreateCertificate.Success.HasCert"
     enum="BooleanSuccess" expires_after="2026-02-10">
diff --git a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
index 9e4d268..9df7665 100644
--- a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
+++ b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
@@ -202,6 +202,10 @@
   <variant name="IPH_ExtensionsMenu" summary="extensions menu opened"/>
   <variant name="IPH_ExtensionsRequestAccessButton"
       summary="extensions request access button shown"/>
+  <variant name="IPH_ExtensionsZeroStatePromo"
+      summary="message shown anchored to the three dots app menu suggesting
+               users with no extensions installed to explore the IPH_Chrome
+               Web Store."/>
   <variant name="IPH_FeatureNotificationGuideDefaultBrowserNotificationShown"
       summary="(obsolete) feature notification guide default browser
                notification"/>
diff --git a/tools/metrics/histograms/metadata/linux/histograms.xml b/tools/metrics/histograms/metadata/linux/histograms.xml
index c8ea6076..5471162 100644
--- a/tools/metrics/histograms/metadata/linux/histograms.xml
+++ b/tools/metrics/histograms/metadata/linux/histograms.xml
@@ -165,7 +165,7 @@
 
 <histogram name="Linux.X11.XInput2" enum="BooleanEnabled"
     expires_after="2025-10-21">
-  <owner>aycyang@chromium.org</owner>
+  <owner>hidehiko@chromium.org</owner>
   <owner>thomasanderson@chromium.org</owner>
   <summary>Whether X Input Extension version 2.x is available.</summary>
 </histogram>
diff --git a/tools/metrics/histograms/metadata/net/histograms.xml b/tools/metrics/histograms/metadata/net/histograms.xml
index 3c2d520..a5a1242b 100644
--- a/tools/metrics/histograms/metadata/net/histograms.xml
+++ b/tools/metrics/histograms/metadata/net/histograms.xml
@@ -632,6 +632,21 @@
   </summary>
 </histogram>
 
+<histogram name="HttpCache.NoVarySearch.EntriesAddedDuringLoading"
+    units="entries" expires_after="2025-12-16">
+  <owner>ricea@chromium.org</owner>
+  <owner>net-dev@chromium.org</owner>
+  <summary>
+    Number of entries that were added to the NoVarySearchCache while the
+    previously persisted cache was being loaded from disk. This is logged once
+    per successful call to NoVarySearchCacheStorage::Load(), which is once per
+    on-the-record NetworkContext.
+
+    This can be considered an approximation of how many requests missed the
+    benefit of the NoVarySearchCache due to the load being slow.
+  </summary>
+</histogram>
+
 <histogram name="HttpCache.NoVarySearch.EntriesLoaded" units="entries"
     expires_after="2025-12-16">
   <owner>ricea@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml
index ee8cad6..a6b1d36 100644
--- a/tools/metrics/histograms/metadata/network/histograms.xml
+++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -5530,6 +5530,30 @@
   </summary>
 </histogram>
 
+<histogram name="NetworkService.IpProtection.{ProxyLayer}.TokenCount.{Event}"
+    units="tokens" expires_after="2025-10-01">
+  <owner>abhipatel@chromium.org</owner>
+  <owner>src/components/ip_protection/OWNERS</owner>
+  <summary>
+    Records the number of IP Protection auth tokens involved in a specific event
+    for {ProxyLayer}. The histogram indicates that {Event}. Allows calculation
+    of tokens lost to other causes (e.g. shutdown) by comparing Requested vs.
+    (Spent + Expired). Recorded when tokens are successfully fetched, spent, or
+    garbage-collected. Expected counts are typically 1 for Spent, and
+    potentially higher for Requested/Expired (up to batch size or cache size).
+  </summary>
+  <token key="ProxyLayer">
+    <variant name="ProxyA"/>
+    <variant name="ProxyB"/>
+  </token>
+  <token key="Event">
+    <variant name="Expired" summary="Token removed from cache due to expiry."/>
+    <variant name="Requested"
+        summary="Tokens successfully fetched and cached."/>
+    <variant name="Spent" summary="Token used for a proxy connection."/>
+  </token>
+</histogram>
+
 <histogram name="NetworkService.IpProtection.{ProxyLayer}.TokenExpirationRate"
     units="tokens" expires_after="2025-10-26">
   <owner>djmitche@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/omnibox/histograms.xml b/tools/metrics/histograms/metadata/omnibox/histograms.xml
index 687d647..5afed18 100644
--- a/tools/metrics/histograms/metadata/omnibox/histograms.xml
+++ b/tools/metrics/histograms/metadata/omnibox/histograms.xml
@@ -2034,6 +2034,22 @@
 </histogram>
 
 <histogram
+    name="Omnibox.SearchPrefetch.DuplicateSearchTermsAgeAheadOfNavigationalPrefetch"
+    units="ms" expires_after="2025-09-14">
+  <owner>lingqi@chromium.org</owner>
+  <owner>nhiroki@chromium.org</owner>
+  <owner>chrome-prerendering@google.com</owner>
+  <summary>
+    Measures the duration between when the search terms are visited and when the
+    same search terms are prefetched by navigational prefetch predictor.
+
+    Recorded at
+    SearchPrefetchService::RecordPotentialDuplicateSearchTermsAheadofNavigationalPrefetch
+    when the same search terms are prefetch by navigational prefetch predictor.
+  </summary>
+</histogram>
+
+<histogram
     name="Omnibox.SearchPrefetch.SearchWhatYouTypedWasAlsoSuggested.{HistoryOrSuggest}"
     enum="Boolean" expires_after="2023-05-02">
   <owner>ryansturm@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/permissions/enums.xml b/tools/metrics/histograms/metadata/permissions/enums.xml
index d2e83330..a598995 100644
--- a/tools/metrics/histograms/metadata/permissions/enums.xml
+++ b/tools/metrics/histograms/metadata/permissions/enums.xml
@@ -177,11 +177,6 @@
 
 <!-- LINT.ThenChange(//components/permissions/permission_request_enums.h:PermissionPredictionSource) -->
 
-<enum name="PermissionPredictionThresholdSource">
-  <int value="0" label="MODEL_METADATA"/>
-  <int value="1" label="HARDCODED_FALLBACK"/>
-</enum>
-
 <enum name="PermissionPromptDismissMethod">
   <int value="0" label="Unspecified"/>
   <int value="1" label="Back navigation"/>
diff --git a/tools/metrics/histograms/metadata/permissions/histograms.xml b/tools/metrics/histograms/metadata/permissions/histograms.xml
index 0a812a8..591f6118 100644
--- a/tools/metrics/histograms/metadata/permissions/histograms.xml
+++ b/tools/metrics/histograms/metadata/permissions/histograms.xml
@@ -949,17 +949,6 @@
   </summary>
 </histogram>
 
-<histogram name="Permissions.PredictionService.PredictionThresholdSource"
-    enum="PermissionPredictionThresholdSource" expires_after="2025-01-05">
-  <owner>ravjit@chromium.org</owner>
-  <owner>src/components/permissions/PERMISSIONS_OWNERS</owner>
-  <summary>
-    Recorded whenever on-device Permissions prediciton service model is
-    executed. Records if the threshold value for the non-grant score is obtained
-    from the model's metadata or if the default fallback was used.
-  </summary>
-</histogram>
-
 <histogram name="Permissions.PredictionService.Response.{PermissionType}"
     enum="BooleanIgnored" expires_after="2025-09-14">
   <owner>ravjit@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
index 765822a..ac19fd0 100644
--- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
+++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -1288,7 +1288,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.FileTypeUpdate.DynamicUpdateVersion"
-    units="FileTypePolicies Version" expires_after="2025-05-18">
+    units="FileTypePolicies Version" expires_after="2026-01-06">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -1322,7 +1322,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.FileTypeUpdate.ResourceBundleResult"
-    enum="SBFileTypeUpdateResult" expires_after="2025-05-18">
+    enum="SBFileTypeUpdateResult" expires_after="2026-01-06">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
@@ -1335,7 +1335,7 @@
 </histogram>
 
 <histogram name="SafeBrowsing.FileTypeUpdate.ResourceBundleVersion"
-    units="FileTypePolicies Version" expires_after="2025-06-22">
+    units="FileTypePolicies Version" expires_after="2026-01-06">
   <owner>drubery@chromium.org</owner>
   <owner>chrome-counter-abuse-alerts@google.com</owner>
   <summary>
diff --git a/tools/metrics/histograms/metadata/security/histograms.xml b/tools/metrics/histograms/metadata/security/histograms.xml
index 2db67cd..5962708 100644
--- a/tools/metrics/histograms/metadata/security/histograms.xml
+++ b/tools/metrics/histograms/metadata/security/histograms.xml
@@ -198,7 +198,7 @@
 </histogram>
 
 <histogram name="Security.GwpAsan.CrashAnalysisResult.{Allocator}"
-    enum="GwpAsanCrashAnalysisResult" expires_after="2025-05-04">
+    enum="GwpAsanCrashAnalysisResult" expires_after="2026-05-04">
   <owner>glazunov@google.com</owner>
   <owner>mpdenton@chromium.org</owner>
   <owner>chrome-memory-tok@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/signin/histograms.xml b/tools/metrics/histograms/metadata/signin/histograms.xml
index 3872829..8efda94c 100644
--- a/tools/metrics/histograms/metadata/signin/histograms.xml
+++ b/tools/metrics/histograms/metadata/signin/histograms.xml
@@ -3059,6 +3059,29 @@
   </summary>
 </histogram>
 
+<histogram name="Signin.SyncOptIn.IdentityPill.DurationBeforeClick" units="ms"
+    expires_after="2026-01-12">
+  <owner>ernn@google.com</owner>
+  <owner>chrome-signin-team@google.com</owner>
+  <summary>
+    Records the time between the history sync opt-in identity pill promo is
+    opened and the user interacts with it (clicks on it). If the user doesn't
+    click the identity pill, the histogram should not be recorded.
+  </summary>
+</histogram>
+
+<histogram name="Signin.SyncOptIn.IdentityPill.Shown" enum="SigninAccessPoint"
+    expires_after="2026-01-12">
+  <owner>ernn@google.com</owner>
+  <owner>chrome-signin-team@google.com</owner>
+  <summary>
+    Counts the number of times the history sync opt-in identity pill was shown.
+
+    Note: This is only recorded for `kHistorySyncOptinExpansionPill*` access
+    points.
+  </summary>
+</histogram>
+
 <histogram name="Signin.SyncOptIn.Offered" enum="SigninAccessPoint"
     expires_after="2026-01-12">
   <owner>ernn@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/toasts/enums.xml b/tools/metrics/histograms/metadata/toasts/enums.xml
index 49c907b..f312f3f 100644
--- a/tools/metrics/histograms/metadata/toasts/enums.xml
+++ b/tools/metrics/histograms/metadata/toasts/enums.xml
@@ -72,6 +72,7 @@
   <int value="13" label="User joined collaboration"/>
   <int value="14" label="Collaboration removed"/>
   <int value="15" label="Video Frame Copied"/>
+  <int value="16" label="Close pinned tab confirmation toast"/>
 </enum>
 
 <!-- LINT.ThenChange(/chrome/browser/ui/toasts/api/toast_id.h:ToastId) -->
diff --git a/tools/perf/core/bot_platforms.py b/tools/perf/core/bot_platforms.py
index b058d2b..d47e3cb 100644
--- a/tools/perf/core/bot_platforms.py
+++ b/tools/perf/core/bot_platforms.py
@@ -516,7 +516,6 @@
 # TODO(crbug.com/338630584): Remove it when other benchmarks can be run on
 # Android.
 _CROSSBENCH_ANDROID = frozenset([
-    _crossbench_speedometer3_0(arguments=['--fileserver']),
     _crossbench_speedometer3(arguments=['--fileserver']),
     _crossbench_loadline_phone(arguments=[
         '--cool-down-threshold=moderate',
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index abf2a42..c3b8b797 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -633,7 +633,7 @@
             'pool': 'chrome',
             'os': 'Android',
             'label-pool': 'chrome.tests.perf',
-            'label-board': 'byra',
+            'label-board': 'brya',
         },
         'server':
         'https://chromeos-swarming.appspot.com',
diff --git a/tools/perf/core/shard_maps/android-pixel6-perf-pgo_map.json b/tools/perf/core/shard_maps/android-pixel6-perf-pgo_map.json
index 555a0e333..42c97928 100644
--- a/tools/perf/core/shard_maps/android-pixel6-perf-pgo_map.json
+++ b/tools/perf/core/shard_maps/android-pixel6-perf-pgo_map.json
@@ -47,7 +47,7 @@
     "2": {
         "benchmarks": {
             "rendering.mobile": {
-                "end": 83,
+                "end": 82,
                 "abridged": false
             },
             "jetstream2": {
@@ -65,8 +65,8 @@
     "3": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 83,
-                "end": 166,
+                "begin": 82,
+                "end": 164,
                 "abridged": false
             },
             "jetstream2": {
@@ -84,8 +84,8 @@
     "4": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 166,
-                "end": 247,
+                "begin": 164,
+                "end": 245,
                 "abridged": false
             },
             "jetstream2": {
@@ -103,8 +103,8 @@
     "5": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 247,
-                "end": 327,
+                "begin": 245,
+                "end": 324,
                 "abridged": false
             },
             "speedometer3": {
@@ -116,7 +116,7 @@
     "6": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 327,
+                "begin": 324,
                 "abridged": false
             },
             "speedometer2": {
@@ -145,12 +145,6 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
-                "display_name": "speedometer3.0.crossbench",
-                "arguments": [
-                    "--fileserver"
-                ]
-            },
             "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": [
@@ -160,18 +154,18 @@
         }
     },
     "extra_infos": {
-        "num_stories": 499,
+        "num_stories": 498,
         "predicted_min_shard_time": 916.0,
         "predicted_min_shard_index": 0,
         "predicted_max_shard_time": 7856.0,
         "predicted_max_shard_index": 1,
         "shard #0": 916.0,
         "shard #1": 7856.0,
-        "shard #2": 2861.0,
-        "shard #3": 2862.0,
-        "shard #4": 2845.0,
-        "shard #5": 2871.0,
-        "shard #6": 2720.0,
-        "shard #7": 3001.0
+        "shard #2": 2836.0,
+        "shard #3": 2841.0,
+        "shard #4": 2847.0,
+        "shard #5": 2839.0,
+        "shard #6": 2796.0,
+        "shard #7": 2941.0
     }
 }
diff --git a/tools/perf/core/shard_maps/android-pixel6-perf_map.json b/tools/perf/core/shard_maps/android-pixel6-perf_map.json
index 2de1f36..489f9ee5 100644
--- a/tools/perf/core/shard_maps/android-pixel6-perf_map.json
+++ b/tools/perf/core/shard_maps/android-pixel6-perf_map.json
@@ -189,7 +189,7 @@
                 "abridged": false
             },
             "rendering.mobile": {
-                "end": 19,
+                "end": 18,
                 "abridged": false
             }
         },
@@ -205,8 +205,8 @@
     "7": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 19,
-                "end": 140,
+                "begin": 18,
+                "end": 139,
                 "abridged": false
             }
         }
@@ -214,8 +214,8 @@
     "8": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 140,
-                "end": 261,
+                "begin": 139,
+                "end": 260,
                 "abridged": false
             }
         }
@@ -223,8 +223,8 @@
     "9": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 261,
-                "end": 366,
+                "begin": 260,
+                "end": 365,
                 "abridged": false
             }
         }
@@ -232,7 +232,7 @@
     "10": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 366,
+                "begin": 365,
                 "abridged": false
             },
             "rendering.mobile.notracing": {
@@ -266,12 +266,6 @@
             }
         },
         "crossbench": {
-            "speedometer_3.0": {
-                "display_name": "speedometer3.0.crossbench",
-                "arguments": [
-                    "--fileserver"
-                ]
-            },
             "speedometer_3": {
                 "display_name": "speedometer3.crossbench",
                 "arguments": [
@@ -328,7 +322,7 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1136,
+        "num_stories": 1135,
         "predicted_min_shard_time": 3580.0,
         "predicted_min_shard_index": 12,
         "predicted_max_shard_time": 7376.0,
@@ -339,11 +333,11 @@
         "shard #3": 7376.0,
         "shard #4": 7376.0,
         "shard #5": 3604.0,
-        "shard #6": 3615.0,
-        "shard #7": 3591.0,
-        "shard #8": 3607.0,
-        "shard #9": 3604.0,
-        "shard #10": 3619.0,
+        "shard #6": 3589.0,
+        "shard #7": 3585.0,
+        "shard #8": 3614.0,
+        "shard #9": 3597.0,
+        "shard #10": 3591.0,
         "shard #11": 3636.0,
         "shard #12": 3580.0,
         "shard #13": 3977.0
diff --git a/tools/perf/process_perf_results.pydeps b/tools/perf/process_perf_results.pydeps
index 21ce947..f38f8f5 100644
--- a/tools/perf/process_perf_results.pydeps
+++ b/tools/perf/process_perf_results.pydeps
@@ -112,7 +112,6 @@
 ../../third_party/catapult/devil/devil/utils/__init__.py
 ../../third_party/catapult/devil/devil/utils/cmd_helper.py
 ../../third_party/catapult/devil/devil/utils/geometry.py
-../../third_party/catapult/devil/devil/utils/host_utils.py
 ../../third_party/catapult/devil/devil/utils/lazy/__init__.py
 ../../third_party/catapult/devil/devil/utils/lazy/weak_constant.py
 ../../third_party/catapult/devil/devil/utils/logging_common.py
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 303a294..fdb44409 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -373,7 +373,7 @@
  <item id="promise_app_service" added_in_milestone="114" content_hash_code="07856112" os_list="chromeos" file_path="chrome/browser/apps/app_service/promise_apps/promise_app_almanac_connector.cc" />
  <item id="safe_browsing_hashprefix_realtime_lookup_ohttp" added_in_milestone="114" content_hash_code="0817ae7d" os_list="linux,windows,chromeos,android" file_path="components/safe_browsing/core/browser/hashprefix_realtime/hash_realtime_service.cc" />
  <item id="gaia_auth_rotate_bound_cookies" added_in_milestone="115" content_hash_code="04014185" os_list="linux,windows" file_path="chrome/browser/signin/bound_session_credentials/bound_session_refresh_cookie_fetcher.cc" />
- <item id="crash_file_uploader" added_in_milestone="115" content_hash_code="05f6902f" os_list="linux,windows" file_path="remoting/host/crash/crash_file_uploader.cc" />
+ <item id="crash_file_uploader" added_in_milestone="115" content_hash_code="05f6902f" os_list="windows" file_path="remoting/host/crash/crash_file_uploader.cc" />
  <item id="glanceables_classroom_integration" added_in_milestone="115" content_hash_code="048f5ccb" os_list="chromeos" file_path="chrome/browser/ui/ash/glanceables/glanceables_classroom_client_impl.cc" />
  <item id="promise_app_service_download_icon" added_in_milestone="115" content_hash_code="00521032" os_list="chromeos" file_path="chrome/browser/apps/app_service/promise_apps/promise_app_service.cc" />
  <item id="iwa_update_manifest_fetcher" added_in_milestone="116" type="completing" content_hash_code="0031297e" os_list="linux,windows,chromeos" semantics_fields="4,5,7,8,9" policy_fields="-1" file_path="chrome/browser/web_applications/isolated_web_apps/update_manifest/update_manifest_fetcher.cc" />
diff --git a/tools/typescript/definitions/bookmark_manager_private.d.ts b/tools/typescript/definitions/bookmark_manager_private.d.ts
index 2579edd4..9aa0f18 100644
--- a/tools/typescript/definitions/bookmark_manager_private.d.ts
+++ b/tools/typescript/definitions/bookmark_manager_private.d.ts
@@ -23,11 +23,6 @@
         elements: BookmarkNodeDataElement[];
       }
 
-      interface OpenInNewTabParams {
-        active?: boolean;
-        split?: boolean;
-      }
-
       export function copy(idList: string[]): Promise<void>;
       export function cut(idList: string[]): Promise<void>;
       export function paste(parentId: string, selectedIdList?: string[]):
@@ -43,8 +38,7 @@
       export function removeTrees(idList: string[]): Promise<void>;
       export function undo(): void;
       export function redo(): void;
-      export function openInNewTab(
-          id: string, params?: OpenInNewTabParams): void;
+      export function openInNewTab(id: string, active: boolean): void;
       export function openInNewWindow(idList: string[], incognito: boolean):
           void;
       export function openInNewTabGroup(idList: string[]): void;
diff --git a/tools/typescript/definitions/pending.d.ts b/tools/typescript/definitions/pending.d.ts
index 497b1f9f..cae6447 100644
--- a/tools/typescript/definitions/pending.d.ts
+++ b/tools/typescript/definitions/pending.d.ts
@@ -79,4 +79,9 @@
 
   /** The values as a flattened one-dimensional array. */
   data: number[];
-}
\ No newline at end of file
+}
+
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/
+interface Set<T> {
+  difference(other: Set<T>): Set<T>;
+}
diff --git a/tools/utr/output_adapter.py b/tools/utr/output_adapter.py
index a2d68b5..8464ae43 100644
--- a/tools/utr/output_adapter.py
+++ b/tools/utr/output_adapter.py
@@ -94,6 +94,7 @@
         'read gclient': logging.DEBUG,
         'write output_properties_file': logging.DEBUG,
         'prepare skylab tests.': logging.DEBUG,
+        'update invocation instructions': logging.DEBUG,
     }
     # Setup logger for printing to the same line
     logger = logging.getLogger('single_line_logger')
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn
index bb5185a..e5e10c86 100644
--- a/ui/android/BUILD.gn
+++ b/ui/android/BUILD.gn
@@ -402,6 +402,7 @@
     "java/src/org/chromium/ui/dragdrop/DropDataProviderImpl.java",
     "java/src/org/chromium/ui/dragdrop/DropDataProviderUtils.java",
     "java/src/org/chromium/ui/drawable/AnimationLooper.java",
+    "java/src/org/chromium/ui/drawable/BorderDrawable.java",
     "java/src/org/chromium/ui/drawable/StateListDrawableBuilder.java",
     "java/src/org/chromium/ui/events/devices/InputDeviceObserver.java",
     "java/src/org/chromium/ui/gfx/Animation.java",
diff --git a/ui/android/java/src/org/chromium/ui/base/EventForwarder.java b/ui/android/java/src/org/chromium/ui/base/EventForwarder.java
index ee31091..b2460f7 100644
--- a/ui/android/java/src/org/chromium/ui/base/EventForwarder.java
+++ b/ui/android/java/src/org/chromium/ui/base/EventForwarder.java
@@ -26,6 +26,7 @@
 import org.chromium.base.ContentUriUtils;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
+import org.chromium.base.MathUtils;
 import org.chromium.base.TraceEvent;
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.build.annotations.NullMarked;
@@ -782,9 +783,8 @@
         float offsetX = 0.0f;
         float offsetY = 0.0f;
 
-        // TODO(crbug.com/405067297): support multi-finger events on the trackpad (scroll)
         if (event.isFromSource(InputDevice.SOURCE_TOUCHPAD)) {
-            event = updateTrackpadButtonState(event);
+            event = updateTrackpadCapturedButtonState(event);
 
             // Ignore calculating the offset if we don't have the previous event trackpad position
             if (mIsLastTrackpadPositionValid) {
@@ -799,6 +799,14 @@
             mLastTrackpadPositionX = event.getX();
             mLastTrackpadPositionY = event.getY();
 
+            event = updateTrackpadCapturedScrollEvent(event, offsetX, offsetY);
+
+            // Cancel any calculated offset, since scroll events shouldn't affect trackpad position
+            if (event.getAction() == MotionEvent.ACTION_SCROLL) {
+                offsetX = 0;
+                offsetY = 0;
+            }
+
             // Invalidate the trackpad position for these cases:
             // ACTION_UP: No pointer on the trackpad, the position data is stale
             // ACTION_POINTER_UP & ACTION_POINTER_DOWN: Multiple trackpad pointers causes the main
@@ -828,7 +836,7 @@
         return ret;
     }
 
-    private static MotionEvent updateTrackpadButtonState(MotionEvent event) {
+    private static MotionEvent updateTrackpadCapturedButtonState(MotionEvent event) {
         if (event.getAction() != MotionEvent.ACTION_BUTTON_PRESS
                 && event.getAction() != MotionEvent.ACTION_BUTTON_RELEASE) {
             return event;
@@ -864,6 +872,59 @@
                 event.getFlags());
     }
 
+    // TODO(https://crbug.com/415730929): Scroll movement has no momentum
+    // TODO(https://crbug.com/415730915): Move helper methods to a util class
+    // When the pointer is captured, multi-touch gestures are not supported, this supports 2 finger
+    // move to count as a scroll gesture
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    public static MotionEvent updateTrackpadCapturedScrollEvent(
+            MotionEvent event, float offsetX, float offsetY) {
+        if (event.getAction() != MotionEvent.ACTION_MOVE
+                || event.getPointerCount() != 2
+                || (offsetX == 0 && offsetY == 0)) {
+            return event;
+        }
+
+        // TODO(https://crbug.com/415730915): Cache the input device resolution
+        InputDevice inputDevice = InputDevice.getDevice(event.getDeviceId());
+        // Resolution is how many pixels per millimeters on the trackpad
+        float xAxisResolution = 1;
+        float yAxisResolution = 1;
+        if (inputDevice != null) {
+            xAxisResolution = inputDevice.getMotionRange(MotionEvent.AXIS_X).getResolution();
+            yAxisResolution = inputDevice.getMotionRange(MotionEvent.AXIS_Y).getResolution();
+        }
+
+        // TODO(https://crbug.com/415730929): inverse scrolling is not respected, doesn't seem that
+        // the setting value is exposed from the OS.
+        float xDirection = MathUtils.clamp(offsetX / xAxisResolution, -1, 1);
+        float yDirection = MathUtils.clamp(offsetY / yAxisResolution, -1, 1);
+
+        MotionEvent.PointerCoords updatedPointerCoords = new MotionEvent.PointerCoords();
+        event.getPointerCoords(0, updatedPointerCoords);
+        updatedPointerCoords.setAxisValue(MotionEvent.AXIS_HSCROLL, xDirection);
+        updatedPointerCoords.setAxisValue(MotionEvent.AXIS_VSCROLL, yDirection);
+
+        MotionEvent.PointerCoords[] updatedPointerCoordsList = getPointerCoordsForEvent(event);
+        updatedPointerCoordsList[0] = updatedPointerCoords;
+
+        return MotionEvent.obtain(
+                event.getDownTime(),
+                event.getEventTime(),
+                MotionEvent.ACTION_SCROLL,
+                event.getPointerCount(),
+                getPointerPropertiesForEvent(event),
+                updatedPointerCoordsList,
+                event.getMetaState(),
+                event.getButtonState(),
+                event.getXPrecision(),
+                event.getYPrecision(),
+                event.getDeviceId(),
+                event.getEdgeFlags(),
+                event.getSource(),
+                event.getFlags());
+    }
+
     private static MotionEvent.PointerProperties[] getPointerPropertiesForEvent(MotionEvent event) {
         MotionEvent.PointerProperties[] ret =
                 new MotionEvent.PointerProperties[event.getPointerCount()];
diff --git a/ui/android/java/src/org/chromium/ui/base/MimeTypeUtils.java b/ui/android/java/src/org/chromium/ui/base/MimeTypeUtils.java
index a9b3b740..cf52d45 100644
--- a/ui/android/java/src/org/chromium/ui/base/MimeTypeUtils.java
+++ b/ui/android/java/src/org/chromium/ui/base/MimeTypeUtils.java
@@ -10,7 +10,6 @@
 
 import androidx.annotation.IntDef;
 
-import org.chromium.base.BuildInfo;
 import org.chromium.build.annotations.NullMarked;
 import org.chromium.build.annotations.Nullable;
 import org.chromium.url.GURL;
@@ -84,11 +83,11 @@
     /**
      * @param mimeType The mime type associated with an operation that needs a permission.
      * @return The name of the Android permission to request. Returns null if no permission will
-     *         allow access to the file, for example on Android T+ where READ_EXTERNAL_STORAGE has
-     *         been replaced with a handful of READ_MEDIA_* permissions.
+     *     allow access to the file, for example on Android T+ where READ_EXTERNAL_STORAGE has been
+     *     replaced with a handful of READ_MEDIA_* permissions.
      */
     public @Nullable static String getPermissionNameForMimeType(@MimeTypeUtils.Type int mimeType) {
-        if (useExternalStoragePermission()) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
             return Manifest.permission.READ_EXTERNAL_STORAGE;
         }
 
@@ -109,10 +108,4 @@
         return clipDescription.hasMimeType(CHROME_MIMETYPE_TAB)
                 || clipDescription.hasMimeType(CHROME_MIMETYPE_TAB_GROUP);
     }
-
-    static boolean useExternalStoragePermission() {
-        // Extracted into a helper method for easy testing. Can be replaced with test annotations
-        // once Robolectric recognizes SDK = T.
-        return Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || !BuildInfo.targetsAtLeastT();
-    }
 }
diff --git a/ui/android/java/src/org/chromium/ui/drawable/BorderDrawable.java b/ui/android/java/src/org/chromium/ui/drawable/BorderDrawable.java
new file mode 100644
index 0000000..d432c029
--- /dev/null
+++ b/ui/android/java/src/org/chromium/ui/drawable/BorderDrawable.java
@@ -0,0 +1,72 @@
+// 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.drawable;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.ColorInt;
+
+import org.chromium.build.annotations.NullMarked;
+import org.chromium.build.annotations.Nullable;
+
+/**
+ * A custom {@link Drawable} designed to render a rounded rectangular border within the drawable's
+ * bounds. This class provides flexibility in defining the border's width, color, corner radius, and
+ * insets. It leverages the Android drawing framework to efficiently draw this border on a {@link
+ * Canvas}.
+ */
+@NullMarked
+public class BorderDrawable extends Drawable {
+    private final Paint mBorderPaint;
+    private final int mInsets;
+    private final float mBorderRadius;
+
+    /**
+     * Constructs a {@link BorderDrawable}.
+     *
+     * @param borderWidth The width of the border to draw, in pixels.
+     * @param insets The insets to apply inside the bounds before drawing the border, in pixels.
+     * @param borderColor The color of the border.
+     * @param borderRadius The radius of the corners of the border, in pixels.
+     */
+    public BorderDrawable(
+            int borderWidth, int insets, @ColorInt int borderColor, float borderRadius) {
+        mInsets = insets;
+        mBorderRadius = borderRadius;
+        mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mBorderPaint.setColor(borderColor);
+        mBorderPaint.setStyle(Paint.Style.STROKE);
+        mBorderPaint.setStrokeWidth(borderWidth);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        RectF bounds = new RectF(getBounds());
+        bounds.inset(mInsets, mInsets);
+        canvas.drawRoundRect(bounds, mBorderRadius, mBorderRadius, mBorderPaint);
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mBorderPaint.setAlpha(alpha);
+        invalidateSelf();
+    }
+
+    @Override
+    public void setColorFilter(@Nullable ColorFilter colorFilter) {
+        mBorderPaint.setColorFilter(colorFilter);
+        invalidateSelf();
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+}
diff --git a/ui/android/junit/src/org/chromium/ui/base/EventForwarderTest.java b/ui/android/junit/src/org/chromium/ui/base/EventForwarderTest.java
index b2bbb38..95a81d4 100644
--- a/ui/android/junit/src/org/chromium/ui/base/EventForwarderTest.java
+++ b/ui/android/junit/src/org/chromium/ui/base/EventForwarderTest.java
@@ -4,6 +4,8 @@
 
 package org.chromium.ui.base;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
@@ -417,6 +419,16 @@
     }
 
     @Test
+    public void testCapturedPointerTrackpadScrollEvent() {
+        MotionEvent event = getTrackpadEvent(MotionEvent.ACTION_MOVE, 0, 2);
+        MotionEvent updatedEvent = EventForwarder.updateTrackpadCapturedScrollEvent(event, 10, -10);
+
+        assertEquals(MotionEvent.ACTION_SCROLL, updatedEvent.getAction());
+        assertTrue(updatedEvent.getAxisValue(MotionEvent.AXIS_HSCROLL) > 0);
+        assertTrue(updatedEvent.getAxisValue(MotionEvent.AXIS_VSCROLL) < 0);
+    }
+
+    @Test
     public void testCapturedPointerMouseMoveEvent() {
         EventForwarder eventForwarder = new EventForwarder(NATIVE_EVENT_FORWARDER_ID, true, true);
 
@@ -531,13 +543,17 @@
     }
 
     private static MotionEvent getTrackpadEvent(int action, int buttonState) {
+        return getTrackpadEvent(action, buttonState, 1);
+    }
+
+    private static MotionEvent getTrackpadEvent(int action, int buttonState, int pointersCnt) {
         return MotionEvent.obtain(
                 0,
                 0,
                 action,
-                1,
-                getToolTypeFingerProperties(1),
-                getPointerCoords(1),
+                pointersCnt,
+                getToolTypeFingerProperties(pointersCnt),
+                getPointerCoords(pointersCnt),
                 0,
                 buttonState,
                 0,
diff --git a/ui/android/junit/src/org/chromium/ui/base/MimeTypeUtilsTest.java b/ui/android/junit/src/org/chromium/ui/base/MimeTypeUtilsTest.java
index 4f9a7630..e7c4c47 100644
--- a/ui/android/junit/src/org/chromium/ui/base/MimeTypeUtilsTest.java
+++ b/ui/android/junit/src/org/chromium/ui/base/MimeTypeUtilsTest.java
@@ -19,8 +19,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
 
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.url.GURL;
@@ -115,7 +113,7 @@
     }
 
     @Test
-    @Config(shadows = {ShadowMimeTypeUtilsForT.class})
+    @Config(sdk = VERSION_CODES.TIRAMISU)
     public void testPermissionForMimeTypeAndroidT() {
         assertEquals(
                 "Wrong permission for audio mime type",
@@ -137,13 +135,4 @@
     private void updateMockGurlSpec(String spec) {
         doReturn(spec).when(mMockedUrl).getSpec();
     }
-
-    @Implements(MimeTypeUtils.class)
-    @SuppressWarnings("UnusedMethod") // Error Prone does not know about shadows.
-    private static class ShadowMimeTypeUtilsForT {
-        @Implementation
-        public static boolean useExternalStoragePermission() {
-            return false;
-        }
-    }
 }
diff --git a/ui/android/view_android.cc b/ui/android/view_android.cc
index e8276d62..dc2e73d8 100644
--- a/ui/android/view_android.cc
+++ b/ui/android/view_android.cc
@@ -10,10 +10,8 @@
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
-#include "base/auto_reset.h"
 #include "base/containers/adapters.h"
 #include "base/containers/contains.h"
-#include "base/debug/dump_without_crashing.h"
 #include "base/memory/raw_ptr.h"
 #include "base/not_fatal_until.h"
 #include "cc/slim/layer.h"
@@ -277,12 +275,6 @@
 }
 
 void ViewAndroid::RemoveAllChildren(bool attached_to_window) {
-  // Modifying children during hit testing can cause issues
-  // (crbug.com/407571917). Log a non-fatal report if this happens.
-  if (is_hit_testing_) {
-    base::debug::DumpWithoutCrashing();
-  }
-
   auto it = children_.begin();
   while (it != children_.end()) {
     if (attached_to_window)
@@ -302,11 +294,6 @@
   std::list<raw_ptr<ViewAndroid, CtnExperimental>>::iterator it =
       std::ranges::find(children_, child);
   CHECK(it != children_.end(), base::NotFatalUntil::M130);
-  // Modifying children during hit testing can cause issues
-  // (crbug.com/407571917). Log a non-fatal report if this happens.
-  if (is_hit_testing_) {
-    base::debug::DumpWithoutCrashing();
-  }
   children_.erase(it);
   child->parent_ = nullptr;
 }
@@ -737,8 +724,6 @@
     }
   }
 
-  base::AutoReset<bool> reset_is_hit_testing(&is_hit_testing_, true);
-
   if (!children_.empty()) {
     gfx::PointF offset_point(point);
     offset_point.Offset(-bounds_dips_.x(), -bounds_dips_.y());
diff --git a/ui/android/view_android.h b/ui/android/view_android.h
index e528d1e..8dca981 100644
--- a/ui/android/view_android.h
+++ b/ui/android/view_android.h
@@ -320,11 +320,6 @@
   const base::android::ScopedJavaLocalRef<jobject> GetViewAndroidDelegate()
       const;
 
-  // True if a hit test is currently being performed on this ViewAndroid or its
-  // descendants. This is used to detect and log cases where the view hierarchy
-  // is modified (e.g., a child is removed) during hit testing, which can lead
-  // to unexpected behavior or crashes.
-  bool is_hit_testing_ = false;
   std::list<raw_ptr<ViewAndroid, CtnExperimental>> children_;
   base::ObserverList<ViewAndroidObserver>::Unchecked observer_list_;
   scoped_refptr<cc::slim::Layer> layer_;
diff --git a/ui/compositor/debug_utils.cc b/ui/compositor/debug_utils.cc
index 70ace71d0..12e380c 100644
--- a/ui/compositor/debug_utils.cc
+++ b/ui/compositor/debug_utils.cc
@@ -59,8 +59,6 @@
       break;
     case ui::LAYER_TEXTURED:
       *out << " textured";
-      if (layer->fills_bounds_opaquely())
-        *out << " opaque";
       break;
     case ui::LAYER_SOLID_COLOR:
       *out << " solid";
@@ -70,6 +68,10 @@
       break;
   }
 
+  if (layer->fills_bounds_opaquely()) {
+    *out << " opaque";
+  }
+
   if (!layer->visible())
     *out << " !visible";
 
diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn
index e598004..491b8006 100644
--- a/ui/display/BUILD.gn
+++ b/ui/display/BUILD.gn
@@ -69,6 +69,8 @@
       "mac/display_link_mac.h",
       "mac/display_link_mac.mm",
       "mac/screen_mac.mm",
+      "mac/screen_mac_headless.h",
+      "mac/screen_mac_headless.mm",
     ]
   }
 
@@ -147,6 +149,7 @@
   }
 
   if (is_mac) {
+    deps += [ "//components/headless/screen_info" ]
     frameworks = [
       "AppKit.framework",
       "CoreGraphics.framework",
diff --git a/ui/display/mac/DEPS b/ui/display/mac/DEPS
new file mode 100644
index 0000000..4a3d7376
--- /dev/null
+++ b/ui/display/mac/DEPS
@@ -0,0 +1,5 @@
+specific_include_rules = {
+  "screen_mac_headless\.mm": [
+    "+components/headless/screen_info",
+  ],
+}
diff --git a/ui/display/mac/screen_mac.mm b/ui/display/mac/screen_mac.mm
index 874ac9d..033ebc00 100644
--- a/ui/display/mac/screen_mac.mm
+++ b/ui/display/mac/screen_mac.mm
@@ -21,6 +21,8 @@
 #include "base/apple/bridging.h"
 #include "base/apple/foundation_util.h"
 #include "base/apple/scoped_cftyperef.h"
+#include "base/check_deref.h"
+#include "base/command_line.h"
 #include "base/functional/bind.h"
 #include "base/i18n/rtl.h"
 #include "base/logging.h"
@@ -32,11 +34,13 @@
 #include "components/device_event_log/device_event_log.h"
 #include "ui/display/display.h"
 #include "ui/display/display_change_notifier.h"
+#include "ui/display/mac/screen_mac_headless.h"
 #include "ui/display/util/display_util.h"
 #include "ui/gfx/geometry/point.h"
 #include "ui/gfx/icc_profile.h"
 #include "ui/gfx/mac/coordinate_conversion.h"
 #include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/switches.h"
 
 extern "C" {
 Boolean CGDisplayUsesForceToGray(void);
@@ -611,6 +615,13 @@
 }
 
 Screen* CreateNativeScreen() {
+  const base::CommandLine& command_line =
+      CHECK_DEREF(base::CommandLine::ForCurrentProcess());
+
+  if (command_line.HasSwitch(switches::kHeadless)) {
+    return new ScreenMacHeadless;
+  }
+
   return new ScreenMac;
 }
 
diff --git a/ui/display/mac/screen_mac_headless.h b/ui/display/mac/screen_mac_headless.h
new file mode 100644
index 0000000..708cd0b8
--- /dev/null
+++ b/ui/display/mac/screen_mac_headless.h
@@ -0,0 +1,40 @@
+// 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 UI_DISPLAY_MAC_SCREEN_MAC_HEADLESS_H_
+#define UI_DISPLAY_MAC_SCREEN_MAC_HEADLESS_H_
+
+#include "ui/display/display.h"
+#include "ui/display/screen_base.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace display {
+
+class ScreenMacHeadless : public ScreenBase {
+ public:
+  ScreenMacHeadless();
+
+  ScreenMacHeadless(const ScreenMacHeadless&) = delete;
+  ScreenMacHeadless& operator=(const ScreenMacHeadless&) = delete;
+
+  ~ScreenMacHeadless() override;
+
+  // display::Screen overrides:
+  gfx::Point GetCursorScreenPoint() override;
+  bool IsWindowUnderCursor(gfx::NativeWindow window) override;
+  gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point) override;
+  gfx::NativeWindow GetLocalProcessWindowAtPoint(
+      const gfx::Point& point,
+      const std::set<gfx::NativeWindow>& ignore) override;
+  Display GetDisplayNearestWindow(gfx::NativeWindow window) const override;
+  bool IsHeadless() const override;
+
+ private:
+  void CreateDisplayList();
+};
+
+}  // namespace display
+
+#endif  // UI_DISPLAY_MAC_SCREEN_MAC_HEADLESS_H_
diff --git a/ui/display/mac/screen_mac_headless.mm b/ui/display/mac/screen_mac_headless.mm
new file mode 100644
index 0000000..2ae6774
--- /dev/null
+++ b/ui/display/mac/screen_mac_headless.mm
@@ -0,0 +1,125 @@
+// 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 "ui/display/mac/screen_mac_headless.h"
+
+#import <AppKit/AppKit.h>
+
+#include <vector>
+
+#include "base/check_deref.h"
+#include "base/command_line.h"
+#include "base/containers/flat_set.h"
+#include "base/types/expected.h"
+#include "components/headless/screen_info/headless_screen_info.h"
+#include "ui/display/util/display_util.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/mac/coordinate_conversion.h"
+#include "ui/gfx/switches.h"
+
+namespace display {
+
+namespace {
+
+// Headless display ids are synthesized sequential numbers.
+constexpr int64_t kHeadlessDisplayIdBase = 1;
+
+std::vector<headless::HeadlessScreenInfo> GetHeadlessScreenInfos() {
+  std::vector<headless::HeadlessScreenInfo> screen_infos;
+
+  const base::CommandLine& command_line =
+      CHECK_DEREF(base::CommandLine::ForCurrentProcess());
+
+  if (command_line.HasSwitch(switches::kScreenInfo)) {
+    const std::string switch_value =
+        command_line.GetSwitchValueASCII(switches::kScreenInfo);
+    base::expected<std::vector<headless::HeadlessScreenInfo>, std::string>
+        screen_infos_or_error =
+            headless::HeadlessScreenInfo::FromString(switch_value);
+    CHECK(screen_infos_or_error.has_value()) << screen_infos_or_error.error();
+    screen_infos = screen_infos_or_error.value();
+  } else {
+    screen_infos.push_back(headless::HeadlessScreenInfo());
+  }
+
+  return screen_infos;
+}
+
+}  // namespace
+
+ScreenMacHeadless::ScreenMacHeadless() {
+  CreateDisplayList();
+}
+
+ScreenMacHeadless::~ScreenMacHeadless() = default;
+
+void ScreenMacHeadless::CreateDisplayList() {
+  std::vector<headless::HeadlessScreenInfo> screen_infos =
+      GetHeadlessScreenInfos();
+  CHECK(!screen_infos.empty());
+
+  bool is_primary = true;
+  base::flat_set<int64_t> internal_display_ids;
+  for (const headless::HeadlessScreenInfo& it : screen_infos) {
+    static int64_t synthesized_display_id = kHeadlessDisplayIdBase;
+    Display display(synthesized_display_id++);
+    display.set_label(it.label);
+    display.set_color_depth(it.color_depth);
+    display.SetScaleAndBounds(it.device_pixel_ratio, it.bounds);
+
+    if (!it.work_area_insets.IsEmpty()) {
+      display.UpdateWorkAreaFromInsets(it.work_area_insets);
+    }
+
+    if (it.rotation) {
+      CHECK(Display::IsValidRotation(it.rotation));
+      display.SetRotationAsDegree(it.rotation);
+    }
+
+    if (it.is_internal) {
+      internal_display_ids.insert(display.id());
+    }
+
+    ProcessDisplayChanged(display, is_primary);
+    is_primary = false;
+  }
+
+  SetInternalDisplayIds(internal_display_ids);
+}
+
+gfx::Point ScreenMacHeadless::GetCursorScreenPoint() {
+  return gfx::Point();
+}
+
+bool ScreenMacHeadless::IsWindowUnderCursor(gfx::NativeWindow window) {
+  return GetWindowAtScreenPoint(GetCursorScreenPoint()) == window;
+}
+
+gfx::NativeWindow ScreenMacHeadless::GetWindowAtScreenPoint(
+    const gfx::Point& point) {
+  return gfx::NativeWindow();
+}
+
+gfx::NativeWindow ScreenMacHeadless::GetLocalProcessWindowAtPoint(
+    const gfx::Point& point,
+    const std::set<gfx::NativeWindow>& ignore) {
+  return gfx::NativeWindow();
+}
+
+Display ScreenMacHeadless::GetDisplayNearestWindow(
+    gfx::NativeWindow window) const {
+  if (window && GetNumDisplays() > 1) {
+    if (NSWindow* ns_window = window.GetNativeNSWindow()) {
+      const gfx::Rect bounds = gfx::ScreenRectFromNSRect([ns_window frame]);
+      return GetDisplayMatching(bounds);
+    }
+  }
+  return GetPrimaryDisplay();
+}
+
+bool ScreenMacHeadless::IsHeadless() const {
+  return true;
+}
+
+}  // namespace display
diff --git a/ui/events/event.cc b/ui/events/event.cc
index 1c13f6a2..cff87755 100644
--- a/ui/events/event.cc
+++ b/ui/events/event.cc
@@ -10,8 +10,6 @@
 #include <string>
 #include <utility>
 
-#include "base/debug/crash_logging.h"
-#include "base/debug/dump_without_crashing.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/metrics/histogram.h"
@@ -888,24 +886,8 @@
 
   // Check if this is a key repeat. This must be called before initial flags
   // processing, e.g: NormalizeFlags(), to avoid issues like crbug.com/1069690.
-  if (synthesize_key_repeat_enabled_ && IsRepeated(GetLastKeyEvent())) {
-    if (!(flags() & EF_IS_REPEAT) && !(code_ == DomCode::ALT_LEFT)) {
-      // If this branch is reached, it means that IsRepeated thinks this should
-      // be a repeat key event, while the native event repeat information was
-      // either incorrect or not preserved. This is unexpected, but execution
-      // is permitted to continue as it is no worse than the old behavior.
-      //
-      // An exception is made for Alt, which is known to cause IsRepeated to
-      // erroneously return true when Alt+Tab is used to rapidly toggle focus.
-      //
-      // TODO(https://crbug.com/411681432) Remove IsRepeated once it is deemed
-      // strictly redundant.
-      SCOPED_CRASH_KEY_STRING32("ui", "key_code", GetCodeString());
-      SCOPED_CRASH_KEY_STRING32("ui", "key_event_type", GetName());
-      base::debug::DumpWithoutCrashing();
-    }
+  if (synthesize_key_repeat_enabled_ && IsRepeated(GetLastKeyEvent()))
     SetFlags(flags() | EF_IS_REPEAT);
-  }
 
 #if BUILDFLAG(IS_LINUX)
   NormalizeFlags();
diff --git a/ui/gfx/android/achoreographer_compat.cc b/ui/gfx/android/achoreographer_compat.cc
index 49cccfa..e367f4a 100644
--- a/ui/gfx/android/achoreographer_compat.cc
+++ b/ui/gfx/android/achoreographer_compat.cc
@@ -47,7 +47,8 @@
 }
 
 AChoreographerCompat33::AChoreographerCompat33() {
-  if (!base::android::BuildInfo::GetInstance()->is_at_least_t()) {
+  if (base::android::BuildInfo::GetInstance()->sdk_int() <
+      base::android::SDK_VERSION_T) {
     supported = false;
     return;
   }
diff --git a/ui/gl/gl_features.cc b/ui/gl/gl_features.cc
index 6aac7eb..ddd12a8f 100644
--- a/ui/gl/gl_features.cc
+++ b/ui/gl/gl_features.cc
@@ -168,11 +168,11 @@
 
 bool IsAndroidFrameDeadlineEnabled() {
 #if BUILDFLAG(IS_ANDROID)
-  static bool enabled =
-      base::android::BuildInfo::GetInstance()->is_at_least_t() &&
-      gfx::AChoreographerCompat33::Get().supported &&
-      gfx::SurfaceControl::SupportsSetFrameTimeline() &&
-      gfx::SurfaceControl::SupportsSetEnableBackPressure();
+  static bool enabled = base::android::BuildInfo::GetInstance()->sdk_int() >=
+                            base::android::SDK_VERSION_T &&
+                        gfx::AChoreographerCompat33::Get().supported &&
+                        gfx::SurfaceControl::SupportsSetFrameTimeline() &&
+                        gfx::SurfaceControl::SupportsSetEnableBackPressure();
   return enabled;
 #else
   return false;
diff --git a/ui/gtk/gtk_key_bindings_handler.cc b/ui/gtk/gtk_key_bindings_handler.cc
index 291c428..48c8397 100644
--- a/ui/gtk/gtk_key_bindings_handler.cc
+++ b/ui/gtk/gtk_key_bindings_handler.cc
@@ -68,8 +68,6 @@
          ui::TextEditCommand::MOVE_UP},
         {{ui::KeyboardCode::VKEY_P, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN},
          ui::TextEditCommand::MOVE_UP_AND_MODIFY_SELECTION},
-        {{ui::KeyboardCode::VKEY_T, ui::EF_CONTROL_DOWN},
-         ui::TextEditCommand::TRANSPOSE},
         {{ui::KeyboardCode::VKEY_U, ui::EF_CONTROL_DOWN},
          ui::TextEditCommand::DELETE_TO_BEGINNING_OF_LINE},
         {{ui::KeyboardCode::VKEY_W, ui::EF_CONTROL_DOWN},
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.cc b/ui/views/bubble/bubble_dialog_delegate_view.cc
index 66b8dcf..afa10e2 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view.cc
@@ -397,21 +397,6 @@
       this};
 };
 
-class BubbleDialogDelegate::ThemeObserver : public ViewObserver {
- public:
-  explicit ThemeObserver(BubbleDialogDelegate* delegate) : delegate_(delegate) {
-    observation_.Observe(delegate->GetContentsView());
-  }
-
-  void OnViewThemeChanged(views::View* view) override {
-    delegate_->UpdateColorsFromTheme();
-  }
-
- private:
-  const raw_ptr<BubbleDialogDelegate> delegate_;
-  base::ScopedObservation<View, ViewObserver> observation_{this};
-};
-
 class BubbleDialogDelegateView::CloseOnDeactivatePin::Pins {
  public:
   Pins() = default;
@@ -475,11 +460,9 @@
 
   RegisterWidgetInitializedCallback(base::BindOnce(
       [](BubbleDialogDelegate* bubble_delegate) {
-        bubble_delegate->theme_observer_ =
-            std::make_unique<ThemeObserver>(bubble_delegate);
         // Call the theme callback to make sure the initial theme is picked up
         // by the BubbleDialogDelegate.
-        bubble_delegate->UpdateColorsFromTheme();
+        bubble_delegate->UpdateFrameColor();
       },
       this));
 
@@ -727,6 +710,17 @@
   }
 }
 
+void BubbleDialogDelegate::SetBackgroundColor(ui::ColorVariant color) {
+  if (color_ == color) {
+    return;
+  }
+
+  color_ = color;
+  if (GetWidget()) {
+    UpdateFrameColor();
+  }
+}
+
 void BubbleDialogDelegate::SetArrow(BubbleBorder::Arrow arrow) {
   SetArrowWithoutResizing(arrow);
   // If SetArrow() is called before CreateWidget(), there's no need to update
@@ -1133,7 +1127,7 @@
   }
 }
 
-void BubbleDialogDelegate::UpdateColorsFromTheme() {
+void BubbleDialogDelegate::UpdateFrameColor() {
   View* const contents_view = GetContentsView();
   DCHECK(contents_view);
 
diff --git a/ui/views/bubble/bubble_dialog_delegate_view.h b/ui/views/bubble/bubble_dialog_delegate_view.h
index 19ac071..9ff1a7e 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view.h
+++ b/ui/views/bubble/bubble_dialog_delegate_view.h
@@ -448,7 +448,7 @@
   // be a good fit for the UI you are building.
 
   ui::ColorVariant background_color() const { return color_; }
-  void set_background_color(ui::ColorVariant color) { color_ = color; }
+  void SetBackgroundColor(ui::ColorVariant color);
 
   void set_force_create_contents_background(
       bool force_create_contents_background) {
@@ -578,7 +578,6 @@
   class AnchorViewObserver;
   class AnchorWidgetObserver;
   class BubbleWidgetObserver;
-  class ThemeObserver;
 
   FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateViewTest,
                            VisibleWidgetShowsInkDropOnAttaching);
@@ -593,7 +592,6 @@
   friend class AnchorWidgetObserver;
   friend class BubbleWidgetObserver;
   friend class TestBubbleUmaLogger;
-  friend class ThemeObserver;
 
   friend class BubbleBorderDelegate;
   friend class BubbleWindowTargeter;
@@ -611,9 +609,7 @@
   void OnBubbleWidgetPaintAsActiveChanged();
 
   void OnDeactivate();
-
-  // Update the bubble color from the NativeTheme unless it was explicitly set.
-  void UpdateColorsFromTheme();
+  void UpdateFrameColor();
 
   // Notify this bubble that it is now the primary anchored bubble. When a new
   // bubble becomes the primary anchor, the previous primary silently loses its
@@ -636,7 +632,6 @@
   std::unique_ptr<AnchorViewObserver> anchor_view_observer_;
   std::unique_ptr<AnchorWidgetObserver> anchor_widget_observer_;
   std::unique_ptr<BubbleWidgetObserver> bubble_widget_observer_;
-  std::unique_ptr<ThemeObserver> theme_observer_;
   bool adjust_if_offscreen_ = true;
   bool focus_traversable_from_anchor_view_ = true;
   ViewTracker highlighted_button_tracker_;
@@ -679,7 +674,7 @@
   bool paint_client_to_layer_ = false;
 
   // If true, contents view will be forced to create a solid color background in
-  // UpdateColorsFromTheme().
+  // `UpdateFrameColor()`.
   bool force_create_contents_background_ = false;
 
 #if BUILDFLAG(IS_MAC)
diff --git a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
index 19d2870..4d214f3 100644
--- a/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
+++ b/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc
@@ -219,7 +219,7 @@
       Widget::InitParams::CLIENT_OWNS_WIDGET, Widget::InitParams::TYPE_WINDOW);
   TestBubbleDialogDelegateView* bubble_delegate =
       new TestBubbleDialogDelegateView(anchor_widget->GetContentsView());
-  bubble_delegate->set_background_color(SK_ColorGREEN);
+  bubble_delegate->SetBackgroundColor(SK_ColorGREEN);
   Widget* bubble_widget =
       BubbleDialogDelegateView::CreateBubble(bubble_delegate);
   EXPECT_EQ(bubble_delegate, bubble_widget->widget_delegate());
diff --git a/ui/views/examples/bubble_example.cc b/ui/views/examples/bubble_example.cc
index 9f5f5c5..e11ce52 100644
--- a/ui/views/examples/bubble_example.cc
+++ b/ui/views/examples/bubble_example.cc
@@ -140,7 +140,7 @@
 
   // |bubble| will be destroyed by its widget when the widget is destroyed.
   auto bubble = std::make_unique<ExampleBubble>(*button, arrow);
-  bubble->set_background_color(colors[(color_index++) % std::size(colors)]);
+  bubble->SetBackgroundColor(colors[(color_index++) % std::size(colors)]);
   bubble->set_shadow(shadow);
   if (persistent) {
     bubble->set_close_on_deactivate(false);
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc
index ac502de..9a42a64f 100644
--- a/ui/views/win/hwnd_message_handler.cc
+++ b/ui/views/win/hwnd_message_handler.cc
@@ -1015,17 +1015,6 @@
 void HWNDMessageHandler::SizeConstraintsChanged() {
   LONG style = GetWindowLong(hwnd(), GWL_STYLE);
 
-  // Ignore if this is not a standard window.
-  if (style & static_cast<LONG>(WS_POPUP | WS_CHILD)) {
-    // Allow Glic to become resizable dynamically.
-    // TODO(404947780): this should be allowed for all widgets. thestig@
-    // suggested to scope the change to glic and and let other widgets to bypass
-    // the restriction on their own schedule.
-    if (debugging_id() != "GlicWidget") {
-      return;
-    }
-  }
-
   // Key style considerations:
   // - WS_THICKFRAME: Enables resizing. Cannot be used with translucent
   //   windows (see CalculateWindowStylesFromInitParams() for details).
diff --git a/v8 b/v8
index 84b3ee9..7f56d60 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit 84b3ee97fdd8f2df1e477b0cc79b893707da21b1
+Subproject commit 7f56d608e37d27607c638cdb4d9995bd7ad4d68c