diff --git a/DEPS b/DEPS
index 47f6573b..46e0a831 100644
--- a/DEPS
+++ b/DEPS
@@ -303,11 +303,11 @@
   # 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': 'b5f7eab40bf7a2f2b1d6acf93a8f672a7cc63df9',
+  'v8_revision': '57c1b239277abc4012a5afe76048710b0c9e56d1',
   # 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': '5fd79e6a2ce891a7e4e965631d4b53e921170638',
+  'angle_revision': '9633d8f745bfb1feebefc3578e188ff3b960983a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -375,7 +375,7 @@
   # 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': 'bd90f97371197657589c4615e71db1601c391a25',
+  'crossbench_revision': 'de2f8a499257e1622a1c122fab1c84a46b98e469',
   # 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': '0987a35390f74a2ecff9ac90e3531be5412c21fc',
+  'devtools_frontend_revision': '5c5b6444531b90826ac72ba9c8fe99965dbff83e',
   # 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': '82963fe1f77a313839809d955fffea7b235fc32b',
+  'dawn_revision': 'a4426dc31c07fa49a58063cae9d7647d096d132d',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -523,7 +523,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.
-  'compiler_rt_revision': '1d27130259209a2b5c8ac992b832fc5522f38a8d',
+  'compiler_rt_revision': '21a487e9fae57351be896c5c34dce665e0990c76',
 
   # If you change this, also update the libc++ revision in
   # //buildtools/deps_revisions.gni.
@@ -1486,7 +1486,7 @@
 
   'src/clank': {
     'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' +
-    '2d55e0d9182a400258fe4f8cd1c5ba1f882cf328',
+    '0f0cb77b3c4c1d3eda905d257d2a612ceae472e1',
     'condition': 'checkout_android and checkout_src_internal',
   },
 
@@ -1645,7 +1645,7 @@
     'packages': [
       {
           'package': 'chromium/third_party/androidx',
-          'version': 'dKxftVsUeWTHR64K1ywUo9zWElvcUVqJnGPnDVQbVGcC',
+          'version': 'T6iAJzbVHlw3lE6kxx7uAYnuanajCjzpRUt8G7WIrgEC',
       },
     ],
     'condition': 'checkout_android and non_git_source',
@@ -2526,7 +2526,7 @@
     Var('pdfium_git') + '/pdfium.git' + '@' +  Var('pdfium_revision'),
 
   'src/third_party/perfetto':
-    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '5acde97bf62e479089bd6466a8d84cd80846f502',
+    Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '998e664be79bba4703c0674ff62a29e37cbeccdb',
 
   'src/base/tracing/test/data': {
     'bucket': 'perfetto',
@@ -2833,7 +2833,7 @@
       'packages': [
           {
               'package': 'chromium/third_party/turbine',
-              'version': 'PbV7UFdzFIl6b_4lNwsj4VnlvnoULNAZRDwsndTGXTsC',
+              'version': 'VGtOG2ivl1SJR7Lai5FQddIu15mWCYDnp47QtozMQeoC',
           },
       ],
       'condition': 'checkout_android and non_git_source',
@@ -2894,7 +2894,7 @@
     Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'),
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '658d2f57f201457e9f8fb7c76ab7ce25505b580a',
+    Var('webrtc_git') + '/src.git' + '@' + '3bf535164c7c13ad6fc5b5dab313dc6acc7c2552',
 
   # 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.
@@ -3016,7 +3016,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/boca_app/app',
-        'version': 'MMDV01P6VbZAdQzoT6IOpeYcpse_LERxWxyKTYaRJ1sC',
+        'version': 'LWXcF-OX0IxmgZq6kTJs8qyMetNUYPvJF59Y5EdhKYkC',
       },
     ],
     'condition': 'checkout_chromeos and checkout_src_internal',
@@ -3071,7 +3071,7 @@
     'packages': [
       {
         'package': 'chromeos_internal/apps/projector_app/app',
-        'version': 'qKsb6npgNOINvTTNvwWwMf6Yws18fKwuZIp6EvpW5H8C',
+        'version': '26oKmBctmtq0u-AjVV95cQ7gxBMBBcUaz8E40r6gH4wC',
       },
     ],
     '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' + '@' +
-        '3fb8783d75611c5ada2ee094ad2be8331e9bd233',
+        '0ba678b370b060940d11c7b7b19edfc37addd02a',
       'condition': 'checkout_src_internal',
   },
 
@@ -4680,7 +4680,7 @@
 
   'src/ios_internal':  {
       'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' +
-        '606119a35c83780604698af972dc4f769e6247dc',
+        'e53c6ac98134184d114ce34da0229ff717e6f4e7',
       'condition': 'checkout_ios and checkout_src_internal',
   },
 
diff --git a/android_webview/nonembedded/BUILD.gn b/android_webview/nonembedded/BUILD.gn
index 70389ae6..2772f6d9 100644
--- a/android_webview/nonembedded/BUILD.gn
+++ b/android_webview/nonembedded/BUILD.gn
@@ -145,7 +145,6 @@
     "//third_party/android_deps:com_google_code_findbugs_jsr305_java",
     "//third_party/android_deps:protobuf_lite_runtime_java",
     "//third_party/androidx:androidx_annotation_annotation_java",
-    "//third_party/androidx:androidx_core_core_java",
     "//third_party/jni_zero:jni_zero_java",
   ]
   resources_package = "org.chromium.android_webview.devui"
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/services/DeveloperUiService.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/services/DeveloperUiService.java
index 2a4413e..6092cca 100644
--- a/android_webview/nonembedded/java/src/org/chromium/android_webview/services/DeveloperUiService.java
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/services/DeveloperUiService.java
@@ -14,7 +14,6 @@
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
@@ -22,7 +21,6 @@
 import android.os.RemoteException;
 
 import androidx.annotation.NonNull;
-import androidx.core.app.ServiceCompat;
 
 import org.chromium.android_webview.common.DeveloperModeUtils;
 import org.chromium.android_webview.common.Flag;
@@ -272,15 +270,7 @@
                         .setTicker(NOTIFICATION_TICKER)
                         .build();
         try {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
-                ServiceCompat.startForeground(
-                        this,
-                        FLAG_OVERRIDE_NOTIFICATION_ID,
-                        notification,
-                        ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
-            } else {
-                startForeground(FLAG_OVERRIDE_NOTIFICATION_ID, notification);
-            }
+            startForeground(FLAG_OVERRIDE_NOTIFICATION_ID, notification);
         } catch (IllegalStateException e) {
             logSuspectedForegroundServiceStartNotAllowedException();
         }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/BrowserServicesThemeColorProviderUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/BrowserServicesThemeColorProviderUnitTest.java
index 81db61e..8c69934 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/BrowserServicesThemeColorProviderUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/toolbar/BrowserServicesThemeColorProviderUnitTest.java
@@ -4,9 +4,24 @@
 
 package org.chromium.chrome.browser.customtabs.features.toolbar;
 
+import static androidx.browser.customtabs.CustomTabsIntent.COLOR_SCHEME_LIGHT;
+
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Color;
+import android.view.ContextThemeWrapper;
+
+import androidx.browser.customtabs.CustomTabColorSchemeParams;
+import androidx.browser.customtabs.CustomTabsIntent;
+import androidx.browser.customtabs.CustomTabsIntent.ColorScheme;
+import androidx.browser.customtabs.CustomTabsSession;
+import androidx.test.core.app.ApplicationProvider;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -16,108 +31,331 @@
 import org.mockito.junit.MockitoRule;
 import org.robolectric.annotation.LooperMode;
 
+import org.chromium.base.IntentUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
 import org.chromium.blink.mojom.DisplayMode;
+import org.chromium.build.annotations.Nullable;
+import org.chromium.chrome.R;
 import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
-import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider.CustomTabProfileType;
-import org.chromium.chrome.browser.browserservices.intents.ColorProvider;
 import org.chromium.chrome.browser.browserservices.intents.WebappExtras;
 import org.chromium.chrome.browser.browserservices.intents.WebappIcon;
+import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
+import org.chromium.chrome.browser.customtabs.IncognitoCustomTabIntentDataProvider;
+import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
+import org.chromium.chrome.browser.customtabs.content.TabObserverRegistrar;
 import org.chromium.chrome.browser.customtabs.features.toolbar.BrowserServicesThemeColorProvider.ThemeColorSource;
+import org.chromium.chrome.browser.document.ChromeLauncherActivity;
 import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.theme.SurfaceColorUpdateUtils;
+import org.chromium.chrome.browser.theme.TopUiThemeColorProvider;
+import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
 
 @RunWith(BaseRobolectricTestRunner.class)
 @LooperMode(LooperMode.Mode.PAUSED)
 public class BrowserServicesThemeColorProviderUnitTest {
+    private static final int LIGHT_COLOR = Color.GREEN;
+    private static final int DARK_COLOR = Color.BLACK;
 
     @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
 
-    @Mock public BrowserServicesIntentDataProvider mIntentDataProvider;
-    @Mock public ColorProvider mColorProvider;
     @Mock public Tab tab;
+    @Mock public TopUiThemeColorProvider mTopUiThemeColorProvider;
+    @Mock public CustomTabActivityTabProvider mCustomTabActivityTabProvider;
+    @Mock public TabObserverRegistrar mTabObserverRegistrar;
+
+    private Context mContext;
 
     @Before
     public void setup() {
-        when(mIntentDataProvider.getColorProvider()).thenReturn(mColorProvider);
-        when(mIntentDataProvider.isOpenedByChrome()).thenReturn(false);
-        when(mIntentDataProvider.getCustomTabMode()).thenReturn(CustomTabProfileType.REGULAR);
-        when(mIntentDataProvider.getColorProvider()).thenReturn(mColorProvider);
-        when(mColorProvider.hasCustomToolbarColor()).thenReturn(false);
+        mContext =
+                new ContextThemeWrapper(
+                        ApplicationProvider.getApplicationContext(),
+                        R.style.Theme_BrowserUI_DayNight);
+    }
+
+    private BrowserServicesThemeColorProvider createThemeColorProvider(
+            BrowserServicesIntentDataProvider intentDataProvider) {
+        return new BrowserServicesThemeColorProvider(
+                mContext,
+                intentDataProvider,
+                mTopUiThemeColorProvider,
+                mCustomTabActivityTabProvider,
+                mTabObserverRegistrar);
+    }
+
+    private BrowserServicesIntentDataProvider buildCctIntentDataProvider(
+            @ColorScheme int colorScheme,
+            @Nullable CustomTabColorSchemeParams schemeParams,
+            boolean isOpenedByChrome,
+            boolean isIncognito) {
+        CustomTabsSession session =
+                CustomTabsSession.createMockSessionForTesting(
+                        new ComponentName(mContext, ChromeLauncherActivity.class));
+        var builder = new CustomTabsIntent.Builder(session);
+        if (schemeParams != null) {
+            builder.setColorSchemeParams(colorScheme, schemeParams);
+        } else {
+            builder.setColorScheme(colorScheme);
+        }
+
+        var intent = builder.build().intent;
+        IntentUtils.setForceIsTrustedIntentForTesting(isOpenedByChrome);
+
+        if (isIncognito) {
+            return new IncognitoCustomTabIntentDataProvider(intent, mContext, colorScheme);
+        } else {
+            return new CustomTabIntentDataProvider(intent, mContext, colorScheme);
+        }
     }
 
     @Test
     public void testTabOpenedByChromeHasIntentColor_UseDefaultTheme() {
-        when(mIntentDataProvider.isOpenedByChrome()).thenReturn(true);
-        when(mColorProvider.hasCustomToolbarColor()).thenReturn(true);
+        var colorParams =
+                new CustomTabColorSchemeParams.Builder().setToolbarColor(DARK_COLOR).build();
+        var intentDataProvider =
+                buildCctIntentDataProvider(
+                        COLOR_SCHEME_LIGHT,
+                        colorParams,
+                        /* isOpenedByChrome= */ true,
+                        /* isIncognito= */ false);
+
         assertEquals(
                 "Intent theme is expected",
                 ThemeColorSource.INTENT,
                 BrowserServicesThemeColorProvider.computeColorSource(
-                        mIntentDataProvider, false, null));
+                        intentDataProvider, false, null));
     }
 
     @Test
     public void testReparentingTabOpenedByChromeNoIntentColor_UseDefaultTheme() {
-        when(mIntentDataProvider.isOpenedByChrome()).thenReturn(true);
+        var intentDataProvider =
+                buildCctIntentDataProvider(
+                        COLOR_SCHEME_LIGHT,
+                        /* schemeParams= */ null,
+                        /* isOpenedByChrome= */ true,
+                        /* isIncognito= */ false);
+
         assertEquals(
                 "Default theme is expected",
                 ThemeColorSource.BROWSER_DEFAULT,
                 BrowserServicesThemeColorProvider.computeColorSource(
-                        mIntentDataProvider, false, null));
+                        intentDataProvider, false, null));
     }
 
     @Test
     public void testCCTOpenedByChromeNoIntentColor_UsePageTheme() {
-        when(mIntentDataProvider.isOpenedByChrome()).thenReturn(true);
+        var intentDataProvider =
+                buildCctIntentDataProvider(
+                        COLOR_SCHEME_LIGHT,
+                        /* schemeParams= */ null,
+                        /* isOpenedByChrome= */ true,
+                        /* isIncognito= */ false);
+
         assertEquals(
                 "Page theme is expected",
                 ThemeColorSource.WEB_PAGE_THEME,
                 BrowserServicesThemeColorProvider.computeColorSource(
-                        mIntentDataProvider, false, tab));
+                        intentDataProvider, false, tab));
     }
 
     @Test
     public void testFullscreenWebApp_UseDefaultTheme() {
-        when(mIntentDataProvider.isOpenedByChrome()).thenReturn(false);
-        when(mIntentDataProvider.getWebappExtras())
+        BrowserServicesIntentDataProvider webAppIntentDataProvider =
+                mock(BrowserServicesIntentDataProvider.class);
+        when(webAppIntentDataProvider.isOpenedByChrome()).thenReturn(false);
+        when(webAppIntentDataProvider.getWebappExtras())
                 .thenReturn(buildWebAppExtras(DisplayMode.FULLSCREEN));
         assertEquals(
                 "Default theme is expected, to keep status bar visible",
                 ThemeColorSource.BROWSER_DEFAULT,
                 BrowserServicesThemeColorProvider.computeColorSource(
-                        mIntentDataProvider, false, tab));
+                        webAppIntentDataProvider, false, tab));
     }
 
     @Test
     public void testUseTabThemeColor() {
-        when(mIntentDataProvider.isOpenedByChrome()).thenReturn(false);
+        var intentDataProvider =
+                buildCctIntentDataProvider(
+                        COLOR_SCHEME_LIGHT,
+                        /* schemeParams= */ null,
+                        /* isOpenedByChrome= */ false,
+                        /* isIncognito= */ false);
+
         assertEquals(
                 "Default theme is expected by default",
                 ThemeColorSource.BROWSER_DEFAULT,
                 BrowserServicesThemeColorProvider.computeColorSource(
-                        mIntentDataProvider, false, tab));
+                        intentDataProvider, false, tab));
     }
 
     @Test
     public void testIntentHasCustomColor() {
-        when(mIntentDataProvider.isOpenedByChrome()).thenReturn(false);
-        when(mColorProvider.hasCustomToolbarColor()).thenReturn(true);
+        var colorSchemeParams =
+                new CustomTabColorSchemeParams.Builder().setToolbarColor(DARK_COLOR).build();
+        var intentDataProvider =
+                buildCctIntentDataProvider(
+                        COLOR_SCHEME_LIGHT,
+                        /* schemeParams= */ colorSchemeParams,
+                        /* isOpenedByChrome= */ false,
+                        /* isIncognito= */ false);
+
         assertEquals(
                 "Intent theme is expected",
                 ThemeColorSource.INTENT,
                 BrowserServicesThemeColorProvider.computeColorSource(
-                        mIntentDataProvider, false, null));
+                        intentDataProvider, false, null));
     }
 
     @Test
     public void testDefaultColorTheme() {
-        when(mIntentDataProvider.isOpenedByChrome()).thenReturn(false);
-        when(mColorProvider.hasCustomToolbarColor()).thenReturn(false);
+        var intentDataProvider =
+                buildCctIntentDataProvider(
+                        COLOR_SCHEME_LIGHT,
+                        /* schemeParams= */ null,
+                        /* isOpenedByChrome= */ false,
+                        /* isIncognito= */ false);
+
         assertEquals(
                 "Default theme is expected",
                 ThemeColorSource.BROWSER_DEFAULT,
                 BrowserServicesThemeColorProvider.computeColorSource(
-                        mIntentDataProvider, false, null));
+                        intentDataProvider, false, null));
+    }
+
+    @Test
+    public void testLightColorTabTheme_TabColorWithLightScheme() {
+        // emulate not incognito tab with page theme
+        when(tab.getThemeColor()).thenReturn(LIGHT_COLOR);
+        when(mTopUiThemeColorProvider.calculateColor(eq(tab), eq(LIGHT_COLOR)))
+                .thenReturn(LIGHT_COLOR);
+        var intentDataProvider =
+                buildCctIntentDataProvider(
+                        COLOR_SCHEME_LIGHT,
+                        /* schemeParams= */ null,
+                        /* isOpenedByChrome= */ false,
+                        /* isIncognito= */ false);
+        var themeColorProvider = createThemeColorProvider(intentDataProvider);
+
+        // check
+        var actual = themeColorProvider.calculateTheme(ThemeColorSource.WEB_PAGE_THEME, tab);
+        assertEquals("Should be tab color", LIGHT_COLOR, actual.color);
+        assertEquals(
+                "Should be light scheme",
+                BrandedColorScheme.LIGHT_BRANDED_THEME,
+                actual.brandedColorScheme);
+    }
+
+    @Test
+    public void testDarkColorTabTheme_TabColorWithDarkScheme() {
+        // emulate not incognito tab with page theme
+        when(tab.getThemeColor()).thenReturn(DARK_COLOR);
+        when(mTopUiThemeColorProvider.calculateColor(eq(tab), eq(DARK_COLOR)))
+                .thenReturn(DARK_COLOR);
+        var intentDataProvider =
+                buildCctIntentDataProvider(
+                        COLOR_SCHEME_LIGHT,
+                        /* schemeParams= */ null,
+                        /* isOpenedByChrome= */ false,
+                        /* isIncognito= */ false);
+        var themeColorProvider = createThemeColorProvider(intentDataProvider);
+
+        // check
+        var actual = themeColorProvider.calculateTheme(ThemeColorSource.WEB_PAGE_THEME, tab);
+        assertEquals("Should be tab color", DARK_COLOR, actual.color);
+        assertEquals(
+                "Should be dark scheme",
+                BrandedColorScheme.DARK_BRANDED_THEME,
+                actual.brandedColorScheme);
+    }
+
+    @Test
+    public void testLightColorIntentTheme_IntentColorLightScheme() {
+        // emulate not incognito tab with intent theme
+        var colorSchemeParams =
+                new CustomTabColorSchemeParams.Builder().setToolbarColor(LIGHT_COLOR).build();
+        var intentDataProvider =
+                buildCctIntentDataProvider(
+                        COLOR_SCHEME_LIGHT,
+                        /* schemeParams= */ colorSchemeParams,
+                        /* isOpenedByChrome= */ false,
+                        /* isIncognito= */ false);
+        var themeColorProvider = createThemeColorProvider(intentDataProvider);
+
+        // check
+        var actual = themeColorProvider.calculateTheme(ThemeColorSource.INTENT, tab);
+        assertEquals("Should be intent color", LIGHT_COLOR, actual.color);
+        assertEquals(
+                "Should be light scheme",
+                BrandedColorScheme.LIGHT_BRANDED_THEME,
+                actual.brandedColorScheme);
+    }
+
+    @Test
+    public void testDarkColorIntentTheme_IntentColorDarkScheme() {
+        // emulate not incognito tab with intent theme
+        var colorSchemeParams =
+                new CustomTabColorSchemeParams.Builder().setToolbarColor(DARK_COLOR).build();
+        var intentDataProvider =
+                buildCctIntentDataProvider(
+                        COLOR_SCHEME_LIGHT,
+                        /* schemeParams= */ colorSchemeParams,
+                        /* isOpenedByChrome= */ false,
+                        /* isIncognito= */ false);
+        var themeColorProvider = createThemeColorProvider(intentDataProvider);
+
+        // check
+        var actual = themeColorProvider.calculateTheme(ThemeColorSource.INTENT, tab);
+        assertEquals("Should be intent color", DARK_COLOR, actual.color);
+        assertEquals(
+                "Should be dark scheme",
+                BrandedColorScheme.DARK_BRANDED_THEME,
+                actual.brandedColorScheme);
+    }
+
+    @Test
+    public void testChromeTheme() {
+        // emulate not incognito tab with chrome default theme
+        var intentDataProvider =
+                buildCctIntentDataProvider(
+                        COLOR_SCHEME_LIGHT,
+                        /* schemeParams= */ null,
+                        /* isOpenedByChrome= */ false,
+                        /* isIncognito= */ false);
+        var themeColorProvider = createThemeColorProvider(intentDataProvider);
+
+        // check
+        var actual = themeColorProvider.calculateTheme(ThemeColorSource.BROWSER_DEFAULT, tab);
+        assertEquals(
+                "Should be app default color",
+                SurfaceColorUpdateUtils.getDefaultThemeColor(mContext, /* isIncognito= */ false),
+                actual.color);
+        assertEquals(
+                "Should be chrome default scheme",
+                BrandedColorScheme.APP_DEFAULT,
+                actual.brandedColorScheme);
+    }
+
+    @Test
+    public void testIncognitoTheme() {
+        // emulate incognito tab with chrome default theme
+        var intentDataProvider =
+                buildCctIntentDataProvider(
+                        COLOR_SCHEME_LIGHT,
+                        /* schemeParams= */ null,
+                        /* isOpenedByChrome= */ false,
+                        /* isIncognito= */ true);
+        var themeColorProvider = createThemeColorProvider(intentDataProvider);
+
+        // check
+        var actual = themeColorProvider.calculateTheme(ThemeColorSource.BROWSER_DEFAULT, tab);
+        assertEquals(
+                "Should be incognito color",
+                SurfaceColorUpdateUtils.getDefaultThemeColor(mContext, /* isIncognito= */ true),
+                actual.color);
+        assertEquals(
+                "Should be incognito scheme",
+                BrandedColorScheme.INCOGNITO,
+                actual.brandedColorScheme);
     }
 
     private static WebappExtras buildWebAppExtras(final int displayMode) {
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 311981b..922807dd 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1978,6 +1978,7 @@
     "//chrome/browser/privacy_budget",
     "//chrome/browser/privacy_sandbox",
     "//chrome/browser/privacy_sandbox:headers",
+    "//chrome/browser/privacy_sandbox/incognito",
     "//chrome/browser/profiles:profile",
     "//chrome/browser/profiles:profile_util",
     "//chrome/browser/profiles:profile_util_impl",
@@ -4658,6 +4659,7 @@
       deps += [
         "//chrome/browser/shortcuts",
         "//chrome/browser/ui/webui/app_home",
+        "//chrome/browser/ui/webui/app_home:impl",
         "//chrome/browser/ui/webui/signin:profile",
         "//chrome/browser/ui/webui/signin:profile_impl",
       ]
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 452cafcf8..42fe1be 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -8781,10 +8781,6 @@
      flag_descriptions::kLauncherNudgeSessionResetName,
      flag_descriptions::kLauncherNudgeSessionResetDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kLauncherNudgeSessionReset)},
-    {"launcher-system-info-answer-cards",
-     flag_descriptions::kLauncherSystemInfoAnswerCardsName,
-     flag_descriptions::kLauncherSystemInfoAnswerCardsDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(search_features::kLauncherSystemInfoAnswerCards)},
     {"text-in-shelf", flag_descriptions::kTextInShelfName,
      flag_descriptions::kTextInShelfDescription, kOsCrOS,
      FEATURE_VALUE_TYPE(ash::features::kHomeButtonWithText)},
@@ -12418,6 +12414,12 @@
      kOsAndroid,
      FEATURE_VALUE_TYPE(blink::features::kSecurePaymentConfirmationUxRefresh)},
 
+#if BUILDFLAG(IS_ANDROID)
+    {"fill-recovery-password", flag_descriptions::kFillRecoveryPasswordName,
+     flag_descriptions::kFillRecoveryPasswordDescription, kOsAndroid,
+     FEATURE_VALUE_TYPE(password_manager::features::kFillRecoveryPassword)},
+#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/android/customtabs/OWNERS b/chrome/browser/android/customtabs/OWNERS
index 47dbacbb..c12b86b 100644
--- a/chrome/browser/android/customtabs/OWNERS
+++ b/chrome/browser/android/customtabs/OWNERS
@@ -1,4 +1,3 @@
-eirage@chromium.org
 lizeb@chromium.org
 peconn@chromium.org
 
diff --git a/chrome/browser/apps/app_service/BUILD.gn b/chrome/browser/apps/app_service/BUILD.gn
index b7335ae..cfe7927 100644
--- a/chrome/browser/apps/app_service/BUILD.gn
+++ b/chrome/browser/apps/app_service/BUILD.gn
@@ -89,6 +89,7 @@
   if (enable_extensions) {
     deps += [
       "//chrome/browser/extensions",
+      "//chrome/browser/ui/extensions:extension_enable_flow_delegate",
       "//chrome/browser/web_applications/extensions",
       "//extensions/browser",
       "//extensions/common",
diff --git a/chrome/browser/apps/platform_apps/BUILD.gn b/chrome/browser/apps/platform_apps/BUILD.gn
index fff7088..0a452c7 100644
--- a/chrome/browser/apps/platform_apps/BUILD.gn
+++ b/chrome/browser/apps/platform_apps/BUILD.gn
@@ -90,6 +90,7 @@
     deps += [
       "//chrome/browser/apps/app_shim",
       "//chrome/browser/profiles",
+      "//chrome/browser/ui/extensions:extension_enable_flow_delegate",
     ]
   }
 
diff --git a/chrome/browser/ash/app_list/search/search_controller_factory.cc b/chrome/browser/ash/app_list/search/search_controller_factory.cc
index c2d43aa..2fa19e168 100644
--- a/chrome/browser/ash/app_list/search/search_controller_factory.cc
+++ b/chrome/browser/ash/app_list/search/search_controller_factory.cc
@@ -77,10 +77,7 @@
         profile, base::FileEnumerator::FileType::FILES |
                      base::FileEnumerator::FileType::DIRECTORIES));
     controller->AddProvider(std::make_unique<DriveSearchProvider>(profile));
-    if (search_features::IsLauncherSystemInfoAnswerCardsEnabled()) {
-      controller->AddProvider(
-          std::make_unique<SystemInfoCardProvider>(profile));
-    }
+    controller->AddProvider(std::make_unique<SystemInfoCardProvider>(profile));
     if (search_features::IsLauncherImageSearchEnabled()) {
       controller->AddProvider(
           std::make_unique<LocalImageSearchProvider>(profile));
diff --git a/chrome/browser/ash/app_list/search/search_features.cc b/chrome/browser/ash/app_list/search/search_features.cc
index 06a017f..c64c004 100644
--- a/chrome/browser/ash/app_list/search/search_features.cc
+++ b/chrome/browser/ash/app_list/search/search_features.cc
@@ -47,10 +47,6 @@
              "kLauncherImageSearchDebug",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
-BASE_FEATURE(kLauncherSystemInfoAnswerCards,
-             "LauncherSystemInfoAnswerCards",
-             base::FEATURE_ENABLED_BY_DEFAULT);
-
 BASE_FEATURE(kLauncherSearchFileScan,
              "kLauncherSearchFileScan",
              base::FEATURE_DISABLED_BY_DEFAULT);
@@ -94,10 +90,6 @@
   return base::FeatureList::IsEnabled(kLauncherImageSearchDebug);
 }
 
-bool IsLauncherSystemInfoAnswerCardsEnabled() {
-  return base::FeatureList::IsEnabled(kLauncherSystemInfoAnswerCards);
-}
-
 bool IsLauncherSearchFileScanEnabled() {
   return base::FeatureList::IsEnabled(kLauncherSearchFileScan);
 }
diff --git a/chrome/browser/ash/app_list/search/search_features.h b/chrome/browser/ash/app_list/search/search_features.h
index f8fbfaf..89595f9b 100644
--- a/chrome/browser/ash/app_list/search/search_features.h
+++ b/chrome/browser/ash/app_list/search/search_features.h
@@ -39,8 +39,6 @@
 // certain tast test and will introduce extra logs to help debug.
 BASE_DECLARE_FEATURE(kLauncherImageSearchDebug);
 
-BASE_DECLARE_FEATURE(kLauncherSystemInfoAnswerCards);
-
 // Enables file scan in launcher. This is used as a stopper if the file scan ran
 // into any issues.
 BASE_DECLARE_FEATURE(kLauncherSearchFileScan);
@@ -55,7 +53,6 @@
 bool IsLauncherImageSearchOcrEnabled();
 bool IsLauncherImageSearchIndexingLimitEnabled();
 bool IsLauncherImageSearchDebugEnabled();
-bool IsLauncherSystemInfoAnswerCardsEnabled();
 bool IsLauncherSearchFileScanEnabled();
 bool IskLauncherKeyShortcutInBestMatchEnabled();
 }  // namespace search_features
diff --git a/chrome/browser/ash/login/enrollment/enrollment_screen.cc b/chrome/browser/ash/login/enrollment/enrollment_screen.cc
index c414a3c..55eb9d0e 100644
--- a/chrome/browser/ash/login/enrollment/enrollment_screen.cc
+++ b/chrome/browser/ash/login/enrollment/enrollment_screen.cc
@@ -41,6 +41,7 @@
 #include "chrome/browser/browser_process_platform_part.h"
 #include "chrome/browser/lifetime/browser_shutdown.h"
 #include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/online_login_utils.h"
 #include "chromeos/ash/components/dbus/dbus_thread_manager.h"
@@ -551,6 +552,7 @@
       ShowSkipEnrollmentDialogue();
       return true;
     } else {
+      LoginDisplayHost::default_host()->GetWebUILoginView()->RequestFocus();
       OnCancel();
       return true;
     }
diff --git a/chrome/browser/ash/login/screens/user_creation_screen.cc b/chrome/browser/ash/login/screens/user_creation_screen.cc
index 720cffff..3e4f711 100644
--- a/chrome/browser/ash/login/screens/user_creation_screen.cc
+++ b/chrome/browser/ash/login/screens/user_creation_screen.cc
@@ -8,6 +8,7 @@
 #include "chrome/browser/ash/login/error_screens_histogram_helper.h"
 #include "chrome/browser/ash/login/wizard_context.h"
 #include "chrome/browser/ui/ash/login/login_display_host.h"
+#include "chrome/browser/ui/ash/login/webui_login_view.h"
 #include "chrome/browser/ui/webui/ash/login/error_screen_handler.h"
 #include "chrome/browser/ui/webui/ash/login/user_creation_screen_handler.h"
 #include "chromeos/ash/components/install_attributes/install_attributes.h"
@@ -159,10 +160,12 @@
 
 bool UserCreationScreen::HandleAccelerator(LoginAcceleratorAction action) {
   if (action == LoginAcceleratorAction::kStartEnrollment) {
+    LoginDisplayHost::default_host()->GetWebUILoginView()->RequestFocus();
     RunExitCallback(Result::ENTERPRISE_ENROLL_SHORTCUT);
     return true;
   }
   if (action == LoginAcceleratorAction::kStartKioskEnrollment) {
+    LoginDisplayHost::default_host()->GetWebUILoginView()->RequestFocus();
     RunExitCallback(Result::KIOSK_ENTERPRISE_ENROLL);
     return true;
   }
diff --git a/chrome/browser/autofill/autofill_ai_model_cache_factory.cc b/chrome/browser/autofill/autofill_ai_model_cache_factory.cc
index c87e8bc..1fae80d 100644
--- a/chrome/browser/autofill/autofill_ai_model_cache_factory.cc
+++ b/chrome/browser/autofill/autofill_ai_model_cache_factory.cc
@@ -33,13 +33,7 @@
 AutofillAiModelCacheFactory::AutofillAiModelCacheFactory()
     : ProfileKeyedServiceFactory(
           "AutofillAiModelCache",
-          ProfileSelections::Builder()
-              .WithRegular(ProfileSelection::kOwnInstance)
-              .WithGuest(ProfileSelection::kOwnInstance)
-              // TODO(crbug.com/41488885): Check if this service is needed for
-              // Ash Internals.
-              .WithAshInternals(ProfileSelection::kOwnInstance)
-              .Build()) {
+          ProfileSelections::BuildRedirectedInIncognito()) {
   DependsOn(HistoryServiceFactory::GetInstance());
 }
 
diff --git a/chrome/browser/autofill/autofill_ai_model_executor_factory.cc b/chrome/browser/autofill/autofill_ai_model_executor_factory.cc
index 2079e7f..ca577c2 100644
--- a/chrome/browser/autofill/autofill_ai_model_executor_factory.cc
+++ b/chrome/browser/autofill/autofill_ai_model_executor_factory.cc
@@ -31,12 +31,8 @@
 }
 
 AutofillAiModelExecutorFactory::AutofillAiModelExecutorFactory()
-    : ProfileKeyedServiceFactory(
-          "AutofillAiModelExecutor",
-          ProfileSelections::Builder()
-              .WithRegular(ProfileSelection::kOwnInstance)
-              .WithGuest(ProfileSelection::kNone)
-              .Build()) {
+    : ProfileKeyedServiceFactory("AutofillAiModelExecutor",
+                                 ProfileSelections::BuildForRegularProfile()) {
   DependsOn(AutofillAiModelCacheFactory::GetInstance());
   DependsOn(OptimizationGuideKeyedServiceFactory::GetInstance());
 }
@@ -63,9 +59,4 @@
       optimization_guide->GetModelQualityLogsUploaderService());
 }
 
-bool AutofillAiModelExecutorFactory::ServiceIsCreatedWithBrowserContext()
-    const {
-  return base::FeatureList::IsEnabled(features::kAutofillAiServerModel);
-}
-
 }  // namespace autofill
diff --git a/chrome/browser/autofill/autofill_ai_model_executor_factory.h b/chrome/browser/autofill/autofill_ai_model_executor_factory.h
index bad2ca9..d7ad3b0 100644
--- a/chrome/browser/autofill/autofill_ai_model_executor_factory.h
+++ b/chrome/browser/autofill/autofill_ai_model_executor_factory.h
@@ -24,10 +24,6 @@
   static AutofillAiModelExecutor* GetForProfile(Profile* profile);
   static AutofillAiModelExecutorFactory* GetInstance();
 
- protected:
-  // ProfileKeyedServiceFactory:
-  bool ServiceIsCreatedWithBrowserContext() const override;
-
  private:
   friend base::NoDestructor<AutofillAiModelExecutorFactory>;
 
diff --git a/chrome/browser/autofill/autofill_entity_data_manager_factory.cc b/chrome/browser/autofill/autofill_entity_data_manager_factory.cc
index f2b3775..ab989f8 100644
--- a/chrome/browser/autofill/autofill_entity_data_manager_factory.cc
+++ b/chrome/browser/autofill/autofill_entity_data_manager_factory.cc
@@ -36,15 +36,7 @@
 AutofillEntityDataManagerFactory::AutofillEntityDataManagerFactory()
     : ProfileKeyedServiceFactory(
           "AutofillEntityDataManager",
-          ProfileSelections::Builder()
-              .WithRegular(ProfileSelection::kOwnInstance)
-              // TODO(crbug.com/40257657): Check if this service is needed in
-              // Guest mode.
-              .WithGuest(ProfileSelection::kOwnInstance)
-              // TODO(crbug.com/41488885): Check if this service is needed for
-              // Ash Internals.
-              .WithAshInternals(ProfileSelection::kOwnInstance)
-              .Build()) {
+          ProfileSelections::BuildRedirectedInIncognito()) {
   DependsOn(WebDataServiceFactory::GetInstance());
   DependsOn(HistoryServiceFactory::GetInstance());
   DependsOn(StrikeDatabaseFactory::GetInstance());
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 46cff0f3..dc89ece 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -5000,6 +5000,11 @@
     "expiry_milestone": 141
   },
   {
+    "name": "fill-recovery-password",
+    "owners": [ "ioanap@chromium.org", "vsemeniuk@google.com" ],
+    "expiry_milestone": 141
+  },
+  {
     "name": "firmware-update-ui-v2",
     "owners": [
       "//ash/webui/firmware_update_ui/OWNERS",
@@ -6040,11 +6045,6 @@
     "expiry_milestone": 140
   },
   {
-    "name": "launcher-system-info-answer-cards",
-    "owners": ["laurencom@chromium.org", "chenjih@google.com"],
-    "expiry_milestone": 130
-  },
-  {
     "name": "left-click-opens-tab-group-bubble",
     "owners": [ "dpenning@chromium.org" ],
     "expiry_milestone": 137
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index b8237e0..3241bfcb 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1749,6 +1749,12 @@
     "Enable language detection for dynamic content which is then exposed to "
     "assistive technologies such as screen readers.";
 
+#if BUILDFLAG(IS_ANDROID)
+const char kFillRecoveryPasswordName[] = "Fill recovery password";
+const char kFillRecoveryPasswordDescription[] =
+    "Offers the previously saved recovery password for filling if one exists.";
+#endif  // BUILDFLAG(IS_ANDROID)
+
 const char kMemlogName[] = "Chrome heap profiler start mode.";
 const char kMemlogDescription[] =
     "Starts heap profiling service that records sampled memory allocation "
@@ -7704,12 +7710,6 @@
     "When enabled, this will reset the launcher nudge shown data on every new "
     "user session, allowing the nudge to be shown again.";
 
-const char kLauncherSystemInfoAnswerCardsName[] =
-    "System Info Answer Cards in launcher";
-const char kLauncherSystemInfoAnswerCardsDescription[] =
-    "Enables System info answer cards in the launcher to provide system "
-    "performance metrics";
-
 const char kMacAddressRandomizationName[] = "MAC address randomization";
 const char kMacAddressRandomizationDescription[] =
     "Feature to allow MAC address randomization to be enabled for WiFi "
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index db56a7c..aea974e 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -726,6 +726,11 @@
 extern const char
     kExperimentalAccessibilityLanguageDetectionDynamicDescription[];
 
+#if BUILDFLAG(IS_ANDROID)
+extern const char kFillRecoveryPasswordName[];
+extern const char kFillRecoveryPasswordDescription[];
+#endif  // BUILDFLAG(IS_ANDROID)
+
 extern const char kForceColorProfileSRGB[];
 extern const char kForceColorProfileP3[];
 extern const char kForceColorProfileRec2020[];
@@ -4541,9 +4546,6 @@
 extern const char kLauncherNudgeSessionResetName[];
 extern const char kLauncherNudgeSessionResetDescription[];
 
-extern const char kLauncherSystemInfoAnswerCardsName[];
-extern const char kLauncherSystemInfoAnswerCardsDescription[];
-
 extern const char kMacAddressRandomizationName[];
 extern const char kMacAddressRandomizationDescription[];
 
diff --git a/chrome/browser/notifications/notification_channels_provider_android.cc b/chrome/browser/notifications/notification_channels_provider_android.cc
index b6814d1..36311e61 100644
--- a/chrome/browser/notifications/notification_channels_provider_android.cc
+++ b/chrome/browser/notifications/notification_channels_provider_android.cc
@@ -373,18 +373,24 @@
   // contain up-to-date information if user has modified notification settings,
   // As a result, schedule an channel update to inform all observers if
   // something has changed.
-  provider->ScheduleGetChannels(
-      /*skip_get_if_cached_channels_are_available=*/false,
-      base::BindOnce(
-          &NotificationChannelsProviderAndroid::UpdateCachedChannelsImpl,
-          provider->weak_factory_.GetWeakPtr(),
-          /*only_initialize_null_cached_channels=*/false, base::DoNothing()));
+
+  provider->EnsureUpdatedSettings(base::DoNothing());
 
   return channels.empty()
              ? nullptr
              : std::make_unique<ChannelsRuleIterator>(std::move(channels));
 }
 
+void NotificationChannelsProviderAndroid::EnsureUpdatedSettings(
+    base::OnceClosure callback) {
+  ScheduleGetChannels(
+      /*skip_get_if_cached_channels_are_available=*/false,
+      base::BindOnce(
+          &NotificationChannelsProviderAndroid::UpdateCachedChannelsImpl,
+          weak_factory_.GetWeakPtr(),
+          /*only_initialize_null_cached_channels=*/false, std::move(callback)));
+}
+
 bool NotificationChannelsProviderAndroid::SetWebsiteSetting(
     const ContentSettingsPattern& primary_pattern,
     const ContentSettingsPattern& secondary_pattern,
diff --git a/chrome/browser/notifications/notification_channels_provider_android.h b/chrome/browser/notifications/notification_channels_provider_android.h
index d09db767..0585df1 100644
--- a/chrome/browser/notifications/notification_channels_provider_android.h
+++ b/chrome/browser/notifications/notification_channels_provider_android.h
@@ -85,6 +85,9 @@
   void Initialize(content_settings::ProviderInterface* pref_provider,
                   TemplateURLService* template_url_service);
 
+  // Forces an update of cached channels and invokes callback upon completion.
+  void EnsureUpdatedSettings(base::OnceClosure callback) override;
+
   // UserModifiableProvider methods.
   std::unique_ptr<content_settings::RuleIterator> GetRuleIterator(
       ContentSettingsType content_type,
diff --git a/chrome/browser/notifications/notification_channels_provider_android_unittest.cc b/chrome/browser/notifications/notification_channels_provider_android_unittest.cc
index 44496cc6..257f21c 100644
--- a/chrome/browser/notifications/notification_channels_provider_android_unittest.cc
+++ b/chrome/browser/notifications/notification_channels_provider_android_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/not_fatal_until.h"
+#include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/test/scoped_feature_list.h"
@@ -26,6 +27,7 @@
 #include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/browser/content_settings_provider.h"
 #include "components/content_settings/core/browser/content_settings_rule.h"
+#include "components/content_settings/core/common/content_settings.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
 #include "components/content_settings/core/common/content_settings_utils.h"
 #include "components/content_settings/core/test/content_settings_mock_provider.h"
@@ -950,3 +952,54 @@
   EXPECT_TRUE(profile_->GetPrefs()->GetBoolean(
       prefs::kClearedBlockedSiteNotificationChannels));
 }
+
+TEST_F(NotificationChannelsProviderAndroidTest, EnsureUpdatedSettings) {
+  InitChannelsProvider();
+  content_settings::MockObserver mock_observer;
+  channels_provider_->AddObserver(&mock_observer);
+  ContentSettingsPattern primary_pattern =
+      ContentSettingsPattern::FromURLNoWildcard(GURL("https://abc.com"));
+
+  // Create channel as enabled initially - this should notify the mock observer.
+  EXPECT_CALL(mock_observer,
+              OnContentSettingChanged(primary_pattern,
+                                      ContentSettingsPattern::Wildcard(),
+                                      ContentSettingsType::NOTIFICATIONS));
+
+  channels_provider_->SetWebsiteSetting(
+      primary_pattern, ContentSettingsPattern::Wildcard(),
+      ContentSettingsType::NOTIFICATIONS, base::Value(CONTENT_SETTING_ALLOW),
+      /*constraints=*/{},
+      content_settings::PartitionKey::GetDefaultForTesting());
+  content::RunAllTasksUntilIdle();
+  testing::Mock::VerifyAndClearExpectations(&mock_observer);
+
+  // Emulate user blocking the channel.
+  fake_bridge_->SetChannelStatus("https://abc.com",
+                                 NotificationChannelStatus::BLOCKED);
+
+  // Observer should be notified on invocation of EnsureUpdatedSettings.
+  EXPECT_CALL(mock_observer,
+              OnContentSettingChanged(ContentSettingsPattern::Wildcard(),
+                                      ContentSettingsPattern::Wildcard(),
+                                      ContentSettingsType::NOTIFICATIONS))
+      .Times(1);
+
+  base::RunLoop run_loop;
+  channels_provider_->EnsureUpdatedSettings(run_loop.QuitClosure());
+  run_loop.Run();
+
+  content::RunAllTasksUntilIdle();
+
+  // Since we called `EnsureUpdatedSettings`, now `GetRuleIterator` should
+  // return up-to-date rules.
+  std::unique_ptr<content_settings::RuleIterator> rule_iterator =
+      channels_provider_->GetRuleIterator(
+          ContentSettingsType::NOTIFICATIONS, false /* off_the_record */,
+          content_settings::PartitionKey::GetDefaultForTesting());
+  EXPECT_TRUE(rule_iterator->HasNext());
+  std::unique_ptr<content_settings::Rule> first_rule = rule_iterator->Next();
+  EXPECT_EQ(primary_pattern, first_rule->primary_pattern);
+  EXPECT_EQ(CONTENT_SETTING_BLOCK,
+            content_settings::ValueToContentSetting(first_rule->value));
+}
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
index 22b9399..7a7dd708 100644
--- a/chrome/browser/password_manager/password_change/model_quality_logs_uploader.cc
+++ b/chrome/browser/password_manager/password_change/model_quality_logs_uploader.cc
@@ -18,6 +18,10 @@
 using PageType = optimization_guide::proto::OpenFormResponseData_PageType;
 
 namespace {
+int64_t ComputeRequestLatencyMs(base::Time server_request_start_time) {
+  return (base::Time::Now() - server_request_start_time).InMilliseconds();
+}
+
 FinalModelStatus GetFinalModelStatus(
     const std::optional<optimization_guide::proto::PasswordChangeResponse>&
         response) {
@@ -67,7 +71,8 @@
 
 void ModelQualityLogsUploader::SetOpenFormQuality(
     const optimization_guide::proto::PasswordChangeResponse& response,
-    std::unique_ptr<LoggingData> logging_data) {
+    std::unique_ptr<LoggingData> logging_data,
+    base::Time server_request_start_time) {
   PageType open_form = response.open_form_data().page_type();
   QualityStatus quality_status;
 
@@ -93,12 +98,19 @@
       ->mutable_quality()
       ->mutable_open_form()
       ->set_status(quality_status);
+  // Set latency
+  request.mutable_password_change_submission()
+      ->mutable_quality()
+      ->mutable_open_form()
+      ->set_request_latency_ms(
+          ComputeRequestLatencyMs(server_request_start_time));
   final_log_data_.MergeFrom(request);
 }
 
 void ModelQualityLogsUploader::SetSubmitFormQuality(
     const optimization_guide::proto::PasswordChangeResponse& response,
-    std::unique_ptr<LoggingData> logging_data) {
+    std::unique_ptr<LoggingData> logging_data,
+    base::Time server_request_start_time) {
   QualityStatus quality_status;
   if (response.submit_form_data().dom_node_id_to_click()) {
     quality_status = QualityStatus::
@@ -114,13 +126,20 @@
       ->mutable_quality()
       ->mutable_submit_form()
       ->set_status(quality_status);
+  // Set latency
+  request.mutable_password_change_submission()
+      ->mutable_quality()
+      ->mutable_submit_form()
+      ->set_request_latency_ms(
+          ComputeRequestLatencyMs(server_request_start_time));
   final_log_data_.MergeFrom(request);
 }
 
 void ModelQualityLogsUploader::SetVerifySubmissionQuality(
     const std::optional<optimization_guide::proto::PasswordChangeResponse>&
         response,
-    std::unique_ptr<LoggingData> logging_data) {
+    std::unique_ptr<LoggingData> logging_data,
+    base::Time server_request_start_time) {
   FinalModelStatus final_model_status = GetFinalModelStatus(response);
   QualityStatus quality_status = GetVerifySubmissionQualityStatus(response);
   optimization_guide::proto::LogAiDataRequest request;
@@ -133,6 +152,12 @@
       ->mutable_quality()
       ->mutable_verify_submission()
       ->set_status(quality_status);
+  // Set latency
+  request.mutable_password_change_submission()
+      ->mutable_quality()
+      ->mutable_verify_submission()
+      ->set_request_latency_ms(
+          ComputeRequestLatencyMs(server_request_start_time));
   final_log_data_.MergeFrom(request);
 }
 
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
index 07904ae..62e756ec 100644
--- a/chrome/browser/password_manager/password_change/model_quality_logs_uploader.h
+++ b/chrome/browser/password_manager/password_change/model_quality_logs_uploader.h
@@ -30,18 +30,21 @@
   // Sets quality data for Step=OPEN_FORM_STEP.
   void SetOpenFormQuality(
       const optimization_guide::proto::PasswordChangeResponse& response,
-      std::unique_ptr<LoggingData> logging_data);
+      std::unique_ptr<LoggingData> logging_data,
+      base::Time server_request_start_time);
 
   // Sets quality data for Step=SUBMIT_FORM_STEP.
   void SetSubmitFormQuality(
       const optimization_guide::proto::PasswordChangeResponse& response,
-      std::unique_ptr<LoggingData> logging_data);
+      std::unique_ptr<LoggingData> logging_data,
+      base::Time server_request_start_time);
 
   // Sets quality data for Step=VERIFY_SUBMISSION_STEP.
   void SetVerifySubmissionQuality(
       const std::optional<optimization_guide::proto::PasswordChangeResponse>&
           response,
-      std::unique_ptr<LoggingData> logging_data);
+      std::unique_ptr<LoggingData> logging_data,
+      base::Time server_request_start_time);
 
 #if defined(UNIT_TEST)
   // Used for testing only.
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
index fbf6d8e..afd18fc1 100644
--- 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
@@ -47,23 +47,30 @@
       log.password_change_submission().quality().verify_submission().status(),
       expected_status);
 }
+
+std::unique_ptr<optimization_guide::proto::PasswordChangeSubmissionLoggingData>
+CreateLoggingData() {
+  return std::make_unique<PasswordChangeSubmissionLoggingData>();
+}
 }  // namespace
 
 class ModelQualityLogsUploaderTest : public ChromeRenderViewHostTestHarness {
  public:
-  ModelQualityLogsUploaderTest() = default;
+  ModelQualityLogsUploaderTest()
+      : ChromeRenderViewHostTestHarness(
+            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
   ~ModelQualityLogsUploaderTest() override = default;
 };
 
 TEST_F(ModelQualityLogsUploaderTest, VerifySubmissionSucessLog) {
+  const base::Time fake_start_time = base::Time::Now();
   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.SetVerifySubmissionQuality(std::optional(response),
-                                           std::move(logging_data));
+  logs_uploader.SetVerifySubmissionQuality(
+      std::optional(response), CreateLoggingData(), fake_start_time);
   CheckVerifySubmissionStatus(
       logs_uploader.GetFinalLog(),
       QualityStatus::
@@ -72,13 +79,14 @@
 }
 
 TEST_F(ModelQualityLogsUploaderTest, OpenFormSuccessLog) {
+  const base::Time fake_start_time = base::Time::Now();
   ModelQualityLogsUploader logs_uploader(profile());
-  auto logging_data = std::make_unique<PasswordChangeSubmissionLoggingData>();
   optimization_guide::proto::PasswordChangeResponse response;
   response.mutable_open_form_data()->set_page_type(
       PageType::OpenFormResponseData_PageType_SETTINGS_PAGE);
   response.mutable_open_form_data()->set_dom_node_id_to_click(123);
-  logs_uploader.SetOpenFormQuality(response, std::move(logging_data));
+  logs_uploader.SetOpenFormQuality(response, CreateLoggingData(),
+                                   fake_start_time);
 
   CheckOpenFormStatus(
       logs_uploader.GetFinalLog(),
@@ -87,12 +95,13 @@
 }
 
 TEST_F(ModelQualityLogsUploaderTest, OpenFormElementNotFoundLog) {
+  const base::Time fake_start_time = base::Time::Now();
   ModelQualityLogsUploader logs_uploader(profile());
-  auto logging_data = std::make_unique<PasswordChangeSubmissionLoggingData>();
   optimization_guide::proto::PasswordChangeResponse response;
   response.mutable_open_form_data()->set_page_type(
       PageType::OpenFormResponseData_PageType_SETTINGS_PAGE);
-  logs_uploader.SetOpenFormQuality(response, std::move(logging_data));
+  logs_uploader.SetOpenFormQuality(response, CreateLoggingData(),
+                                   fake_start_time);
   CheckOpenFormStatus(
       logs_uploader.GetFinalLog(),
       QualityStatus::
@@ -100,12 +109,13 @@
 }
 
 TEST_F(ModelQualityLogsUploaderTest, OpenFormUnexpectedStateLog) {
+  const base::Time fake_start_time = base::Time::Now();
   ModelQualityLogsUploader logs_uploader(profile());
-  auto logging_data = std::make_unique<PasswordChangeSubmissionLoggingData>();
   optimization_guide::proto::PasswordChangeResponse response;
   response.mutable_open_form_data()->set_page_type(
       PageType::OpenFormResponseData_PageType_LOG_IN_PAGE);
-  logs_uploader.SetOpenFormQuality(response, std::move(logging_data));
+  logs_uploader.SetOpenFormQuality(response, CreateLoggingData(),
+                                   fake_start_time);
   CheckOpenFormStatus(
       logs_uploader.GetFinalLog(),
       QualityStatus::
@@ -113,11 +123,12 @@
 }
 
 TEST_F(ModelQualityLogsUploaderTest, SubmitFormSuccessLog) {
+  const base::Time fake_start_time = base::Time::Now();
   ModelQualityLogsUploader logs_uploader(profile());
-  auto logging_data = std::make_unique<PasswordChangeSubmissionLoggingData>();
   optimization_guide::proto::PasswordChangeResponse response;
   response.mutable_submit_form_data()->set_dom_node_id_to_click(123);
-  logs_uploader.SetSubmitFormQuality(response, std::move(logging_data));
+  logs_uploader.SetSubmitFormQuality(response, CreateLoggingData(),
+                                     fake_start_time);
   CheckSubmitFormStatus(
       logs_uploader.GetFinalLog(),
       QualityStatus::
@@ -125,10 +136,11 @@
 }
 
 TEST_F(ModelQualityLogsUploaderTest, SubmitFormElementNotFoundLog) {
+  const base::Time fake_start_time = base::Time::Now();
   ModelQualityLogsUploader logs_uploader(profile());
-  auto logging_data = std::make_unique<PasswordChangeSubmissionLoggingData>();
   optimization_guide::proto::PasswordChangeResponse response;
-  logs_uploader.SetSubmitFormQuality(response, std::move(logging_data));
+  logs_uploader.SetSubmitFormQuality(response, CreateLoggingData(),
+                                     fake_start_time);
   CheckSubmitFormStatus(
       logs_uploader.GetFinalLog(),
       QualityStatus::
@@ -136,30 +148,27 @@
 }
 
 TEST_F(ModelQualityLogsUploaderTest, MergeLogsDoesNotOverwrite) {
+  const base::Time fake_start_time = base::Time::Now();
   ModelQualityLogsUploader logs_uploader(profile());
-
-  // Helper function to create a unique_ptr for logging data.
-  auto CreateLoggingData = []() {
-    return std::make_unique<PasswordChangeSubmissionLoggingData>();
-  };
-
   // Set open form data.
   optimization_guide::proto::PasswordChangeResponse open_form_response;
   open_form_response.mutable_open_form_data()->set_page_type(
       PageType::OpenFormResponseData_PageType_SETTINGS_PAGE);
   open_form_response.mutable_open_form_data()->set_dom_node_id_to_click(123);
-  logs_uploader.SetOpenFormQuality(open_form_response, CreateLoggingData());
+  logs_uploader.SetOpenFormQuality(open_form_response, CreateLoggingData(),
+                                   fake_start_time);
 
   // Set submit form data.
   optimization_guide::proto::PasswordChangeResponse submit_form_response;
   submit_form_response.mutable_submit_form_data()->set_dom_node_id_to_click(
       123);
-  logs_uploader.SetSubmitFormQuality(submit_form_response, CreateLoggingData());
+  logs_uploader.SetSubmitFormQuality(submit_form_response, CreateLoggingData(),
+                                     fake_start_time);
 
   // Set verify submission data.
   optimization_guide::proto::PasswordChangeResponse verify_submission_response;
-  logs_uploader.SetVerifySubmissionQuality(verify_submission_response,
-                                           CreateLoggingData());
+  logs_uploader.SetVerifySubmissionQuality(
+      verify_submission_response, CreateLoggingData(), fake_start_time);
 
   // Verify all steps have quality data and it is not overwritten.
   const optimization_guide::proto::LogAiDataRequest final_log =
@@ -179,3 +188,45 @@
           PasswordChangeQuality_StepQuality_SubmissionStatus_ACTION_SUCCESS,
       FinalModelStatus::FINAL_MODEL_STATUS_SUCCESS);
 }
+
+TEST_F(ModelQualityLogsUploaderTest, LatencyRecordedForAllSteps) {
+  const base::Time fake_start_time = base::Time::Now();
+  ModelQualityLogsUploader logs_uploader(profile());
+  constexpr int64_t expected_latency_ms = 2;
+  constexpr base::TimeDelta latency = base::Milliseconds(expected_latency_ms);
+
+  task_environment()->FastForwardBy(latency);
+  // Set open form data.
+  optimization_guide::proto::PasswordChangeResponse open_form_response;
+  logs_uploader.SetOpenFormQuality(open_form_response, CreateLoggingData(),
+                                   fake_start_time);
+
+  // Set submit form data.
+  optimization_guide::proto::PasswordChangeResponse submit_form_response;
+  logs_uploader.SetSubmitFormQuality(submit_form_response, CreateLoggingData(),
+                                     fake_start_time);
+
+  // Set verify submission data.
+  optimization_guide::proto::PasswordChangeResponse verify_submission_response;
+  logs_uploader.SetVerifySubmissionQuality(
+      verify_submission_response, CreateLoggingData(), fake_start_time);
+
+  // Verify that all steps have latency set.
+  const optimization_guide::proto::LogAiDataRequest final_log =
+      logs_uploader.GetFinalLog();
+  EXPECT_EQ(final_log.password_change_submission()
+                .quality()
+                .open_form()
+                .request_latency_ms(),
+            expected_latency_ms);
+  EXPECT_EQ(final_log.password_change_submission()
+                .quality()
+                .open_form()
+                .request_latency_ms(),
+            expected_latency_ms);
+  EXPECT_EQ(final_log.password_change_submission()
+                .quality()
+                .open_form()
+                .request_latency_ms(),
+            expected_latency_ms);
+}
diff --git a/chrome/browser/password_manager/password_change/password_change_submission_verifier.cc b/chrome/browser/password_manager/password_change/password_change_submission_verifier.cc
index eaf0a3c..fd0b92ac 100644
--- a/chrome/browser/password_manager/password_change/password_change_submission_verifier.cc
+++ b/chrome/browser/password_manager/password_change/password_change_submission_verifier.cc
@@ -180,6 +180,7 @@
     std::move(callback_).Run(false);
     return;
   }
+  server_request_start_time_ = base::Time::Now();
   optimization_guide::ModelExecutionCallbackWithLogging<
       optimization_guide::proto::PasswordChangeSubmissionLoggingData>
       wrapper_callback = password_manager::metrics_util::TimeCallback(
@@ -228,8 +229,8 @@
   }
 
   if (logging_data) {
-    logs_uploader_->SetVerifySubmissionQuality(response,
-                                               std::move(logging_data));
+    logs_uploader_->SetVerifySubmissionQuality(
+        response, std::move(logging_data), server_request_start_time_);
   }
 
   if (!response) {
diff --git a/chrome/browser/password_manager/password_change/password_change_submission_verifier.h b/chrome/browser/password_manager/password_change/password_change_submission_verifier.h
index 5eab995..e2c97c6 100644
--- a/chrome/browser/password_manager/password_change/password_change_submission_verifier.h
+++ b/chrome/browser/password_manager/password_change/password_change_submission_verifier.h
@@ -90,6 +90,8 @@
   optimization_guide::proto::PasswordChangeRequest
       check_submission_successful_request_;
 
+  base::Time server_request_start_time_;
+
   base::WeakPtrFactory<PasswordChangeSubmissionVerifier> weak_ptr_factory_{
       this};
 };
diff --git a/chrome/browser/preloading/search_preload/search_preload_browsertest.cc b/chrome/browser/preloading/search_preload/search_preload_browsertest.cc
index 8f48a1f0..61cdcec 100644
--- a/chrome/browser/preloading/search_preload/search_preload_browsertest.cc
+++ b/chrome/browser/preloading/search_preload/search_preload_browsertest.cc
@@ -105,10 +105,14 @@
             {features::kPrefetchPrerenderIntegration, {}},
             {
                 features::kDsePreload2,
+                {},
+            },
+            {
+                features::kDsePreload2OnPress,
                 {
-                    {"kDsePreload2PredictorMouseDown", "true"},
-                    {"kDsePreload2PredictorUpOrDownArrowButton", "true"},
-                    {"kDsePreload2PredictorTouchDown", "true"},
+                    {"kDsePreload2OnPressMouseDown", "true"},
+                    {"kDsePreload2OnPressUpOrDownArrowButton", "true"},
+                    {"kDsePreload2OnPressTouchDown", "true"},
                 },
             },
         },
diff --git a/chrome/browser/preloading/search_preload/search_preload_features.cc b/chrome/browser/preloading/search_preload/search_preload_features.cc
index c4696ab..145c030 100644
--- a/chrome/browser/preloading/search_preload/search_preload_features.cc
+++ b/chrome/browser/preloading/search_preload/search_preload_features.cc
@@ -12,26 +12,35 @@
 
 BASE_FEATURE(kDsePreload2, "DsePreload2", base::FEATURE_DISABLED_BY_DEFAULT);
 
-const base::FeatureParam<bool> kDsePreload2PredictorMouseDown{
-    &kDsePreload2, "kDsePreload2PredictorMouseDown", true};
-const base::FeatureParam<bool> kDsePreload2PredictorUpOrDownArrowButton{
-    &kDsePreload2, "kDsePreload2PredictorUpOrDownArrowButton", true};
-const base::FeatureParam<bool> kDsePreload2PredictorTouchDown{
-    &kDsePreload2, "kDsePreload2PredictorTouchDown", true};
+BASE_FEATURE(kDsePreload2OnPress,
+             "DsePreload2OnPress",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+const base::FeatureParam<bool> kDsePreload2OnPressMouseDown{
+    &kDsePreload2, "kDsePreload2OnPressMouseDown", true};
+const base::FeatureParam<bool> kDsePreload2OnPressUpOrDownArrowButton{
+    &kDsePreload2, "kDsePreload2OnPressUpOrDownArrowButton", true};
+const base::FeatureParam<bool> kDsePreload2OnPressTouchDown{
+    &kDsePreload2, "kDsePreload2OnPressTouchDown", true};
 
 bool IsDsePreload2Enabled() {
   return base::FeatureList::IsEnabled(kDsePreload2);
 }
 
-bool DsePreload2IsPredictorEnabled(
+bool IsDsePreload2OnPressEnabled() {
+  return base::FeatureList::IsEnabled(kDsePreload2) &&
+         base::FeatureList::IsEnabled(kDsePreload2OnPress);
+}
+
+bool DsePreload2OnPressIsPredictorEnabled(
     omnibox::mojom::NavigationPredictor navigation_predictor) {
   switch (navigation_predictor) {
     case omnibox::mojom::NavigationPredictor::kMouseDown:
-      return kDsePreload2PredictorMouseDown.Get();
+      return kDsePreload2OnPressMouseDown.Get();
     case omnibox::mojom::NavigationPredictor::kUpOrDownArrowButton:
-      return kDsePreload2PredictorUpOrDownArrowButton.Get();
+      return kDsePreload2OnPressUpOrDownArrowButton.Get();
     case omnibox::mojom::NavigationPredictor::kTouchDown:
-      return kDsePreload2PredictorTouchDown.Get();
+      return kDsePreload2OnPressTouchDown.Get();
   }
 }
 
diff --git a/chrome/browser/preloading/search_preload/search_preload_features.h b/chrome/browser/preloading/search_preload/search_preload_features.h
index 1ab6e198..e40fb82 100644
--- a/chrome/browser/preloading/search_preload/search_preload_features.h
+++ b/chrome/browser/preloading/search_preload/search_preload_features.h
@@ -15,18 +15,29 @@
 namespace features {
 
 // DefaultSearchEngine Preload v2
+//
+// - Base feature flag for DSE preload 2.
+// - Disables `SearchPrefetch` (DSE v1).
+// - Enables on-suggest preloads.
+//
 // https://crbug.com/394988793
 BASE_DECLARE_FEATURE(kDsePreload2);
 
-extern const base::FeatureParam<bool> kDsePreload2PredictorMouseDown;
-extern const base::FeatureParam<bool> kDsePreload2PredictorUpOrDownArrowButton;
-extern const base::FeatureParam<bool> kDsePreload2PredictorTouchDown;
+// Enables on-press prefetch.
+BASE_DECLARE_FEATURE(kDsePreload2OnPress);
+
+extern const base::FeatureParam<bool> kDsePreload2OnPressMouseDown;
+extern const base::FeatureParam<bool> kDsePreload2OnPressUpOrDownArrowButton;
+extern const base::FeatureParam<bool> kDsePreload2OnPressTouchDown;
 
 // Returns true iff we should enter DsePreload2 code path.
 bool IsDsePreload2Enabled();
+
+// Returns true iff on-press triggers are enabled.
+bool IsDsePreload2OnPressEnabled();
 // Returns true iff we should start prefetch on
 // `SearchPreloadService::OnNavigationLikely()` with a predictor.
-bool DsePreload2IsPredictorEnabled(
+bool DsePreload2OnPressIsPredictorEnabled(
     omnibox::mojom::NavigationPredictor navigation_predictor);
 
 }  // namespace features
diff --git a/chrome/browser/preloading/search_preload/search_preload_pipeline_manager.cc b/chrome/browser/preloading/search_preload/search_preload_pipeline_manager.cc
index 2ce96fc..23ca996b 100644
--- a/chrome/browser/preloading/search_preload/search_preload_pipeline_manager.cc
+++ b/chrome/browser/preloading/search_preload/search_preload_pipeline_manager.cc
@@ -143,7 +143,11 @@
     Profile& profile,
     const AutocompleteMatch& match,
     omnibox::mojom::NavigationPredictor navigation_predictor) {
-  if (!features::DsePreload2IsPredictorEnabled(navigation_predictor)) {
+  if (!features::IsDsePreload2OnPressEnabled()) {
+    return false;
+  }
+
+  if (!features::DsePreload2OnPressIsPredictorEnabled(navigation_predictor)) {
     return false;
   }
 
diff --git a/chrome/browser/privacy_sandbox/incognito/BUILD.gn b/chrome/browser/privacy_sandbox/incognito/BUILD.gn
new file mode 100644
index 0000000..c9e4e28d
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/incognito/BUILD.gn
@@ -0,0 +1,68 @@
+# 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.
+
+group("incognito") {
+  public_deps = [
+    ":incognito_features",
+    ":incognito_surveys",
+    ":tab_observer",
+  ]
+}
+
+group("unit_tests") {
+  testonly = true
+  deps = []
+
+  if (!is_android) {
+    deps += [ ":incognito_surveys_unittest" ]
+  }
+}
+
+source_set("incognito_features") {
+  sources = [ "privacy_sandbox_incognito_features.cc" ]
+  public = [ "privacy_sandbox_incognito_features.h" ]
+  deps = [ "//base" ]
+}
+
+source_set("tab_observer") {
+  sources = [ "privacy_sandbox_incognito_tab_observer.cc" ]
+  public = [ "privacy_sandbox_incognito_tab_observer.h" ]
+  deps = [
+    ":incognito_surveys",
+    "//chrome/browser/profiles",
+    "//chrome/common",
+    "//content/public/browser",
+  ]
+}
+
+source_set("incognito_surveys") {
+  sources = [
+    "privacy_sandbox_incognito_survey_service.cc",
+    "privacy_sandbox_incognito_survey_service_factory.cc",
+  ]
+  public = [
+    "privacy_sandbox_incognito_survey_service.h",
+    "privacy_sandbox_incognito_survey_service_factory.h",
+  ]
+  deps = [
+    ":incognito_features",
+    "//chrome/browser/profiles",
+    "//chrome/browser/ui/hats",
+    "//content/public/browser",
+  ]
+}
+
+if (!is_android) {
+  source_set("incognito_surveys_unittest") {
+    testonly = true
+    sources = [ "privacy_sandbox_incognito_survey_service_unittest.cc" ]
+    deps = [
+      ":incognito_features",
+      ":incognito_surveys",
+      "//chrome/browser/ui/hats:hats",
+      "//chrome/browser/ui/hats:test_support",
+      "//chrome/test:test_support",
+    ]
+  }
+}
diff --git a/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_features.cc b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_features.cc
new file mode 100644
index 0000000..39c8d83
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_features.cc
@@ -0,0 +1,27 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "privacy_sandbox_incognito_features.h"
+
+#include "base/feature_list.h"
+
+namespace privacy_sandbox {
+
+BASE_FEATURE(kPrivacySandboxActSurvey,
+             "PrivacySandboxActSurvey",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+
+const base::FeatureParam<base::TimeDelta> kPrivacySandboxActSurveyDelay{
+    &kPrivacySandboxActSurvey, "delay", base::Seconds(0)};
+
+const base::FeatureParam<bool> kPrivacySandboxActSurveyDelayRandomize{
+    &kPrivacySandboxActSurvey, "delay_randomize", false};
+
+const base::FeatureParam<base::TimeDelta> kPrivacySandboxActSurveyDelayMin{
+    &kPrivacySandboxActSurvey, "delay_min", base::Seconds(0)};
+
+const base::FeatureParam<base::TimeDelta> kPrivacySandboxActSurveyDelayMax{
+    &kPrivacySandboxActSurvey, "delay_max", base::Seconds(0)};
+
+}  // namespace privacy_sandbox
diff --git a/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_features.h b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_features.h
new file mode 100644
index 0000000..3e4cca4
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_features.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.
+
+#include "base/component_export.h"
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+#include "build/build_config.h"
+
+#ifndef CHROME_BROWSER_PRIVACY_SANDBOX_INCOGNITO_PRIVACY_SANDBOX_INCOGNITO_FEATURES_H_
+#define CHROME_BROWSER_PRIVACY_SANDBOX_INCOGNITO_PRIVACY_SANDBOX_INCOGNITO_FEATURES_H_
+
+namespace privacy_sandbox {
+
+// Privacy Sandbox ACT survey. Displayed on a NTP in Incognito.
+BASE_DECLARE_FEATURE(kPrivacySandboxActSurvey);
+
+// Delay of the survey. Used only when delay randomization is disabled.
+extern const base::FeatureParam<base::TimeDelta> kPrivacySandboxActSurveyDelay;
+
+// When set to true, the value of `delay` will be ignored and the delay wll be
+// instead randomized uniformly from the interval [`delay_min`;`delay_max`].
+extern const base::FeatureParam<bool> kPrivacySandboxActSurveyDelayRandomize;
+
+// The minimum value of the randomized delay (inclusive)
+extern const base::FeatureParam<base::TimeDelta>
+    kPrivacySandboxActSurveyDelayMin;
+
+// The maximum value of the randomized delay (inclusive)
+extern const base::FeatureParam<base::TimeDelta>
+    kPrivacySandboxActSurveyDelayMax;
+
+}  // namespace privacy_sandbox
+
+#endif  // CHROME_BROWSER_PRIVACY_SANDBOX_INCOGNITO_PRIVACY_SANDBOX_INCOGNITO_FEATURES_H_
diff --git a/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service.cc b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service.cc
new file mode 100644
index 0000000..22d7c46
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service.cc
@@ -0,0 +1,114 @@
+// 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 "privacy_sandbox_incognito_survey_service.h"
+
+#include "base/metrics/histogram_functions.h"
+#include "base/rand_util.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/hats/hats_service.h"
+#include "chrome/browser/ui/hats/hats_service_factory.h"
+#include "chrome/browser/ui/hats/survey_config.h"
+#include "privacy_sandbox_incognito_features.h"
+
+namespace privacy_sandbox {
+
+PrivacySandboxIncognitoSurveyService::PrivacySandboxIncognitoSurveyService(
+    HatsService* hats_service,
+    const bool is_incognito)
+    : rand_int_callback_(base::BindRepeating(&base::RandInt)),
+      is_incognito_(is_incognito),
+      hats_service_(hats_service) {
+  CHECK(rand_int_callback_);
+}
+
+PrivacySandboxIncognitoSurveyService::~PrivacySandboxIncognitoSurveyService() =
+    default;
+
+void PrivacySandboxIncognitoSurveyService::SetRandIntCallbackForTesting(
+    const PrivacySandboxIncognitoSurveyService::RandIntCallback&&
+        rand_int_callback) {
+  rand_int_callback_ = std::move(rand_int_callback);
+}
+
+bool PrivacySandboxIncognitoSurveyService::IsActSurveyEnabled() {
+  return base::FeatureList::IsEnabled(
+      privacy_sandbox::kPrivacySandboxActSurvey);
+}
+
+base::TimeDelta
+PrivacySandboxIncognitoSurveyService::CalculateActSurveyDelay() {
+  auto randomize_delay =
+      privacy_sandbox::kPrivacySandboxActSurveyDelayRandomize.Get();
+  if (randomize_delay) {
+    base::TimeDelta min =
+        privacy_sandbox::kPrivacySandboxActSurveyDelayMin.Get();
+    base::TimeDelta max =
+        privacy_sandbox::kPrivacySandboxActSurveyDelayMax.Get();
+
+    if (max < min) {
+      // Invalid configuration, do our best to fix it
+      std::swap(min, max);
+    }
+
+    return base::Seconds(
+        rand_int_callback_.Run(min.InSeconds(), max.InSeconds()));
+  } else {
+    return privacy_sandbox::kPrivacySandboxActSurveyDelay.Get();
+  }
+}
+
+std::map<std::string, std::string>
+PrivacySandboxIncognitoSurveyService::GetActSurveyPsd(int delay_ms) {
+  return {{"Survey Trigger Delay", base::NumberToString(delay_ms)}};
+}
+
+void PrivacySandboxIncognitoSurveyService::RecordActSurveyStatus(
+    ActSurveyStatus status) {
+  base::UmaHistogramEnumeration("PrivacySandbox.ActSurvey.Status", status);
+}
+
+void PrivacySandboxIncognitoSurveyService::MaybeShowActSurvey(
+    content::WebContents* web_contents) {
+  if (!is_incognito_) {
+    RecordActSurveyStatus(ActSurveyStatus::kNonIncognitoProfile);
+    return;
+  }
+
+  if (!IsActSurveyEnabled()) {
+    RecordActSurveyStatus(ActSurveyStatus::kFeatureDisabled);
+    return;
+  }
+
+  if (!hats_service_) {
+    RecordActSurveyStatus(ActSurveyStatus::kHatsServiceFailed);
+    return;
+  }
+
+  auto delay = CalculateActSurveyDelay();
+  auto delay_ms = delay.InMilliseconds();
+  hats_service_->LaunchDelayedSurveyForWebContents(
+      kHatsSurveyTriggerPrivacySandboxActSurvey, web_contents, delay_ms,
+      /*product_specific_bits_data=*/{},
+      /*product_specific_string_data=*/
+      GetActSurveyPsd(delay_ms),
+      /*navigation_behaviour=*/
+      HatsService::NavigationBehaviour::REQUIRE_SAME_DOCUMENT,
+      /*success_callback=*/
+      base::BindOnce(&PrivacySandboxIncognitoSurveyService::OnActSurveyShown,
+                     weak_ptr_factory_.GetWeakPtr()),
+      /*failure_callback=*/
+      base::BindOnce(&PrivacySandboxIncognitoSurveyService::OnActSurveyFailure,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void PrivacySandboxIncognitoSurveyService::OnActSurveyShown() {
+  RecordActSurveyStatus(ActSurveyStatus::kSurveyShown);
+}
+
+void PrivacySandboxIncognitoSurveyService::OnActSurveyFailure() {
+  RecordActSurveyStatus(ActSurveyStatus::kSurveyLaunchFailed);
+}
+
+}  // namespace privacy_sandbox
diff --git a/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service.h b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service.h
new file mode 100644
index 0000000..03b4caf1
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service.h
@@ -0,0 +1,86 @@
+// 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_PRIVACY_SANDBOX_INCOGNITO_PRIVACY_SANDBOX_INCOGNITO_SURVEY_SERVICE_H_
+#define CHROME_BROWSER_PRIVACY_SANDBOX_INCOGNITO_PRIVACY_SANDBOX_INCOGNITO_SURVEY_SERVICE_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "build/build_config.h"
+#include "chrome/browser/ui/hats/hats_service.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "content/public/browser/web_contents.h"
+
+namespace privacy_sandbox {
+
+// A service responsible for managing Privacy Sandbox surveys in Incognito.
+class PrivacySandboxIncognitoSurveyService : public KeyedService {
+ public:
+  // Records the survey's status when attempting to surface the ACT survey.
+  //
+  // LINT.IfChange(PrivacySandboxActSurveyStatus)
+  enum class ActSurveyStatus {
+    kSurveyShown = 0,          // Survey was successfully shown.
+    kFeatureDisabled = 1,      // ACT Survey feature disabled.
+    kHatsServiceFailed = 2,    // Could not initialize HaTS service.
+    kSurveyLaunchFailed = 3,   // Survey failed to launch.
+    kNonIncognitoProfile = 4,  // Not inside Incognito.
+    kMaxValue = kNonIncognitoProfile,
+  };
+  // LINT.ThenChange(/tools/metrics/histograms/enums.xml:PrivacySandboxActSurveyStatus)
+
+  using RandIntCallback = base::RepeatingCallback<int(int, int)>;
+
+  explicit PrivacySandboxIncognitoSurveyService(HatsService* hats_service,
+                                                bool is_incognito);
+  ~PrivacySandboxIncognitoSurveyService() override;
+
+  // Called to surface the ACT survey if the conditions are met.
+  void MaybeShowActSurvey(content::WebContents* web_contents);
+
+  // Set a custom RandInt callback for testing.
+  void SetRandIntCallbackForTesting(const RandIntCallback&& rand_int_callback);
+
+ private:
+  // Determines if the ACT survey is enabled.
+  bool IsActSurveyEnabled();
+
+  // Calculates the delay of the ACT survey based on feature params. The delay
+  // is the time between triggering the survey and launching it. Note that
+  // survey loading time is not included in the delay.
+  base::TimeDelta CalculateActSurveyDelay();
+
+  // Construct product specific string data for the ACT survey.
+  std::map<std::string, std::string> GetActSurveyPsd(int delay_ms);
+
+  // Emits the given ACT survey status to UMA.
+  void RecordActSurveyStatus(ActSurveyStatus status);
+
+  void OnActSurveyShown();
+  void OnActSurveyFailure();
+
+  RandIntCallback rand_int_callback_;
+  const bool is_incognito_;
+  raw_ptr<HatsService> hats_service_;
+  base::WeakPtrFactory<PrivacySandboxIncognitoSurveyService> weak_ptr_factory_{
+      this};
+
+  FRIEND_TEST_ALL_PREFIXES(PrivacySandboxIncognitoSurveyServiceTest,
+                           IsActSurveyEnabled_DisabledByDefault);
+  FRIEND_TEST_ALL_PREFIXES(PrivacySandboxIncognitoSurveyServiceTest,
+                           RecordActSurveyStatus_EmitsHistogram);
+  FRIEND_TEST_ALL_PREFIXES(PrivacySandboxIncognitoSurveyServiceTest,
+                           GetActSurveyPsd_ReturnsProperPsd);
+  FRIEND_TEST_ALL_PREFIXES(
+      PrivacySandboxIncognitoSurveyServiceActSurveyDelayTest,
+      CalculateActSurveyDelay_ProperlyCalculatesDelay);
+  FRIEND_TEST_ALL_PREFIXES(
+      PrivacySandboxIncognitoSurveyServiceActSurveyDelayRandomizationTest,
+      CalculateActSurveyDelay_ProperlyCalculatesRandomizedDelay);
+};
+
+}  // namespace privacy_sandbox
+
+#endif  // CHROME_BROWSER_PRIVACY_SANDBOX_INCOGNITO_PRIVACY_SANDBOX_INCOGNITO_SURVEY_SERVICE_H_
diff --git a/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service_factory.cc b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service_factory.cc
new file mode 100644
index 0000000..d4409bfe
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service_factory.cc
@@ -0,0 +1,49 @@
+// 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 "privacy_sandbox_incognito_survey_service_factory.h"
+
+#include "base/no_destructor.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_selections.h"
+#include "chrome/browser/ui/hats/hats_service_factory.h"
+#include "privacy_sandbox_incognito_survey_service.h"
+
+PrivacySandboxIncognitoSurveyServiceFactory*
+PrivacySandboxIncognitoSurveyServiceFactory::GetInstance() {
+  static base::NoDestructor<PrivacySandboxIncognitoSurveyServiceFactory>
+      instance;
+  return instance.get();
+}
+
+privacy_sandbox::PrivacySandboxIncognitoSurveyService*
+PrivacySandboxIncognitoSurveyServiceFactory::GetForProfile(Profile* profile) {
+  return static_cast<privacy_sandbox::PrivacySandboxIncognitoSurveyService*>(
+      GetInstance()->GetServiceForBrowserContext(profile, true));
+}
+
+PrivacySandboxIncognitoSurveyServiceFactory::
+    PrivacySandboxIncognitoSurveyServiceFactory()
+    : ProfileKeyedServiceFactory(
+          "PrivacySandboxIncognitoSurveyService",
+          ProfileSelections::Builder()
+              .WithRegular(ProfileSelection::kOffTheRecordOnly)
+              .Build()) {
+  DependsOn(HatsServiceFactory::GetInstance());
+}
+
+std::unique_ptr<KeyedService> PrivacySandboxIncognitoSurveyServiceFactory::
+    BuildServiceInstanceForBrowserContext(
+        content::BrowserContext* context) const {
+  Profile* profile = Profile::FromBrowserContext(context);
+  HatsService* hats_service = HatsServiceFactory::GetForProfile(profile, true);
+  return std::make_unique<
+      privacy_sandbox::PrivacySandboxIncognitoSurveyService>(
+      hats_service, profile->IsIncognitoProfile());
+}
+
+bool PrivacySandboxIncognitoSurveyServiceFactory::
+    ServiceIsCreatedWithBrowserContext() const {
+  return true;
+}
diff --git a/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service_factory.h b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service_factory.h
new file mode 100644
index 0000000..76c4a223
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service_factory.h
@@ -0,0 +1,33 @@
+// 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_PRIVACY_SANDBOX_INCOGNITO_PRIVACY_SANDBOX_INCOGNITO_SURVEY_SERVICE_FACTORY_H_
+#define CHROME_BROWSER_PRIVACY_SANDBOX_INCOGNITO_PRIVACY_SANDBOX_INCOGNITO_SURVEY_SERVICE_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "chrome/browser/profiles/profile_keyed_service_factory.h"
+#include "privacy_sandbox_incognito_survey_service.h"
+
+class Profile;
+
+class PrivacySandboxIncognitoSurveyServiceFactory
+    : public ProfileKeyedServiceFactory {
+ public:
+  static PrivacySandboxIncognitoSurveyServiceFactory* GetInstance();
+  static privacy_sandbox::PrivacySandboxIncognitoSurveyService* GetForProfile(
+      Profile* profile);
+
+ private:
+  friend base::NoDestructor<PrivacySandboxIncognitoSurveyServiceFactory>;
+  PrivacySandboxIncognitoSurveyServiceFactory();
+  ~PrivacySandboxIncognitoSurveyServiceFactory() override = default;
+
+  // BrowserContextKeyedServiceFactory:
+  std::unique_ptr<KeyedService> BuildServiceInstanceForBrowserContext(
+      content::BrowserContext* context) const override;
+
+  bool ServiceIsCreatedWithBrowserContext() const override;
+};
+
+#endif  // CHROME_BROWSER_PRIVACY_SANDBOX_INCOGNITO_PRIVACY_SANDBOX_INCOGNITO_SURVEY_SERVICE_FACTORY_H_
diff --git a/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service_unittest.cc b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service_unittest.cc
new file mode 100644
index 0000000..935fff5
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service_unittest.cc
@@ -0,0 +1,332 @@
+// 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 "privacy_sandbox_incognito_survey_service.h"
+
+#include <string>
+#include <tuple>
+
+#include "base/test/gmock_callback_support.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_features.h"
+#include "chrome/browser/ui/hats/hats_service.h"
+#include "chrome/browser/ui/hats/hats_service_factory.h"
+#include "chrome/browser/ui/hats/mock_hats_service.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "content/public/test/browser_task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace privacy_sandbox {
+
+namespace {
+
+using ::testing::_;
+using ::testing::Bool;
+using ::testing::Combine;
+using ::testing::ContainerEq;
+using ::testing::Eq;
+using ::testing::Return;
+
+using ::privacy_sandbox::PrivacySandboxIncognitoSurveyService;
+
+using ActSurveyStatus =
+    ::privacy_sandbox::PrivacySandboxIncognitoSurveyService::ActSurveyStatus;
+
+template <size_t I, typename T>
+auto RunOnceClosureAndReturn(T output) {
+  return [output](auto&&... args) -> decltype(auto) {
+    base::test::RunOnceClosure<I>()(args...);
+    return std::move(output);
+  };
+}
+
+class RandIntObserver {
+ public:
+  RandIntObserver() = default;
+
+  int GetMinFromLastCall() { return last_min_; }
+
+  int GetMaxFromLastCall() { return last_max_; }
+
+  void SetReturn(int value) { return_ = value; }
+
+  int RandIntCallback(int min, int max) {
+    last_min_ = min;
+    last_max_ = max;
+    return return_;
+  }
+
+ private:
+  int last_min_ = 0;
+  int last_max_ = 0;
+  int return_ = 0;
+};
+
+class PrivacySandboxIncognitoSurveyServiceTest : public testing::Test {
+ public:
+  PrivacySandboxIncognitoSurveyServiceTest() = default;
+
+  void SetUp() override {
+    original_profile_ = TestingProfile::Builder().Build();
+    profile_ = CreateProfile(original_profile_.get());
+
+    SetUpHatsService();
+
+    feature_list_.InitWithFeaturesAndParameters(GetEnabledFeatures(), {});
+    survey_service_ = std::make_unique<PrivacySandboxIncognitoSurveyService>(
+        hats_service(), profile()->IsIncognitoProfile());
+  }
+
+  void TearDown() override {
+    survey_service_.reset();
+    TearDownHatsService();
+    profile_ = nullptr;
+    original_profile_.reset();
+  }
+
+ protected:
+  virtual raw_ptr<TestingProfile> CreateProfile(
+      TestingProfile* original_profile) {
+    return TestingProfile::Builder().BuildIncognito(original_profile);
+  }
+
+  virtual void SetUpHatsService() {
+    mock_hats_service_ = BuildMockHatsService(profile());
+    ON_CALL(*hats_service(), CanShowAnySurvey(_)).WillByDefault(Return(true));
+  }
+
+  virtual void TearDownHatsService() { mock_hats_service_.reset(); }
+
+  virtual std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() {
+    return {};
+  }
+
+  PrivacySandboxIncognitoSurveyService* survey_service() {
+    return survey_service_.get();
+  }
+  MockHatsService* hats_service() {
+    return static_cast<MockHatsService*>(mock_hats_service_.get());
+  }
+  TestingProfile* profile() { return profile_.get(); }
+
+  void TriggerActSurvey() { survey_service_->MaybeShowActSurvey(nullptr); }
+
+  content::BrowserTaskEnvironment task_environment_{
+      base::test::TaskEnvironment::MainThreadType::UI};
+  std::unique_ptr<TestingProfile> original_profile_;
+  raw_ptr<TestingProfile> profile_;
+  base::HistogramTester histogram_tester_;
+  std::unique_ptr<KeyedService> mock_hats_service_;
+  std::unique_ptr<PrivacySandboxIncognitoSurveyService> survey_service_;
+  base::test::ScopedFeatureList feature_list_;
+};
+
+class PrivacySandboxIncognitoSurveyServiceNullHatsServiceTest
+    : public PrivacySandboxIncognitoSurveyServiceTest {
+  void SetUpHatsService() override {}
+  void TearDownHatsService() override {}
+};
+
+class PrivacySandboxIncognitoSurveyServiceRegularProfileTest
+    : public PrivacySandboxIncognitoSurveyServiceTest {
+  raw_ptr<TestingProfile> CreateProfile(
+      TestingProfile* original_profile) override {
+    return original_profile;
+  }
+};
+
+class PrivacySandboxIncognitoSurveyServiceActSurveyEnabledTest
+    : public PrivacySandboxIncognitoSurveyServiceTest {
+  std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override {
+    return {{kPrivacySandboxActSurvey, {}}};
+  }
+};
+
+}  // namespace
+
+TEST_F(PrivacySandboxIncognitoSurveyServiceTest,
+       IsActSurveyEnabled_DisabledByDefault) {
+  EXPECT_FALSE(survey_service()->IsActSurveyEnabled());
+}
+
+TEST_F(PrivacySandboxIncognitoSurveyServiceTest,
+       RecordActSurveyStatus_EmitsHistogram) {
+  survey_service()->RecordActSurveyStatus(ActSurveyStatus::kFeatureDisabled);
+  histogram_tester_.ExpectBucketCount("PrivacySandbox.ActSurvey.Status",
+                                      ActSurveyStatus::kFeatureDisabled, 1);
+  histogram_tester_.ExpectTotalCount("PrivacySandbox.ActSurvey.Status", 1);
+}
+
+TEST_F(PrivacySandboxIncognitoSurveyServiceTest,
+       GetActSurveyPsd_ReturnsProperPsd) {
+  std::map<std::string, std::string> expected_psd = {
+      {"Survey Trigger Delay", "12345"},
+  };
+
+  EXPECT_THAT(survey_service()->GetActSurveyPsd(12345),
+              ContainerEq(expected_psd));
+}
+
+TEST_F(PrivacySandboxIncognitoSurveyServiceTest,
+       MaybeShowActSurvey_DisabledByDefault) {
+  TriggerActSurvey();
+  histogram_tester_.ExpectBucketCount("PrivacySandbox.ActSurvey.Status",
+                                      ActSurveyStatus::kFeatureDisabled, 1);
+}
+
+TEST_F(PrivacySandboxIncognitoSurveyServiceActSurveyEnabledTest,
+       MaybeShowActSurvey_EmitsSurveyShownHistogram) {
+  EXPECT_CALL(*hats_service(), LaunchDelayedSurveyForWebContents)
+      .WillOnce(RunOnceClosureAndReturn<6>(true));  // run the success callback
+  TriggerActSurvey();
+  histogram_tester_.ExpectBucketCount("PrivacySandbox.ActSurvey.Status",
+                                      ActSurveyStatus::kSurveyShown, 1);
+}
+
+TEST_F(PrivacySandboxIncognitoSurveyServiceActSurveyEnabledTest,
+       MaybeShowActSurvey_EmitsSurveyLaunchedFailedHistogram) {
+  EXPECT_CALL(*hats_service(), LaunchDelayedSurveyForWebContents)
+      .WillOnce(RunOnceClosureAndReturn<7>(true));  // run the failure callback
+  TriggerActSurvey();
+  histogram_tester_.ExpectBucketCount("PrivacySandbox.ActSurvey.Status",
+                                      ActSurveyStatus::kSurveyLaunchFailed, 1);
+}
+
+class PrivacySandboxIncognitoSurveyServiceRegularProfileActSurveyTest
+    : public PrivacySandboxIncognitoSurveyServiceRegularProfileTest {
+  std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override {
+    return {{kPrivacySandboxActSurvey, {}}};
+  }
+};
+
+TEST_F(PrivacySandboxIncognitoSurveyServiceRegularProfileActSurveyTest,
+       MaybeShowActSurvey_NotLaunchedInRegularProfile) {
+  TriggerActSurvey();
+  histogram_tester_.ExpectBucketCount("PrivacySandbox.ActSurvey.Status",
+                                      ActSurveyStatus::kNonIncognitoProfile, 1);
+}
+
+class PrivacySandboxIncognitoSurveyServiceActSurveyWithDelayTest
+    : public PrivacySandboxIncognitoSurveyServiceTest {
+  std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override {
+    return {{kPrivacySandboxActSurvey,
+             {
+                 {kPrivacySandboxActSurveyDelay.name, "16s"},
+             }}};
+  }
+};
+
+TEST_F(PrivacySandboxIncognitoSurveyServiceActSurveyWithDelayTest,
+       MaybeShowActSurvey_LaunchedWithDelay) {
+  std::map<std::string, std::string> expected_psd = {
+      {"Survey Trigger Delay", "16000"}};
+  EXPECT_CALL(
+      *hats_service(),
+      LaunchDelayedSurveyForWebContents(
+          kHatsSurveyTriggerPrivacySandboxActSurvey, _, 16000, _, expected_psd,
+          HatsService::NavigationBehaviour::REQUIRE_SAME_DOCUMENT, _, _, _, _));
+  TriggerActSurvey();
+  testing::Mock::VerifyAndClearExpectations(hats_service());
+}
+
+class PrivacySandboxIncognitoSurveyServiceNullHatsServiceActSurveyTest
+    : public PrivacySandboxIncognitoSurveyServiceNullHatsServiceTest {
+  std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override {
+    return {{kPrivacySandboxActSurvey, {}}};
+  }
+};
+
+TEST_F(PrivacySandboxIncognitoSurveyServiceNullHatsServiceActSurveyTest,
+       MaybeShowActSurvey_EmitsHatsServiceFailedHistogram) {
+  TriggerActSurvey();
+  histogram_tester_.ExpectBucketCount("PrivacySandbox.ActSurvey.Status",
+                                      ActSurveyStatus::kHatsServiceFailed, 1);
+}
+
+class PrivacySandboxIncognitoSurveyServiceActSurveyDelayTest
+    : public PrivacySandboxIncognitoSurveyServiceTest,
+      public testing::WithParamInterface<
+          testing::tuple<std::string, base::TimeDelta>> {
+ protected:
+  std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override {
+    return {{kPrivacySandboxActSurvey,
+             {
+                 {kPrivacySandboxActSurveyDelayRandomize.name, "false"},
+                 {kPrivacySandboxActSurveyDelay.name, GetDelay()},
+                 {kPrivacySandboxActSurveyDelayMin.name, "10s"},
+                 {kPrivacySandboxActSurveyDelayMax.name, "20s"},
+             }}};
+  }
+
+  std::string GetDelay() { return testing::get<0>(GetParam()); }
+
+  base::TimeDelta GetExpectedDelay() { return testing::get<1>(GetParam()); }
+};
+
+TEST_P(PrivacySandboxIncognitoSurveyServiceActSurveyDelayTest,
+       CalculateActSurveyDelay_ProperlyCalculatesDelay) {
+  auto delay = survey_service()->CalculateActSurveyDelay();
+  EXPECT_EQ(delay, GetExpectedDelay());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    PrivacySandboxIncognitoSurveyServiceActSurveyDelayTest,
+    PrivacySandboxIncognitoSurveyServiceActSurveyDelayTest,
+    testing::Values(testing::make_tuple("0s", base::Seconds(0)),
+                    testing::make_tuple("10s", base::Seconds(10)),
+                    testing::make_tuple("1234ms", base::Milliseconds(1234))));
+
+class PrivacySandboxIncognitoSurveyServiceActSurveyDelayRandomizationTest
+    : public PrivacySandboxIncognitoSurveyServiceTest,
+      public testing::WithParamInterface<
+          testing::
+              tuple<std::string, std::string, int, int, int, base::TimeDelta>> {
+ protected:
+  std::vector<base::test::FeatureRefAndParams> GetEnabledFeatures() override {
+    return {{kPrivacySandboxActSurvey,
+             {
+                 {kPrivacySandboxActSurveyDelayRandomize.name, "true"},
+                 {kPrivacySandboxActSurveyDelay.name, "42s"},
+                 {kPrivacySandboxActSurveyDelayMin.name, GetDelayMin()},
+                 {kPrivacySandboxActSurveyDelayMax.name, GetDelayMax()},
+             }}};
+  }
+
+  std::string GetDelayMin() { return testing::get<0>(GetParam()); }
+
+  std::string GetDelayMax() { return testing::get<1>(GetParam()); }
+
+  int GetExpectedDelayMin() { return testing::get<2>(GetParam()); }
+
+  int GetExpectedDelayMax() { return testing::get<3>(GetParam()); }
+
+  int GetMockedDelay() { return testing::get<4>(GetParam()); }
+
+  base::TimeDelta GetExpectedDelay() { return testing::get<5>(GetParam()); }
+};
+
+TEST_P(PrivacySandboxIncognitoSurveyServiceActSurveyDelayRandomizationTest,
+       CalculateActSurveyDelay_ProperlyCalculatesRandomizedDelay) {
+  RandIntObserver rand_int_observer;
+  survey_service()->SetRandIntCallbackForTesting(base::BindRepeating(
+      &RandIntObserver::RandIntCallback, base::Unretained(&rand_int_observer)));
+
+  rand_int_observer.SetReturn(GetMockedDelay());
+  auto delay = survey_service()->CalculateActSurveyDelay();
+
+  EXPECT_EQ(delay, GetExpectedDelay());
+  EXPECT_EQ(rand_int_observer.GetMinFromLastCall(), GetExpectedDelayMin());
+  EXPECT_EQ(rand_int_observer.GetMaxFromLastCall(), GetExpectedDelayMax());
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    PrivacySandboxIncognitoSurveyServiceActSurveyDelayRandomizationTest,
+    PrivacySandboxIncognitoSurveyServiceActSurveyDelayRandomizationTest,
+    testing::Values(
+        testing::make_tuple("0s", "10s", 0, 10, 5, base::Seconds(5)),
+        testing::make_tuple("0", "1m1s", 0, 61, 60, base::Seconds(60)),
+        testing::make_tuple("1234ms", "12345ms", 1, 12, 1, base::Seconds(1)),
+        testing::make_tuple("10s", "5s", 5, 10, 7, base::Seconds(7))));
+
+}  // namespace privacy_sandbox
diff --git a/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_tab_observer.cc b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_tab_observer.cc
new file mode 100644
index 0000000..495c9357
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_tab_observer.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 "privacy_sandbox_incognito_tab_observer.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/webui_url_constants.h"
+#include "privacy_sandbox_incognito_survey_service.h"
+#include "privacy_sandbox_incognito_survey_service_factory.h"
+
+namespace privacy_sandbox {
+
+PrivacySandboxIncognitoTabObserver::PrivacySandboxIncognitoTabObserver(
+    content::WebContents* web_contents)
+    : content::WebContentsObserver(web_contents) {}
+
+PrivacySandboxIncognitoTabObserver::~PrivacySandboxIncognitoTabObserver() =
+    default;
+
+void PrivacySandboxIncognitoTabObserver::DidFinishLoad(
+    content::RenderFrameHost* render_frame_host,
+    const GURL& validated_url) {
+  if (render_frame_host->IsInPrimaryMainFrame() &&
+      IsNewTabPage(validated_url)) {
+    Profile* profile =
+        Profile::FromBrowserContext(web_contents()->GetBrowserContext());
+    auto* survey_service =
+        PrivacySandboxIncognitoSurveyServiceFactory::GetForProfile(profile);
+    if (survey_service) {
+      // We cannot show the ACT survey on DidFinishNavigation, because is uses
+      // the HaTS navigation behavior for delayed surveys which uses
+      // DidFinishNavigation to check for valid navigations.
+      survey_service->MaybeShowActSurvey(web_contents());
+    }
+  }
+}
+
+bool PrivacySandboxIncognitoTabObserver::IsNewTabPage(const GURL& url) {
+  return url == chrome::kChromeUINewTabPageURL ||
+         url == chrome::kChromeUINewTabURL;
+}
+
+}  // namespace privacy_sandbox
diff --git a/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_tab_observer.h b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_tab_observer.h
new file mode 100644
index 0000000..b2447fc
--- /dev/null
+++ b/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_tab_observer.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 CHROME_BROWSER_PRIVACY_SANDBOX_INCOGNITO_PRIVACY_SANDBOX_INCOGNITO_TAB_OBSERVER_H_
+#define CHROME_BROWSER_PRIVACY_SANDBOX_INCOGNITO_PRIVACY_SANDBOX_INCOGNITO_TAB_OBSERVER_H_
+
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace privacy_sandbox {
+
+class PrivacySandboxIncognitoTabObserver : public content::WebContentsObserver {
+ public:
+  explicit PrivacySandboxIncognitoTabObserver(
+      content::WebContents* web_contents);
+  ~PrivacySandboxIncognitoTabObserver() override;
+
+ protected:
+  void DidFinishLoad(content::RenderFrameHost* render_frame_host,
+                     const GURL& validated_url) override;
+
+ private:
+  bool IsNewTabPage(const GURL& url);
+};
+
+}  // namespace privacy_sandbox
+
+#endif  // CHROME_BROWSER_PRIVACY_SANDBOX_INCOGNITO_PRIVACY_SANDBOX_INCOGNITO_TAB_OBSERVER_H_
diff --git a/chrome/browser/profiles/BUILD.gn b/chrome/browser/profiles/BUILD.gn
index 96d8afb..b1f435a 100644
--- a/chrome/browser/profiles/BUILD.gn
+++ b/chrome/browser/profiles/BUILD.gn
@@ -244,6 +244,7 @@
     "//chrome/browser/plus_addresses",
     "//chrome/browser/prefs",
     "//chrome/browser/preloading/search_preload",
+    "//chrome/browser/privacy_sandbox/incognito:incognito_surveys",
     "//chrome/browser/privacy_sandbox/notice:factory",
     "//chrome/browser/reading_list",
     "//chrome/browser/regional_capabilities",
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 8609adc..e8326897 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -155,6 +155,7 @@
 #include "chrome/browser/preloading/prefetch/search_prefetch/search_prefetch_service_factory.h"
 #include "chrome/browser/preloading/search_preload/search_preload_service_factory.h"
 #include "chrome/browser/privacy/privacy_metrics_service_factory.h"
+#include "chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service_factory.h"
 #include "chrome/browser/privacy_sandbox/notice/notice_service_factory.h"
 #include "chrome/browser/privacy_sandbox/privacy_sandbox_service_factory.h"
 #include "chrome/browser/privacy_sandbox/privacy_sandbox_settings_factory.h"
@@ -1176,6 +1177,7 @@
   PrivateNetworkDevicePermissionContextFactory::GetInstance();
 #endif
   PrivacyMetricsServiceFactory::GetInstance();
+  PrivacySandboxIncognitoSurveyServiceFactory::GetInstance();
   PrivacySandboxNoticeServiceFactory::GetInstance();
   PrivacySandboxServiceFactory::GetInstance();
   PrivacySandboxSettingsFactory::GetInstance();
diff --git a/chrome/browser/profiles/profile_keyed_service_browsertest.cc b/chrome/browser/profiles/profile_keyed_service_browsertest.cc
index 04a1c36..72ff470 100644
--- a/chrome/browser/profiles/profile_keyed_service_browsertest.cc
+++ b/chrome/browser/profiles/profile_keyed_service_browsertest.cc
@@ -19,7 +19,6 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/profile_waiter.h"
-#include "components/autofill/core/common/autofill_features.h"
 #include "components/commerce/core/commerce_feature_list.h"
 #include "components/enterprise/buildflags/buildflags.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
@@ -194,7 +193,6 @@
 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
           features::kEnableCertManagementUIV2Write,
 #endif
-          autofill::features::kAutofillAiWithDataSchema,
           network::features::kBrowsingTopics,
           blink::features::kBuiltInAIAPI,
           extensions_features::kForceWebRequestProxyForTest,
@@ -364,7 +362,6 @@
   std::set<std::string> guest_otr_active_services {
     "AlarmManager",
     "AXMainNodeAnnotatorController",
-    "AutofillEntityDataManager",
     "AutocompleteActionPredictor",
     "AutocompleteClassifier",
     "AutocompleteControllerEmitter",
@@ -571,7 +568,6 @@
     "AutocompleteScoringModelService",
 #endif  // BUILDFLAG(BUILD_WITH_TFLITE_LIB)
     "AutofillClientProvider",
-    "AutofillEntityDataManager",
     "AutofillImageFetcher",
     "AutofillPrivateEventRouter",
     "AutofillStrikeDatabase",
diff --git a/chrome/browser/search_engines/template_url_service_sync_unittest.cc b/chrome/browser/search_engines/template_url_service_sync_unittest.cc
index 7b0e68b..ff29fd1 100644
--- a/chrome/browser/search_engines/template_url_service_sync_unittest.cc
+++ b/chrome/browser/search_engines/template_url_service_sync_unittest.cc
@@ -7239,3 +7239,104 @@
   histogram_tester.ExpectUniqueSample(
       "Sync.SearchEngine.HasLocalDataDuringStopSyncing2", true, 1);
 }
+
+TEST_F(TemplateURLServiceSyncTestWithSeparateLocalAndAccountSearchEngines,
+       ShouldLogCommittedChangesUponSyncStart) {
+  syncer::SyncDataList initial_data;
+  // This should lead to a deletion commit (because of empty url).
+  syncer::SyncData sync_data1 =
+      TemplateURLService::CreateSyncDataFromTemplateURLData(
+          CreateTestTemplateURL(u"key1", /*url=*/"https://key1.com", "guid1")
+              ->data());
+  const_cast<sync_pb::EntitySpecifics&>(sync_data1.GetSpecifics())
+      .mutable_search_engine()
+      ->set_url("");
+  initial_data.push_back(sync_data1);
+
+  // This should lead to an update commit.
+  TemplateURLData data2 =
+      CreateTestTemplateURL(u"key2", "http://key2.com", "guid2")->data();
+  data2.input_encodings = {"UTF-8", "UTF-16", "UTF-16", "UTF-8"};
+  initial_data.push_back(
+      TemplateURLService::CreateSyncDataFromTemplateURLData(data2));
+
+  base::HistogramTester histogram_tester;
+  model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES, initial_data,
+                                    PassProcessor());
+
+  ASSERT_EQ(processor()->change_list_size(), 2u);
+  ASSERT_TRUE(processor()->contains_guid("guid1"));
+  ASSERT_EQ(processor()->change_for_guid("guid1").change_type(),
+            syncer::SyncChange::ACTION_DELETE);
+  ASSERT_FALSE(model()->GetTemplateURLForGUID("guid1"));
+  histogram_tester.ExpectUniqueSample(
+      "Sync.SearchEngine.ChangesCommittedUponSyncStart_Deleted", /*sample=*/1,
+      /*expected_bucket_count=*/1);
+  ASSERT_TRUE(processor()->contains_guid("guid2"));
+  ASSERT_EQ(processor()->change_for_guid("guid2").change_type(),
+            syncer::SyncChange::ACTION_UPDATE);
+  ASSERT_THAT(model()->GetTemplateURLForGUID("guid2"),
+              Pointee(Property(&TemplateURL::input_encodings,
+                               std::vector<std::string>{"UTF-8", "UTF-16"})));
+  histogram_tester.ExpectUniqueSample(
+      "Sync.SearchEngine.ChangesCommittedUponSyncStart_Updated", /*sample=*/1,
+      /*expected_bucket_count=*/1);
+
+  // No adds are committed upon sync start.
+  histogram_tester.ExpectUniqueSample(
+      "Sync.SearchEngine.ChangesCommittedUponSyncStart_Added", /*sample=*/0,
+      /*expected_bucket_count=*/1);
+}
+
+TEST_F(TemplateURLServiceSyncTestWithSeparateLocalAndAccountSearchEngines,
+       ShouldLogCommittedChangesUponIncrementalUpdate) {
+  model()->MergeDataAndStartSyncing(syncer::SEARCH_ENGINES,
+                                    syncer::SyncDataList{}, PassProcessor());
+
+  syncer::SyncChangeList changes;
+  // This should lead to an deletion commit (because of empty url).
+  syncer::SyncData sync_data1 =
+      TemplateURLService::CreateSyncDataFromTemplateURLData(
+          CreateTestTemplateURL(u"key1", /*url=*/"https://key1.com", "guid1")
+              ->data());
+  const_cast<sync_pb::EntitySpecifics&>(sync_data1.GetSpecifics())
+      .mutable_search_engine()
+      ->set_url("");
+  changes.emplace_back(FROM_HERE, syncer::SyncChange::ACTION_UPDATE,
+                       sync_data1);
+  // This should lead to an update commit.
+  TemplateURLData data2 =
+      CreateTestTemplateURL(u"key2", "http://key2.com", "guid2")->data();
+  data2.input_encodings = {"UTF-8", "UTF-16", "UTF-16", "UTF-8"};
+  changes.push_back(CreateTestSyncChange(syncer::SyncChange::ACTION_UPDATE,
+                                         std::make_unique<TemplateURL>(data2)));
+
+  base::HistogramTester histogram_tester;
+  model()->ProcessSyncChanges(FROM_HERE, changes);
+
+  ASSERT_EQ(processor()->change_list_size(), 2u);
+  ASSERT_TRUE(processor()->contains_guid("guid1"));
+  ASSERT_EQ(processor()->change_for_guid("guid1").change_type(),
+            syncer::SyncChange::ACTION_DELETE);
+  ASSERT_FALSE(model()->GetTemplateURLForGUID("guid1"));
+  histogram_tester.ExpectUniqueSample(
+      "Sync.SearchEngine.ChangesCommittedUponIncrementalUpdate_Deleted",
+      /*sample=*/1,
+      /*expected_bucket_count=*/1);
+  ASSERT_TRUE(processor()->contains_guid("guid2"));
+  ASSERT_EQ(processor()->change_for_guid("guid2").change_type(),
+            syncer::SyncChange::ACTION_UPDATE);
+  ASSERT_THAT(model()->GetTemplateURLForGUID("guid2"),
+              Pointee(Property(&TemplateURL::input_encodings,
+                               std::vector<std::string>{"UTF-8", "UTF-16"})));
+  histogram_tester.ExpectUniqueSample(
+      "Sync.SearchEngine.ChangesCommittedUponIncrementalUpdate_Updated",
+      /*sample=*/1,
+      /*expected_bucket_count=*/1);
+
+  // No adds are committed upon sync start.
+  histogram_tester.ExpectUniqueSample(
+      "Sync.SearchEngine.ChangesCommittedUponIncrementalUpdate_Added",
+      /*sample=*/0,
+      /*expected_bucket_count=*/1);
+}
diff --git a/chrome/browser/supervised_user/BUILD.gn b/chrome/browser/supervised_user/BUILD.gn
index cbfbee6..deafe76 100644
--- a/chrome/browser/supervised_user/BUILD.gn
+++ b/chrome/browser/supervised_user/BUILD.gn
@@ -15,11 +15,14 @@
     "//chrome/browser",
     "//chrome/browser/content_settings:content_settings_factory",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/sync",
     "//chrome/common:constants",
     "//components/content_settings/core/browser",
     "//components/prefs",
+    "//components/signin/public/identity_manager",
     "//components/supervised_user/core/browser",
     "//components/supervised_user/core/common",
+    "//components/supervised_user/test_support",
   ]
   if (is_android) {
     sources += [ "android/supervised_user_settings_test_bridge.cc" ]
diff --git a/chrome/browser/supervised_user/classify_url_navigation_throttle_unittest.cc b/chrome/browser/supervised_user/classify_url_navigation_throttle_unittest.cc
index 8da3f1c..4cec41c 100644
--- a/chrome/browser/supervised_user/classify_url_navigation_throttle_unittest.cc
+++ b/chrome/browser/supervised_user/classify_url_navigation_throttle_unittest.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #include "chrome/browser/supervised_user/supervised_user_test_util.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
 #include "components/safe_search_api/fake_url_checker_client.h"
 #include "components/supervised_user/core/browser/supervised_user_preferences.h"
 #include "components/supervised_user/core/browser/supervised_user_service.h"
@@ -56,32 +57,35 @@
 
 class MockSupervisedUserURLFilter : public SupervisedUserURLFilter {
  public:
-  explicit MockSupervisedUserURLFilter(PrefService& prefs)
-      : SupervisedUserURLFilter(prefs,
-                                std::make_unique<FakeURLFilterDelegate>()) {}
+  explicit MockSupervisedUserURLFilter(
+      PrefService& prefs,
+      std::unique_ptr<SupervisedUserURLFilter::Delegate> delegate)
+      : SupervisedUserURLFilter(prefs, std::move(delegate)) {}
   MOCK_METHOD(bool,
               RunAsyncChecker,
               (const GURL& url, ResultCallback callback),
               (const));
 };
-}  // namespace
 
 class ClassifyUrlNavigationThrottleTest
     : public ChromeRenderViewHostTestHarness {
  public:
   void SetUp() override {
     ChromeRenderViewHostTestHarness::SetUp();
-
-    SupervisedUserService* service =
-        SupervisedUserServiceFactory::GetForProfile(profile());
-    service->SetURLFilterForTesting(
-        std::make_unique<MockSupervisedUserURLFilter>(*profile()->GetPrefs()));
     // In unit tests, the service is not automatically initialized but is
     // required to ensure proper flow of preference values.
-    service->Init();
+    SupervisedUserServiceFactory::GetForProfile(profile())->Init();
     EnableParentalControls(*profile()->GetPrefs());
   }
 
+  TestingProfile::TestingFactories GetTestingFactories() const override {
+    return {TestingProfile::TestingFactory{
+        SupervisedUserServiceFactory::GetInstance(),
+        base::BindRepeating(
+            &supervised_user_test_util::BuildSupervisedUserService<
+                MockSupervisedUserURLFilter>)}};
+  }
+
   std::unique_ptr<content::MockNavigationThrottleRegistry>
   CreateNavigationThrottle(const std::vector<GURL> redirects) {
     CHECK_GT(redirects.size(), 0U) << "At least one url is required";
@@ -133,7 +137,8 @@
   }
 
   MockSupervisedUserURLFilter* GetSupervisedUserURLFilter() {
-    // Cast is safe, see this::SetUp() to see how the object was created.
+    // Cast is safe, see this::GetTestingFactories() to see how the object was
+    // created.
     return static_cast<MockSupervisedUserURLFilter*>(
         SupervisedUserServiceFactory::GetForProfile(profile())->GetURLFilter());
   }
@@ -227,21 +232,17 @@
 
 TEST_F(ClassifyUrlNavigationThrottleTest,
        BlockedMatureSitesRecordedInBlockSafeSitesBucket) {
-  std::unique_ptr<MockSupervisedUserURLFilter> mock_url_filter =
-      std::make_unique<MockSupervisedUserURLFilter>(*profile()->GetPrefs());
-  ON_CALL(*mock_url_filter, RunAsyncChecker(testing::_, testing::_))
+  ON_CALL(*GetSupervisedUserURLFilter(),
+          RunAsyncChecker(testing::_, testing::_))
       .WillByDefault([](const GURL& url,
                         MockSupervisedUserURLFilter::ResultCallback callback) {
         std::move(callback).Run({url, FilteringBehavior::kBlock,
                                  FilteringBehaviorReason::ASYNC_CHECKER});
         return true;
       });
-  EXPECT_CALL(*mock_url_filter, RunAsyncChecker(GURL(kExampleURL), testing::_))
+  EXPECT_CALL(*GetSupervisedUserURLFilter(),
+              RunAsyncChecker(GURL(kExampleURL), testing::_))
       .Times(1);
-
-  SupervisedUserServiceFactory::GetForProfile(profile())
-      ->SetURLFilterForTesting(std::move(mock_url_filter));
-
   std::unique_ptr<content::MockNavigationThrottleRegistry> registry =
       CreateNavigationThrottle(GURL(kExampleURL));
   ASSERT_EQ(content::NavigationThrottle::DEFER,
@@ -793,4 +794,6 @@
                          [](const testing::TestParamInfo<TestCase>& info) {
                            return info.param.name;
                          });
+
+}  // namespace
 }  // namespace supervised_user
diff --git a/chrome/browser/supervised_user/supervised_user_service_factory.cc b/chrome/browser/supervised_user/supervised_user_service_factory.cc
index 099637f..51dcd5d 100644
--- a/chrome/browser/supervised_user/supervised_user_service_factory.cc
+++ b/chrome/browser/supervised_user/supervised_user_service_factory.cc
@@ -76,7 +76,8 @@
       *SupervisedUserSettingsServiceFactory::GetInstance()->GetForKey(
           profile->GetProfileKey()),
       SyncServiceFactory::GetInstance()->GetForProfile(profile),
-      std::make_unique<FilterDelegateImpl>(),
+      std::make_unique<supervised_user::SupervisedUserURLFilter>(
+          *profile->GetPrefs(), std::make_unique<FilterDelegateImpl>()),
       std::make_unique<SupervisedUserServicePlatformDelegate>(*profile));
 }
 
diff --git a/chrome/browser/supervised_user/supervised_user_test_util.h b/chrome/browser/supervised_user/supervised_user_test_util.h
index 03c98c8a..4efaaf9 100644
--- a/chrome/browser/supervised_user/supervised_user_test_util.h
+++ b/chrome/browser/supervised_user/supervised_user_test_util.h
@@ -5,12 +5,22 @@
 #ifndef CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_TEST_UTIL_H_
 #define CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_TEST_UTIL_H_
 
+#include <memory>
 #include <string>
 
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/profiles/profile_key.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chrome/browser/supervised_user/android/supervised_user_service_platform_delegate.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
+#include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
+#include "chrome/browser/sync/sync_service_factory.h"
+#include "components/keyed_service/core/keyed_service_factory.h"
 #include "components/supervised_user/core/common/supervised_user_constants.h"
+#include "components/supervised_user/test_support/supervised_user_url_filter_test_utils.h"
+#include "content/public/browser/storage_partition.h"
 
 struct AccountInfo;
-class Profile;
 
 namespace supervised_user_test_util {
 
@@ -50,6 +60,27 @@
 void SetWebFilterType(const Profile* profile,
                       supervised_user::WebFilterType web_filter_type);
 
+// Handy utility to use with TestingProfile::TestingFactory, that attaches to
+// the testing profile a supervised user service instance with substituted url
+// filtering functionalities.
+template <typename URLFilter,
+          typename URLFilterDelegate = supervised_user::FakeURLFilterDelegate>
+std::unique_ptr<KeyedService> BuildSupervisedUserService(
+    content::BrowserContext* browser_context) {
+  Profile* profile = Profile::FromBrowserContext(browser_context);
+  return std::make_unique<supervised_user::SupervisedUserService>(
+      IdentityManagerFactory::GetForProfile(profile),
+      profile->GetDefaultStoragePartition()
+          ->GetURLLoaderFactoryForBrowserProcess(),
+      *profile->GetPrefs(),
+      *SupervisedUserSettingsServiceFactory::GetInstance()->GetForKey(
+          profile->GetProfileKey()),
+      SyncServiceFactory::GetInstance()->GetForProfile(profile),
+      std::make_unique<URLFilter>(*profile->GetPrefs(),
+                                  std::make_unique<URLFilterDelegate>()),
+      std::make_unique<SupervisedUserServicePlatformDelegate>(*profile));
+}
+
 }  // namespace supervised_user_test_util
 
 #endif  // CHROME_BROWSER_SUPERVISED_USER_SUPERVISED_USER_TEST_UTIL_H_
diff --git a/chrome/browser/supervised_user/supervised_user_url_filter_extensions_unittest.cc b/chrome/browser/supervised_user/supervised_user_url_filter_extensions_unittest.cc
index 0754e46..5bcef7e 100644
--- a/chrome/browser/supervised_user/supervised_user_url_filter_extensions_unittest.cc
+++ b/chrome/browser/supervised_user/supervised_user_url_filter_extensions_unittest.cc
@@ -7,24 +7,16 @@
 
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
-#include "chrome/browser/profiles/profile_key.h"
-#include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/supervised_user/android/supervised_user_service_platform_delegate.h"
 #include "chrome/browser/supervised_user/supervised_user_browser_utils.h"
 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
-#include "chrome/browser/supervised_user/supervised_user_settings_service_factory.h"
 #include "chrome/browser/supervised_user/supervised_user_test_util.h"
-#include "chrome/browser/sync/sync_service_factory.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
-#include "components/keyed_service/core/keyed_service_factory.h"
 #include "components/safe_search_api/fake_url_checker_client.h"
 #include "components/supervised_user/core/browser/supervised_user_preferences.h"
 #include "components/supervised_user/core/browser/supervised_user_service.h"
-#include "components/supervised_user/core/browser/supervised_user_settings_service.h"
 #include "components/supervised_user/core/browser/supervised_user_url_filter.h"
 #include "components/supervised_user/core/browser/supervised_user_utils.h"
-#include "content/public/browser/storage_partition.h"
 #include "extensions/buildflags/buildflags.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -42,29 +34,17 @@
   }
 };
 
-std::unique_ptr<KeyedService> BuildSupervisedUserService(
-    content::BrowserContext* browser_context) {
-  Profile* profile = Profile::FromBrowserContext(browser_context);
-  return std::make_unique<SupervisedUserService>(
-      IdentityManagerFactory::GetForProfile(profile),
-      profile->GetDefaultStoragePartition()
-          ->GetURLLoaderFactoryForBrowserProcess(),
-      *profile->GetPrefs(),
-      *SupervisedUserSettingsServiceFactory::GetInstance()->GetForKey(
-          profile->GetProfileKey()),
-      SyncServiceFactory::GetInstance()->GetForProfile(profile),
-      std::make_unique<FakeURLFilterDelegate>(),
-      std::make_unique<SupervisedUserServicePlatformDelegate>(*profile));
-}
-
 class SupervisedUserURLFilterExtensionsTest
     : public ChromeRenderViewHostTestHarness {
  protected:
   std::unique_ptr<TestingProfile> CreateTestingProfile() override {
     return TestingProfile::Builder()
         .SetIsSupervisedProfile()
-        .AddTestingFactory(SupervisedUserServiceFactory::GetInstance(),
-                           base::BindRepeating(&BuildSupervisedUserService))
+        .AddTestingFactory(
+            SupervisedUserServiceFactory::GetInstance(),
+            base::BindRepeating(
+                &supervised_user_test_util::BuildSupervisedUserService<
+                    SupervisedUserURLFilter, FakeURLFilterDelegate>))
         .Build();
   }
 
diff --git a/chrome/browser/sync/test/integration/single_client_common_sync_test.cc b/chrome/browser/sync/test/integration/single_client_common_sync_test.cc
index 8fc437b..b15a942 100644
--- a/chrome/browser/sync/test/integration/single_client_common_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_common_sync_test.cc
@@ -267,8 +267,7 @@
 
   // BOOKMARKS has no unsynced data.
   EXPECT_FALSE(GetClient(0)
-                   ->GetTypesWithUnsyncedData({syncer::BOOKMARKS})
-                   .Get()
+                   ->GetTypesWithUnsyncedDataAndWait({syncer::BOOKMARKS})
                    .contains(syncer::BOOKMARKS));
 
   ASSERT_TRUE(bookmarks_helper::BookmarkModelMatchesFakeServerChecker(
@@ -283,8 +282,7 @@
 
   // BOOKMARKS now has local changes not yet synced with the server.
   EXPECT_TRUE(GetClient(0)
-                  ->GetTypesWithUnsyncedData({syncer::BOOKMARKS})
-                  .Get()
+                  ->GetTypesWithUnsyncedDataAndWait({syncer::BOOKMARKS})
                   .contains(syncer::BOOKMARKS));
 
   // Clear the error and wait for the local changes to be committed.
@@ -296,8 +294,7 @@
 
   // BOOKMARKS has no unsynced data.
   EXPECT_FALSE(GetClient(0)
-                   ->GetTypesWithUnsyncedData({syncer::BOOKMARKS})
-                   .Get()
+                   ->GetTypesWithUnsyncedDataAndWait({syncer::BOOKMARKS})
                    .contains(syncer::BOOKMARKS));
 }
 #endif  // BUILDFLAG(IS_ANDROID)
@@ -312,8 +309,7 @@
 
   // THEMES has no unsynced data.
   ASSERT_FALSE(GetClient(0)
-                   ->GetTypesWithUnsyncedData({syncer::THEMES})
-                   .Get()
+                   ->GetTypesWithUnsyncedDataAndWait({syncer::THEMES})
                    .contains(syncer::THEMES));
 
   // Force theme saved to the account to be unsynced.
@@ -325,8 +321,7 @@
 
   // THEMES now has local changes not yet synced with the server.
   EXPECT_TRUE(GetClient(0)
-                  ->GetTypesWithUnsyncedData({syncer::THEMES})
-                  .Get()
+                  ->GetTypesWithUnsyncedDataAndWait({syncer::THEMES})
                   .contains(syncer::THEMES));
 
   // Http error is not an auth error.
@@ -339,8 +334,7 @@
 
   // THEMES has no unsynced data.
   EXPECT_FALSE(GetClient(0)
-                   ->GetTypesWithUnsyncedData({syncer::THEMES})
-                   .Get()
+                   ->GetTypesWithUnsyncedDataAndWait({syncer::THEMES})
                    .contains(syncer::THEMES));
 }
 
@@ -356,8 +350,7 @@
 
   // THEMES has no unsynced data.
   ASSERT_FALSE(GetClient(0)
-                   ->GetTypesWithUnsyncedData({syncer::THEMES})
-                   .Get()
+                   ->GetTypesWithUnsyncedDataAndWait({syncer::THEMES})
                    .contains(syncer::THEMES));
 
   // Enter sign-in pending state.
@@ -369,8 +362,7 @@
 
   // THEMES now has local changes not yet synced with the server.
   EXPECT_TRUE(GetClient(0)
-                  ->GetTypesWithUnsyncedData({syncer::THEMES})
-                  .Get()
+                  ->GetTypesWithUnsyncedDataAndWait({syncer::THEMES})
                   .contains(syncer::THEMES));
 
   EXPECT_TRUE(
@@ -382,8 +374,7 @@
 
   // THEMES has no unsynced data.
   EXPECT_FALSE(GetClient(0)
-                   ->GetTypesWithUnsyncedData({syncer::THEMES})
-                   .Get()
+                   ->GetTypesWithUnsyncedDataAndWait({syncer::THEMES})
                    .contains(syncer::THEMES));
 
   EXPECT_FALSE(
diff --git a/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc b/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
index bafdcfa9..17069c18 100644
--- a/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_themes_sync_test.cc
@@ -697,8 +697,7 @@
  public:
   bool HasUnsyncedThemeData() {
     return GetClient(0)
-        ->GetTypesWithUnsyncedData({syncer::THEMES})
-        .Get()
+        ->GetTypesWithUnsyncedDataAndWait({syncer::THEMES})
         .contains(syncer::THEMES);
   }
 
diff --git a/chrome/browser/sync/test/integration/sync_service_impl_harness.cc b/chrome/browser/sync/test/integration/sync_service_impl_harness.cc
index 59c1ab6d..2ce0c687 100644
--- a/chrome/browser/sync/test/integration/sync_service_impl_harness.cc
+++ b/chrome/browser/sync/test/integration/sync_service_impl_harness.cc
@@ -601,12 +601,12 @@
   return SyncCycleSnapshot();
 }
 
-base::test::TestFuture<absl::flat_hash_map<syncer::DataType, size_t>>
-SyncServiceImplHarness::GetTypesWithUnsyncedData(
+absl::flat_hash_map<syncer::DataType, size_t>
+SyncServiceImplHarness::GetTypesWithUnsyncedDataAndWait(
     syncer::DataTypeSet requested_types) const {
   base::test::TestFuture<absl::flat_hash_map<syncer::DataType, size_t>> future;
   service()->GetTypesWithUnsyncedData(requested_types, future.GetCallback());
-  return future;
+  return future.Get();
 }
 
 syncer::LocalDataDescription
diff --git a/chrome/browser/sync/test/integration/sync_service_impl_harness.h b/chrome/browser/sync/test/integration/sync_service_impl_harness.h
index 821858f..f2eb905 100644
--- a/chrome/browser/sync/test/integration/sync_service_impl_harness.h
+++ b/chrome/browser/sync/test/integration/sync_service_impl_harness.h
@@ -193,10 +193,10 @@
   // Returns a snapshot of the current sync session.
   syncer::SyncCycleSnapshot GetLastCycleSnapshot() const;
 
-  // Returns a TestFuture that will be resolved with the set of data types that
-  // have unsynced data.
-  base::test::TestFuture<absl::flat_hash_map<syncer::DataType, size_t>>
-  GetTypesWithUnsyncedData(syncer::DataTypeSet requested_types) const;
+  // Returns the datatypes which have local changes that have not yet been
+  // synced with the server.
+  absl::flat_hash_map<syncer::DataType, size_t> GetTypesWithUnsyncedDataAndWait(
+      syncer::DataTypeSet requested_types) const;
 
   // Retrieves the LocalDataDescription for the specified |data_type|.
   // it assumes the service will provide a unique description for this specific
diff --git a/chrome/browser/touch_to_fill/password_manager/android/internal/BUILD.gn b/chrome/browser/touch_to_fill/password_manager/android/internal/BUILD.gn
index 8daa3b3..1338d27 100644
--- a/chrome/browser/touch_to_fill/password_manager/android/internal/BUILD.gn
+++ b/chrome/browser/touch_to_fill/password_manager/android/internal/BUILD.gn
@@ -59,7 +59,6 @@
     "java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillCoordinator.java",
     "java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillMediator.java",
     "java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillProperties.java",
-    "java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillResourceProvider.java",
     "java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillView.java",
     "java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewBinder.java",
     "java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillViewHolder.java",
diff --git a/chrome/browser/touch_to_fill/password_manager/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillResourceProvider.java b/chrome/browser/touch_to_fill/password_manager/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillResourceProvider.java
deleted file mode 100644
index 3ee73502..0000000
--- a/chrome/browser/touch_to_fill/password_manager/android/internal/java/src/org/chromium/chrome/browser/touch_to_fill/TouchToFillResourceProvider.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2022 The Chromium Authors
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.touch_to_fill;
-
-import androidx.annotation.DrawableRes;
-
-/**
- * Provides functions that choose the correct resource id for touch-to-fill UI. Needed to
- * differentiate upstream and downstream resources. This exists to ensure all implementations of
- * TouchToFillResourceProviderImpl provide the same set of methods. Please use the
- * org.chromium.chrome.browser.touch_to_fill.common.TouchToFillResourceProvider instead.
- *
- * <p>TODO(wnwen): Remove this once downstream no longer depends on it.
- */
-@Deprecated
-public interface TouchToFillResourceProvider {
-    /**
-     * Returns the drawable id to be displayed as a bottom sheet header image.
-     *
-     * @return A {@link DrawableRes} that is never 0.
-     */
-    public @DrawableRes int getHeaderImageDrawableId();
-}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index d84a3ea..d7ec9e6 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -3171,6 +3171,7 @@
       "//chrome/browser/profiles",
       "//chrome/browser/shortcuts",
       "//chrome/browser/ui/webui/app_home",
+      "//chrome/browser/ui/webui/app_home:impl",
       "//chrome/browser/ui/webui/signin:profile",
       "//chrome/browser/ui/webui/signin:profile_impl",
       "//components/capture_mode",
@@ -3187,7 +3188,7 @@
 
       # TODO(crbug.com/364667551): It includes quite a few files from //c/b/ui's
       # subdirectories such as views and webui which are not modularized yet.
-      "//chrome/browser/ui/webui/app_home",
+      "//chrome/browser/ui/webui/app_home:impl",
     ]
 
     if (enable_dice_support) {
@@ -5346,6 +5347,7 @@
       "//chrome/browser/extensions/api/omnibox",
       "//chrome/browser/policy:policy_util",
       "//chrome/browser/profiles",
+      "//chrome/browser/ui/extensions:extension_enable_flow_delegate",
       "//chrome/browser/web_applications",
       "//chrome/browser/web_applications/app_service",
       "//chrome/browser/web_share_target",
@@ -5381,7 +5383,6 @@
       "extensions/extension_action_view_controller.h",
       "extensions/extension_enable_flow.cc",
       "extensions/extension_enable_flow.h",
-      "extensions/extension_enable_flow_delegate.h",
       "extensions/extension_installed_bubble_model.cc",
       "extensions/extension_installed_bubble_model.h",
       "extensions/extension_installed_waiter.cc",
diff --git a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java
index 9579b5e..5d626ae 100644
--- a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java
+++ b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinator.java
@@ -196,7 +196,8 @@
         mControlsEnabledSupplier.set(!mDisabledControlsHolder.hasTokens());
     }
 
-    private List<Rect> collectNonDraggableAreas() {
+    @VisibleForTesting
+    List<Rect> collectNonDraggableAreas() {
         final var areas = new ArrayList<Rect>();
         if (mReloadButtonCoordinator != null && mReloadButtonCoordinator.isVisibile()) {
             areas.add(mReloadButtonCoordinator.getHitRect());
diff --git a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinatorTest.java b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinatorTest.java
index 91e4060..e46c204 100644
--- a/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinatorTest.java
+++ b/chrome/browser/ui/android/web_app_header/java/src/org/chromium/chrome/browser/ui/web_app_header/WebAppHeaderLayoutCoordinatorTest.java
@@ -36,7 +36,6 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.robolectric.annotation.Config;
-import org.robolectric.annotation.LooperMode;
 import org.robolectric.shadows.ShadowLooper;
 
 import org.chromium.base.supplier.ObservableSupplierImpl;
@@ -59,10 +58,11 @@
 import org.chromium.ui.base.TestActivity;
 import org.chromium.ui.util.TokenHolder;
 
+import java.util.HashSet;
+import java.util.List;
 import java.util.Locale;
 
 @RunWith(BaseRobolectricTestRunner.class)
-@LooperMode(LooperMode.Mode.PAUSED)
 @Config(sdk = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
 @EnableFeatures({ChromeFeatureList.ANDROID_MINIMAL_UI_LARGE_SCREEN})
 public class WebAppHeaderLayoutCoordinatorTest {
@@ -213,6 +213,31 @@
                 mActivity.findViewById(R.id.back_button).getVisibility());
     }
 
+    private void verifyHeaderContainsNonDraggableAreas(List<Rect> expectedNonDraggableAreas) {
+        var expectedNonDraggableSet = new HashSet<>(expectedNonDraggableAreas);
+        var headerView = mContentView.findViewById(R.id.web_app_header_layout);
+        var nonDraggableAreas = headerView.getSystemGestureExclusionRects();
+        assertEquals(
+                "Header non-draggable areas size should match expected areas size",
+                expectedNonDraggableAreas.size(),
+                nonDraggableAreas.size());
+
+        for (var rect : nonDraggableAreas) {
+            assertTrue(
+                    String.format(
+                            Locale.US,
+                            "Header should not contain non-draggable area=%s",
+                            rect.toString()),
+                    expectedNonDraggableSet.contains(rect));
+        }
+    }
+
+    private void verifyWholeHeaderIsDraggable() {
+        // Empty rect is expected, because Android SDK keeps previous list of rects if null or empty
+        // list is passed.
+        verifyHeaderContainsNonDraggableAreas(List.of(new Rect(0, 0, 0, 0)));
+    }
+
     @Test
     public void testInitNoAppHeaderState_shouldNotInitCoordinator() {
         when(mDesktopWindowStateManager.getAppHeaderState()).thenReturn(null);
@@ -235,29 +260,33 @@
 
     @Test
     public void testMinUiDisplayMode_shouldMakeMinUiVisible() {
+        // Init header in a window with enough space and wait for flexible area and layout updates
+        // to propagate.
         setupDesktopWindowing(/* isInDesktopWindow= */ true);
         setupDisplayMode(DisplayMode.MINIMAL_UI);
         setupTab(/* isLoading= */ false, /* canGoBack= */ false);
         createCoordinator();
-
-        // Wait for animation to finish and update the view.
         mShadowLooper.idle();
 
+        // Verify min ui controls are in consistent state and non-draggable area is updated.
+        var reloadButton = mActivity.findViewById(R.id.refresh_button);
+        var backButton = mActivity.findViewById(R.id.back_button);
+
         verifyControlsVisibility(View.VISIBLE);
-        assertTrue(
-                "Reload button should be enabled",
-                mActivity.findViewById(R.id.refresh_button).isEnabled());
-        assertFalse(
-                "Back button should be disabled",
-                mActivity.findViewById(R.id.back_button).isEnabled());
+        assertTrue("Reload button should be enabled", reloadButton.isEnabled());
+        assertFalse("Back button should be disabled", backButton.isEnabled());
+        verifyHeaderContainsNonDraggableAreas(mCoordinator.collectNonDraggableAreas());
     }
 
     @Test
     public void testMinUiMinimizeWindow_ControlsDoNotFit_HideControls() {
+        // Init header in a window with enough space and wait for flexible area and layout updates
+        // to propagate.
         setupDesktopWindowing(/* isInDesktopWindow= */ true);
         setupDisplayMode(DisplayMode.MINIMAL_UI);
         setupTab(/* isLoading= */ false, /* canGoBack= */ false);
         createCoordinator();
+        mShadowLooper.idle();
 
         // Emulate minimizing window.
         int flexibleAreaWidth = MIN_HEADER_WIDTH_DP - 1;
@@ -265,9 +294,12 @@
                 new Rect(0, 0, LEFT_INSET + flexibleAreaWidth + RIGHT_INSET, SCREEN_HEIGHT),
                 new Rect(LEFT_INSET, 0, LEFT_INSET + flexibleAreaWidth, SYS_APP_HEADER_HEIGHT),
                 /* isInDesktopWindow= */ true);
-
         notifyHeaderStateChanged();
+        mShadowLooper.idle();
+
+        // Verify buttons are not visible and the whole header is draggable.
         verifyControlsVisibility(View.GONE);
+        verifyWholeHeaderIsDraggable();
     }
 
     @Test
@@ -282,12 +314,16 @@
         setupDisplayMode(DisplayMode.MINIMAL_UI);
         setupTab(/* isLoading= */ false, /* canGoBack= */ false);
         createCoordinator();
+        mShadowLooper.idle();
 
         // Emulate maximizing window.
         setupDesktopWindowing(/* isInDesktopWindow= */ true);
-
         notifyHeaderStateChanged();
+        mShadowLooper.idle();
+
+        // Verify buttons visible and draggable area is updated.
         verifyControlsVisibility(View.VISIBLE);
+        verifyHeaderContainsNonDraggableAreas(mCoordinator.collectNonDraggableAreas());
     }
 
     @Test
diff --git a/chrome/browser/ui/ash/login/webui_login_view.cc b/chrome/browser/ui/ash/login/webui_login_view.cc
index c36a352..8cea258 100644
--- a/chrome/browser/ui/ash/login/webui_login_view.cc
+++ b/chrome/browser/ui/ash/login/webui_login_view.cc
@@ -171,6 +171,7 @@
 
 void WebUILoginView::RequestFocus() {
   web_view_->RequestFocus();
+  web_view_->web_contents()->SetInitialFocus();
 }
 
 web_modal::WebContentsModalDialogHost*
@@ -358,7 +359,8 @@
 }
 
 void WebUILoginView::OnFocusLeavingSystemTray(bool reverse) {
-  AboutToRequestFocusFromTabTraversal(reverse);
+  web_view_->RequestFocus();
+  web_view_->web_contents()->FocusThroughTabTraversal(reverse);
 }
 
 void WebUILoginView::OnSystemTrayBubbleShown() {}
diff --git a/chrome/browser/ui/ash/shelf/BUILD.gn b/chrome/browser/ui/ash/shelf/BUILD.gn
index 75e4d955..abdd26d 100644
--- a/chrome/browser/ui/ash/shelf/BUILD.gn
+++ b/chrome/browser/ui/ash/shelf/BUILD.gn
@@ -69,6 +69,7 @@
 
   public_deps = [
     "//chrome/browser:browser_public_dependencies",
+    "//chrome/browser/ui/extensions:extension_enable_flow_delegate",
     "//chrome/browser/ui/tabs:tab_strip_model_observer",
   ]
 
diff --git a/chrome/browser/ui/autofill/autofill_suggestion_controller_utils.cc b/chrome/browser/ui/autofill/autofill_suggestion_controller_utils.cc
index 660b2091..96159de 100644
--- a/chrome/browser/ui/autofill/autofill_suggestion_controller_utils.cc
+++ b/chrome/browser/ui/autofill/autofill_suggestion_controller_utils.cc
@@ -77,6 +77,7 @@
     case SuggestionType::kFillExistingPlusAddress:
     case SuggestionType::kFillPassword:
     case SuggestionType::kGeneratePasswordEntry:
+    case SuggestionType::kHomeAndWorkAddressEntry:
     case SuggestionType::kIbanEntry:
     case SuggestionType::kInsecureContextPaymentDisabledMessage:
     case SuggestionType::kLoyaltyCardEntry:
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h
index 97b8e2fb..7153227c 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h
@@ -66,12 +66,15 @@
       const BookmarkParentFolder& folder) override;
   void BookmarkAllUserNodesRemoved() override;
 
-  // Rebuilds the main bookmark menu, if it has been marked invalid. Or builds
-  // a bookmark folder submenu on demand. If |recurse| is true, also fills all
-  // submenus recursively.
-  void UpdateMenu(NSMenu* menu,
-                  std::optional<BookmarkParentFolder> folder,
-                  bool recurse);
+  bool IsMenuRoot(NSMenu* menu);
+
+  // Builds the main bookmark menu if it has been marked invalid. Its submenus
+  // will NOT be built recursively.
+  void UpdateRootMenuIfInvalid();
+
+  // Builds a bookmark folder submenu on demand. Submenus of `menu` will NOT be
+  // built recursively.
+  void UpdateNonRootMenu(NSMenu* menu, const BookmarkParentFolder& folder);
 
   // I wish I had a "friend @class" construct.
   bookmarks::BookmarkModel* GetBookmarkModelForTesting();
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm
index 041d342..ba3ecc90 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.mm
@@ -85,22 +85,26 @@
   InvalidateMenu();
 }
 
-void BookmarkMenuBridge::UpdateMenu(NSMenu* menu,
-                                    std::optional<BookmarkParentFolder> folder,
-                                    bool recurse) {
+bool BookmarkMenuBridge::IsMenuRoot(NSMenu* menu) {
   CHECK(menu);
+  return menu == menu_root_;
+}
+
+void BookmarkMenuBridge::UpdateRootMenuIfInvalid() {
+  CHECK(menu_root_);
+  if (!IsMenuValid()) {
+    BuildRootMenu(/*recurse=*/false);
+  }
+}
+
+void BookmarkMenuBridge::UpdateNonRootMenu(NSMenu* menu,
+                                           const BookmarkParentFolder& folder) {
+  CHECK(menu);
+  CHECK(!IsMenuRoot(menu));
   CHECK(controller_);
   CHECK_EQ([menu delegate], controller_);
 
-  if (menu == menu_root_) {
-    if (!IsMenuValid()) {
-      BuildRootMenu(recurse);
-    }
-    return;
-  }
-
-  CHECK(folder);
-  AddChildrenToMenu(folder.value(), menu, recurse);
+  AddChildrenToMenu(folder, menu, /*recurse=*/false);
 
   // Clear the delegate to prevent further refreshes.
   [menu setDelegate:nil];
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm
index 5b645b3..19bb7d01 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm
@@ -52,6 +52,7 @@
     menu_ = [[NSMenu alloc] initWithTitle:@"test"];
 
     bridge_ = std::make_unique<BookmarkMenuBridge>(profile(), menu_);
+    CHECK(bridge_->IsMenuRoot(menu_));
   }
 
   void TearDown() override {
@@ -72,9 +73,7 @@
                 BookmarkMergedSurfaceServiceFactory::GetDefaultFactory()}};
   }
 
-  void UpdateRootMenu() {
-    bridge_->UpdateMenu(menu_, std::nullopt, /*recurse=*/false);
-  }
+  void UpdateRootMenu() { bridge_->UpdateRootMenuIfInvalid(); }
 
   // We are a friend of BookmarkMenuBridge (and have access to
   // protected methods), but none of the classes generated by TEST_F()
@@ -369,7 +368,7 @@
 
   BookmarkParentFolder folder = BookmarkParentFolder::FromFolderNode(node);
 
-  bridge_->UpdateMenu(submenu, folder, /*recurse=*/false);
+  bridge_->UpdateNonRootMenu(submenu, folder);
   // Updating the menu clears the delegate to prevent further updates.
   EXPECT_FALSE([submenu delegate]);
 
@@ -379,8 +378,7 @@
   model->AddURL(node, 0, u"Test Item", GURL("http://test"));
   UpdateRootMenu();
   // There will be a new submenu each time, Cocoa will update it if needed.
-  bridge_->UpdateMenu([[menu_ itemAtIndex:1] submenu], folder,
-                      /*recurse=*/false);
+  bridge_->UpdateNonRootMenu([[menu_ itemAtIndex:1] submenu], folder);
 
   EXPECT_TRUE(MenuItemForNode(bridge_.get(), node->children().front().get()));
 
@@ -397,8 +395,7 @@
   EXPECT_NE(old_menu, [[menu_ itemAtIndex:1] submenu]);
   EXPECT_FALSE([old_menu delegate]);
 
-  bridge_->UpdateMenu([[menu_ itemAtIndex:1] submenu], folder,
-                      /*recurse=*/false);
+  bridge_->UpdateNonRootMenu([[menu_ itemAtIndex:1] submenu], folder);
   EXPECT_TRUE(MenuItemForNode(bridge_.get(), node->children()[0].get()));
   EXPECT_TRUE(MenuItemForNode(bridge_.get(), node->children()[1].get()));
 
@@ -417,8 +414,7 @@
   EXPECT_FALSE(MenuItemForNode(bridge_.get(), node->children()[0].get()));
 
   UpdateRootMenu();
-  bridge_->UpdateMenu([[menu_ itemAtIndex:1] submenu], folder,
-                      /*recurse=*/false);
+  bridge_->UpdateNonRootMenu([[menu_ itemAtIndex:1] submenu], folder);
 
   EXPECT_FALSE(MenuItemForNode(bridge_.get(), removed_node));
   EXPECT_TRUE(MenuItemForNode(bridge_.get(), node->children()[0].get()));
@@ -451,8 +447,8 @@
 
   // The "other" submenu is loaded lazily.
   EXPECT_EQ(0u, [[other submenu] numberOfItems]);
-  bridge_->UpdateMenu([other submenu], BookmarkParentFolder::OtherFolder(),
-                      /*recurse=*/false);
+  bridge_->UpdateNonRootMenu([other submenu],
+                             BookmarkParentFolder::OtherFolder());
 
   ASSERT_GT([[other submenu] numberOfItems], 0);
   EXPECT_NSEQ(@"http://foo/", [[[other submenu] itemAtIndex:0] title]);
@@ -481,8 +477,8 @@
 
   // The "Mobile Bookmarks" submenu is loaded lazily.
   EXPECT_EQ(0u, [[mobile submenu] numberOfItems]);
-  bridge_->UpdateMenu([mobile submenu], BookmarkParentFolder::MobileFolder(),
-                      /*recurse=*/false);
+  bridge_->UpdateNonRootMenu([mobile submenu],
+                             BookmarkParentFolder::MobileFolder());
 
   ASSERT_GT([[mobile submenu] numberOfItems], 0);
   EXPECT_NSEQ(@"http://foo/", [[[mobile submenu] itemAtIndex:0] title]);
diff --git a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm
index 2e5596f1..d2e8a53 100644
--- a/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm
+++ b/chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm
@@ -4,9 +4,9 @@
 
 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.h"
 
-#include <optional>
-
 #import "base/apple/foundation_util.h"
+#include "base/debug/crash_logging.h"
+#include "base/debug/dump_without_crashing.h"
 #include "base/memory/raw_ptr.h"
 #include "base/metrics/user_metrics.h"
 #include "base/strings/sys_string_conversions.h"
@@ -204,22 +204,38 @@
 
 // NSMenu delegate method: called just before menu is displayed.
 - (void)menuNeedsUpdate:(NSMenu*)menu {
-  NSMenuItem* item = GetItemWithSubmenu(menu);
   Profile* profile = _bridge->GetProfile();
   if (!profile) {
     // Unfortunately, we can't update a menu with a dead profile.
     return;
   }
 
+  // The root menu does not have a corresponding bookmark node.
+  if (_bridge->IsMenuRoot(menu)) {
+    _bridge->UpdateRootMenuIfInvalid();
+    return;
+  }
+
+  // Find the bookmark node corresponding to this menu.
   const BookmarkModel* model =
       BookmarkMergedSurfaceServiceFactory::GetForProfile(profile)
           ->bookmark_model();
+  NSMenuItem* item = GetItemWithSubmenu(menu);
   base::Uuid guid = _bridge->TagToGUID([item tag]);
   const BookmarkNode* node = GetNodeByUuid(model, guid);
-  auto folder = node ? std::optional<BookmarkParentFolder>(
-                           BookmarkParentFolder::FromFolderNode(node))
-                     : std::nullopt;
-  _bridge->UpdateMenu(menu, folder, /*recurse=*/false);
+
+  if (!(node && node->is_folder())) {
+    // TODO(crbug.com/417269167): every non-root menu must correspond to a
+    // bookmark folder by construction.
+    SCOPED_CRASH_KEY_NUMBER("BookmarkMenuCocoaController", "NSMenuItem",
+                            [item tag]);
+    SCOPED_CRASH_KEY_STRING32("BookmarkMenuCocoaController", "NSMenuItem",
+                              base::SysNSStringToUTF8([item title]));
+    base::debug::DumpWithoutCrashing();
+    return;
+  }
+
+  _bridge->UpdateNonRootMenu(menu, BookmarkParentFolder::FromFolderNode(node));
 }
 
 - (BOOL)menuHasKeyEquivalent:(NSMenu*)menu
diff --git a/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc b/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
index 5c840481..8b179a84 100644
--- a/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
+++ b/chrome/browser/ui/cookie_controls/cookie_controls_controller_unittest.cc
@@ -283,12 +283,12 @@
 
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -297,7 +297,7 @@
   // Accessing cookies should be notified.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   page_specific_content_settings()->OnBrowsingDataAccessed(
@@ -312,12 +312,12 @@
 
   // Enabling third-party cookies records metrics.
   EXPECT_CALL(*mock(), OnStatusChanged(
-                           CookieControlsState::k3pcsAllowed,
+                           CookieControlsState::kAllowed3pc,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
@@ -346,12 +346,12 @@
   NavigateAndCommit(GURL(kUrl));
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -377,12 +377,12 @@
   NavigateAndCommit(GURL(kUrl));
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -391,12 +391,12 @@
   // Disable all cookies - an OnStatusCallback should get triggered.
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_settings()->SetDefaultCookieSetting(CONTENT_SETTING_BLOCK);
@@ -404,12 +404,12 @@
 
   // Disable cookie blocking for example.com.
   EXPECT_CALL(*mock(), OnStatusChanged(
-                           CookieControlsState::k3pcsAllowed,
+                           CookieControlsState::kAllowed3pc,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
@@ -426,12 +426,12 @@
   NavigateAndCommit(GURL(kUrl));
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -439,12 +439,12 @@
 
   // Disabling cookie blocking for example.com should update the ui.
   EXPECT_CALL(*mock(), OnStatusChanged(
-                           CookieControlsState::k3pcsAllowed,
+                           CookieControlsState::kAllowed3pc,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
@@ -454,12 +454,12 @@
   NavigateAndCommit(GURL("https://somethingelse.com"));
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -468,12 +468,12 @@
   // Visiting example.com should turn protections off.
   NavigateAndCommit(GURL(kUrl));
   EXPECT_CALL(*mock(), OnStatusChanged(
-                           CookieControlsState::k3pcsAllowed,
+                           CookieControlsState::kAllowed3pc,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -482,12 +482,12 @@
   // Enabling example.com again should re-enable protections.
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(true);
@@ -499,12 +499,12 @@
 
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -533,13 +533,13 @@
   tester->NavigateAndCommit(GURL(kUrl));
   EXPECT_CALL(
       incognito_mock,
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(
       incognito_mock,
       OnCookieControlsIconStatusChanged(
-          /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+          /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
           CookieBlocking3pcdStatus::kNotIn3pcd, /*should_highlight=*/false));
   incognito_cookie_controls.Update(incognito_web_contents.get());
   testing::Mock::VerifyAndClearExpectations(mock());
@@ -548,24 +548,24 @@
   // Allow cookies in regular mode should also allow in incognito but enforced
   // through regular mode.
   EXPECT_CALL(*mock(), OnStatusChanged(
-                           CookieControlsState::k3pcsAllowed,
+                           CookieControlsState::kAllowed3pc,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
 
   EXPECT_CALL(
       incognito_mock,
-      OnStatusChanged(CookieControlsState::k3pcsAllowed,
+      OnStatusChanged(CookieControlsState::kAllowed3pc,
                       CookieControlsEnforcement::kEnforcedByCookieSetting,
                       CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
   EXPECT_CALL(
       incognito_mock,
       OnCookieControlsIconStatusChanged(
-          /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+          /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
           CookieBlocking3pcdStatus::kNotIn3pcd, /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
   testing::Mock::VerifyAndClearExpectations(mock());
@@ -588,13 +588,13 @@
 
   EXPECT_CALL(
       incognito_mock,
-      OnStatusChanged(CookieControlsState::k3pcsAllowed,
+      OnStatusChanged(CookieControlsState::kAllowed3pc,
                       CookieControlsEnforcement::kEnforcedByCookieSetting,
                       CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
   EXPECT_CALL(
       incognito_mock,
       OnCookieControlsIconStatusChanged(
-          /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+          /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
           CookieBlocking3pcdStatus::kNotIn3pcd, /*should_highlight=*/false));
   profile()->GetPrefs()->SetInteger(
       prefs::kCookieControlsMode,
@@ -614,12 +614,12 @@
   // created for user bypass.
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsAllowed,
+      OnStatusChanged(CookieControlsState::kAllowed3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -628,12 +628,12 @@
   // Disabling 3PC for example.com again should change status to kEnabled.
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(true);
@@ -647,12 +647,12 @@
 
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
@@ -662,7 +662,7 @@
   // Accessing cookies should be notified.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   page_specific_content_settings()->OnBrowsingDataAccessed(
@@ -675,7 +675,7 @@
   // Reload the page and simulate accessing storage on page load.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
@@ -688,7 +688,7 @@
   // After the second reload and accessing storage, UB should highlight.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/true));
 
@@ -717,12 +717,12 @@
 
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
@@ -734,7 +734,7 @@
 
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
@@ -746,7 +746,7 @@
   // Expect that we attempt to highlight the user bypass icon.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/true));
   NavigateAndCommit(GURL(kUrl));
@@ -802,12 +802,12 @@
   NavigateAndCommit(GURL(kUrl));
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -816,7 +816,7 @@
   // Accessing cookies should be notified.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   page_specific_content_settings()->OnBrowsingDataAccessed(
@@ -828,7 +828,7 @@
   // Reload the page and simulate accessing storage on page load.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
@@ -841,7 +841,7 @@
   // After the second reload and accessing storage, UB should be highlighted.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/true));
   NavigateAndCommit(GURL(kUrl));
@@ -853,12 +853,12 @@
 
   // Enabling third-party cookies records metrics.
   EXPECT_CALL(*mock(), OnStatusChanged(
-                           CookieControlsState::k3pcsAllowed,
+                           CookieControlsState::kAllowed3pc,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
@@ -882,12 +882,12 @@
   NavigateAndCommit(GURL(kUrl));
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -896,7 +896,7 @@
   // Accessing cookies should be notified.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   page_specific_content_settings()->OnBrowsingDataAccessed(
@@ -909,7 +909,7 @@
   // Reload the page and simulate accessing storage on page load.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
@@ -925,7 +925,7 @@
   // The second reload happens with a delay and doesn't highlight.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   NavigateAndCommit(GURL(kUrl));
@@ -937,12 +937,12 @@
 
   // Enabling third-party cookies records metrics.
   EXPECT_CALL(*mock(), OnStatusChanged(
-                           CookieControlsState::k3pcsAllowed,
+                           CookieControlsState::kAllowed3pc,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
@@ -977,12 +977,12 @@
   NavigateAndCommit(GURL("https://highengagement.com"));
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/true));
   cookie_controls()->Update(web_contents());
@@ -991,7 +991,7 @@
   // Site data access should highlight.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/true));
   page_specific_content_settings()->OnBrowsingDataAccessed(
@@ -1013,12 +1013,12 @@
   NavigateAndCommit(GURL("https://somethingelse.com"));
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -1028,7 +1028,7 @@
   // highlight UB and only shows the icon.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   page_specific_content_settings()->OnBrowsingDataAccessed(
@@ -1042,7 +1042,7 @@
   // because the entry point was already highlighted for that site.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   NavigateAndCommit(GURL("https://highengagement.com"));
@@ -1072,12 +1072,12 @@
   NavigateAndCommit(GURL("https://highengagement.com"));
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -1087,7 +1087,7 @@
   // because SAA was requested in the site context.
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   page_specific_content_settings()->OnBrowsingDataAccessed(
@@ -1099,12 +1099,12 @@
 
   // Enabling third-party cookies records metrics.
   EXPECT_CALL(*mock(), OnStatusChanged(
-                           CookieControlsState::k3pcsAllowed,
+                           CookieControlsState::kAllowed3pc,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
@@ -1130,12 +1130,12 @@
   NavigateAndCommit(GURL("https://cool.things.com"));
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -1145,12 +1145,12 @@
   // wildcards in the domain and isn't enforced.
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsAllowed,
+      OnStatusChanged(CookieControlsState::kAllowed3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   hcsm->SetContentSettingCustomScope(
@@ -1166,12 +1166,12 @@
   NavigateAndCommit(GURL("https://cool.things.com"));
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -1181,12 +1181,12 @@
   // the domain and cannot be reset, it is enforced by cookie setting.
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsAllowed,
+      OnStatusChanged(CookieControlsState::kAllowed3pc,
                       CookieControlsEnforcement::kEnforcedByCookieSetting,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   hcsm->SetContentSettingCustomScope(
@@ -1203,12 +1203,12 @@
   NavigateAndCommit(GURL("https://cool.things.com"));
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -1219,13 +1219,13 @@
   // setting.
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsAllowed,
+      OnStatusChanged(CookieControlsState::kAllowed3pc,
                       CookieControlsEnforcement::kEnforcedByCookieSetting,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
 
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   hcsm->SetContentSettingCustomScope(
@@ -1241,12 +1241,12 @@
   NavigateAndCommit(GURL("https://cool.things.com"));
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/false, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/false, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -1257,12 +1257,12 @@
   // setting.
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsAllowed,
+      OnStatusChanged(CookieControlsState::kAllowed3pc,
                       CookieControlsEnforcement::kEnforcedByCookieSetting,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   hcsm->SetContentSettingCustomScope(
@@ -1364,12 +1364,12 @@
 
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -1377,12 +1377,12 @@
 
   // Enable third-party cookies.
   EXPECT_CALL(*mock(), OnStatusChanged(
-                           CookieControlsState::k3pcsAllowed,
+                           CookieControlsState::kAllowed3pc,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->OnCookieBlockingEnabledForSite(false);
@@ -1407,12 +1407,12 @@
       /*blocked=*/true);
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/true));
   cookie_controls()->Update(web_contents());
@@ -1427,12 +1427,12 @@
       /*blocked=*/true);
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -1445,12 +1445,12 @@
 
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -1473,12 +1473,12 @@
 
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -1501,12 +1501,12 @@
 
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -1528,12 +1528,12 @@
 
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -1550,7 +1550,7 @@
   navigation->Commit();
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
   cookie_controls()->Update(web_contents());
@@ -1648,12 +1648,12 @@
 
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsAllowed,
+      OnStatusChanged(CookieControlsState::kAllowed3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
 
@@ -1677,12 +1677,12 @@
 
   EXPECT_CALL(
       *mock(),
-      OnStatusChanged(CookieControlsState::k3pcsBlocked,
+      OnStatusChanged(CookieControlsState::kBlocked3pc,
                       CookieControlsEnforcement::kNoEnforcement,
                       CookieBlocking3pcdStatus::kNotIn3pcd, zero_expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsBlocked,
+                  /*icon_visible=*/true, CookieControlsState::kBlocked3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
 
@@ -1814,7 +1814,7 @@
   tester->NavigateAndCommit(GURL(kUrl));
   incognito_cookie_controls()->Update(incognito_web_contents());
   incognito_cookie_controls()->OnTrackingProtectionsChangedForSite(
-      std::get<0>(GetParam()) == CookieControlsState::kTpPaused);
+      std::get<0>(GetParam()) == CookieControlsState::kPausedTp);
 
   EXPECT_CALL(
       *incognito_mock(),
@@ -1833,7 +1833,7 @@
   tester->NavigateAndCommit(GURL(kUrl));
   incognito_cookie_controls()->Update(incognito_web_contents());
 
-  bool tp_paused = std::get<0>(GetParam()) == CookieControlsState::kTpPaused;
+  bool tp_paused = std::get<0>(GetParam()) == CookieControlsState::kPausedTp;
 
   incognito_cookie_controls()->OnTrackingProtectionsChangedForSite(tp_paused);
 
@@ -1841,12 +1841,12 @@
   // Protections (i.e. the toggle) should still be on iff ACT features are
   // enabled.
   EXPECT_CALL(*mock(), OnStatusChanged(
-                           CookieControlsState::k3pcsAllowed,
+                           CookieControlsState::kAllowed3pc,
                            CookieControlsEnforcement::kNoEnforcement,
                            CookieBlocking3pcdStatus::kNotIn3pcd, expiration()));
   EXPECT_CALL(*mock(),
               OnCookieControlsIconStatusChanged(
-                  /*icon_visible=*/true, CookieControlsState::k3pcsAllowed,
+                  /*icon_visible=*/true, CookieControlsState::kAllowed3pc,
                   CookieBlocking3pcdStatus::kNotIn3pcd,
                   /*should_highlight=*/false));
 
@@ -1869,7 +1869,7 @@
 
 TEST_P(CookieControlsUserBypassTrackingProtectionUiTest,
        RecordUmaToggleMetricWhenActFeaturesAreActive) {
-  bool tp_paused = std::get<0>(GetParam()) == CookieControlsState::kTpPaused;
+  bool tp_paused = std::get<0>(GetParam()) == CookieControlsState::kPausedTp;
   bool ipp_enabled = std::get<1>(GetParam()) != ActFeatureState::kIppDisabled;
   bool fpp_enabled = std::get<1>(GetParam()) != ActFeatureState::kFppDisabled;
   incognito_cookie_controls()->Update(web_contents());
@@ -1926,7 +1926,7 @@
     const testing::TestParamInfo<
         CookieControlsUserBypassTrackingProtectionUiTest::ParamType>& info) {
   std::stringstream name;
-  if (std::get<0>(info.param) == CookieControlsState::kTpActive) {
+  if (std::get<0>(info.param) == CookieControlsState::kActiveTp) {
     name << "TpActive";
   } else {
     name << "TpPaused";
@@ -1950,8 +1950,8 @@
     All,
     CookieControlsUserBypassTrackingProtectionUiTest,
     testing::Combine(
-        /*controls_state*/ testing::Values(CookieControlsState::kTpActive,
-                                           CookieControlsState::kTpPaused),
+        /*controls_state*/ testing::Values(CookieControlsState::kActiveTp,
+                                           CookieControlsState::kPausedTp),
         testing::Values(ActFeatureState::kActFeaturesEnabled,
                         ActFeatureState::kIppDisabled,
                         ActFeatureState::kFppDisabled)),
diff --git a/chrome/browser/ui/extensions/BUILD.gn b/chrome/browser/ui/extensions/BUILD.gn
new file mode 100644
index 0000000..eac26ea
--- /dev/null
+++ b/chrome/browser/ui/extensions/BUILD.gn
@@ -0,0 +1,11 @@
+# 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("//extensions/buildflags/buildflags.gni")
+
+assert(enable_extensions)
+
+source_set("extension_enable_flow_delegate") {
+  sources = [ "extension_enable_flow_delegate.h" ]
+}
diff --git a/chrome/browser/ui/hats/BUILD.gn b/chrome/browser/ui/hats/BUILD.gn
index 792aa857..e80c83e 100644
--- a/chrome/browser/ui/hats/BUILD.gn
+++ b/chrome/browser/ui/hats/BUILD.gn
@@ -58,6 +58,7 @@
     "//chrome/browser:browser_process",
     "//chrome/browser/content_settings:content_settings_factory",
     "//chrome/browser/prefs",
+    "//chrome/browser/privacy_sandbox/incognito:incognito_features",
     "//chrome/browser/profiles",
     "//chrome/browser/ui",
     "//chrome/browser/ui/safety_hub",
diff --git a/chrome/browser/ui/hats/survey_config.cc b/chrome/browser/ui/hats/survey_config.cc
index 502954d..4ff70d06 100644
--- a/chrome/browser/ui/hats/survey_config.cc
+++ b/chrome/browser/ui/hats/survey_config.cc
@@ -11,6 +11,7 @@
 #include "base/features.h"
 #include "base/strings/string_util.h"
 #include "chrome/browser/metrics/variations/google_groups_manager_factory.h"
+#include "chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_features.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_features.h"
 #include "components/autofill/core/common/autofill_features.h"
@@ -157,6 +158,8 @@
         "plus-address-filled-plus-address-via-manual-fallback";
 constexpr char kHatsSurveyTriggerPrivacySandboxSentimentSurvey[] =
     "privacy-sandbox-sentiment-survey";
+constexpr char kHatsSurveyTriggerPrivacySandboxActSurvey[] =
+    "privacy-sandbox-act-survey";
 constexpr char kHatsSurveyTriggerMerchantTrustEvaluationControlSurvey[] =
     "merchant-trust-evaluation-control-survey";
 constexpr char kHatsSurveyTriggerMerchantTrustEvaluationExperimentSurvey[] =
@@ -231,6 +234,20 @@
       /*log_responses_to_ukm=*/true);
 
 #if !BUILDFLAG(IS_ANDROID)
+  // Privacy sandbox ACT survey
+  survey_configs.emplace_back(  //
+      &privacy_sandbox::kPrivacySandboxActSurvey,
+      kHatsSurveyTriggerPrivacySandboxActSurvey,
+      /*presupplied_trigger_id=*/std::nullopt,
+      /*product_specific_bits_data_fields=*/
+      std::vector<std::string>{},
+      /*product_specific_string_data_fields=*/
+      std::vector<std::string>{"Survey Trigger Delay"},
+      /*log_responses_to_uma=*/false,
+      /*log_responses_to_ukm=*/false,
+      /*requested_browser_type=*/
+      hats::SurveyConfig::RequestedBrowserType::kIncognito);
+
   // Dev tools surveys.
   survey_configs.emplace_back(&features::kHaTSDesktopDevToolsIssuesCOEP,
                               "devtools-issues-coep",
diff --git a/chrome/browser/ui/hats/survey_config.h b/chrome/browser/ui/hats/survey_config.h
index f036eef..96cb3e7 100644
--- a/chrome/browser/ui/hats/survey_config.h
+++ b/chrome/browser/ui/hats/survey_config.h
@@ -84,6 +84,7 @@
 extern const char
     kHatsSurveyTriggerPlusAddressFilledPlusAddressViaManualFallback[];
 extern const char kHatsSurveyTriggerPrivacySandboxSentimentSurvey[];
+extern const char kHatsSurveyTriggerPrivacySandboxActSurvey[];
 extern const char kHatsSurveyTriggerMerchantTrustEvaluationControlSurvey[];
 extern const char kHatsSurveyTriggerMerchantTrustEvaluationExperimentSurvey[];
 extern const char kHatsSurveyTriggerMerchantTrustLearnSurvey[];
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc
index 644b788..aba216f 100644
--- a/chrome/browser/ui/page_info/page_info_unittest.cc
+++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -1540,7 +1540,7 @@
   // its call chain.
   EXPECT_CALL(*mock_ui(), SetCookieInfo(_)).Times(2);
 
-  page_info()->OnStatusChanged(CookieControlsState::k3pcsBlocked,
+  page_info()->OnStatusChanged(CookieControlsState::kBlocked3pc,
                                CookieControlsEnforcement::kNoEnforcement,
                                CookieBlocking3pcdStatus::kNotIn3pcd,
                                base::Time());
@@ -1563,7 +1563,7 @@
   Mock::VerifyAndClearExpectations(mock_ui());
   EXPECT_CALL(*mock_ui(), SetCookieInfo(_)).Times(2);
 
-  page_info()->OnStatusChanged(CookieControlsState::k3pcsAllowed,
+  page_info()->OnStatusChanged(CookieControlsState::kAllowed3pc,
                                CookieControlsEnforcement::kNoEnforcement,
                                CookieBlocking3pcdStatus::kNotIn3pcd,
                                base::Time());
diff --git a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.cc b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.cc
index e3c01ea..d1c3e76 100644
--- a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.cc
+++ b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.cc
@@ -45,15 +45,6 @@
 constexpr char kRevocationResultHistogram[] =
     "Settings.SafetyHub.DisruptiveNotificationRevocations.RevocationResult";
 
-void UpdateNotificationPermission(HostContentSettingsMap* hcsm,
-                                  const GURL& url,
-                                  ContentSetting setting_value) {
-  hcsm->SetContentSettingCustomScope(
-      ContentSettingsPattern::FromURLNoWildcard(url),
-      ContentSettingsPattern::Wildcard(), ContentSettingsType::NOTIFICATIONS,
-      setting_value);
-}
-
 DisruptiveNotificationPermissionsManager::RevocationState GetRevocationState(
     const base::Value::Dict& dict) {
   const std::string* revocation_state =
@@ -235,23 +226,21 @@
         std::make_pair(item.primary_pattern, item.secondary_pattern));
     int notification_count =
         it != notification_count_map.end() ? it->second : 0;
-    if (!IsNotificationDisruptive(url, notification_count)) {
-      base::UmaHistogramEnumeration(kRevocationResultHistogram,
-                                    RevocationResult::kNotDisruptive);
-      continue;
-    }
+    bool is_disruptive = IsNotificationDisruptive(url, notification_count);
 
-    // At this point we know that the url is allowed to send notifications and
-    // is classified as sending disruptive notifications. Now check if we
-    // already have a revocation entry for this url and process it.
-    //
-    // Note that proposed revocations from previous runs will not actually be
-    // revoked if they are not anymore classified are disruptive.
+    // Now check if we already have a revocation entry for this url and process
+    // it.
     std::optional<RevocationEntry> revocation_entry =
         ContentSettingHelper(*hcsm_).GetRevocationEntry(url);
     if (revocation_entry) {
-      revoked_anything |=
-          HandleExistingValueAndMaybeRevoke(url, *revocation_entry);
+      revoked_anything |= HandleExistingValueAndMaybeRevoke(
+          url, *revocation_entry, is_disruptive);
+      continue;
+    }
+
+    if (!is_disruptive) {
+      base::UmaHistogramEnumeration(kRevocationResultHistogram,
+                                    RevocationResult::kNotDisruptive);
       continue;
     }
 
@@ -289,7 +278,8 @@
 
 bool DisruptiveNotificationPermissionsManager::
     HandleExistingValueAndMaybeRevoke(const GURL& url,
-                                      const RevocationEntry& revocation_entry) {
+                                      const RevocationEntry& revocation_entry,
+                                      bool is_disruptive) {
   switch (revocation_entry.revocation_state) {
     case RevocationState::kNone:
     case RevocationState::kUnknown:
@@ -305,6 +295,13 @@
                                     RevocationResult::kIgnore);
       return false;
     case RevocationState::kProposed:
+      if (!is_disruptive) {
+        // Not disruptive anymore, clean up proposed revocation.
+        ContentSettingHelper(*hcsm_).DeleteRevocationEntry(url);
+        base::UmaHistogramEnumeration(kRevocationResultHistogram,
+                                      RevocationResult::kNotDisruptive);
+        return false;
+      }
       if (!features::kSafetyHubDisruptiveNotificationRevocationShadowRun
                .Get() &&
           CanRevokeNotifications(url, revocation_entry)) {
@@ -323,14 +320,18 @@
     const GURL& url,
     const RevocationEntry& revocation_entry) {
   CHECK_EQ(revocation_entry.revocation_state, RevocationState::kProposed);
-  const int days_since_proposed_revocation =
-      (clock_->Now() - revocation_entry.timestamp).InDays();
+  const base::TimeDelta time_since_proposed_revocation =
+      clock_->Now() - revocation_entry.timestamp;
 
-  return revocation_entry.has_reported_proposal ||
-         days_since_proposed_revocation >=
+  return time_since_proposed_revocation >=
              features::
-                 kSafetyHubDisruptiveNotificationRevocationWaitingForMetricsDays
-                     .Get();
+                 kSafetyHubDisruptiveNotificationRevocationWaitingTimeAsProposed
+                     .Get() &&
+         (revocation_entry.has_reported_proposal ||
+          time_since_proposed_revocation.InDays() >=
+              features::
+                  kSafetyHubDisruptiveNotificationRevocationWaitingForMetricsDays
+                      .Get());
 }
 
 void DisruptiveNotificationPermissionsManager::RevokeNotifications(
@@ -342,8 +343,7 @@
   revocation_entry.created_at = clock_->Now();
   revocation_entry.lifetime = safety_hub_util::GetCleanUpThreshold();
   ContentSettingHelper(*hcsm_).PersistRevocationEntry(url, revocation_entry);
-  UpdateNotificationPermission(hcsm_.get(), url,
-                               ContentSetting::CONTENT_SETTING_DEFAULT);
+  UpdateNotificationPermission(url, ContentSetting::CONTENT_SETTING_DEFAULT);
   base::UmaHistogramEnumeration(kRevocationResultHistogram,
                                 RevocationResult::kRevoke);
   base::UmaHistogramCounts100(
@@ -372,7 +372,8 @@
           ContentSettingsType::REVOKED_DISRUPTIVE_NOTIFICATION_PERMISSIONS) {
     UpdateNotificationCount();
   }
-  if (!IsRunning() && !content_type_set.ContainsAllTypes() &&
+  if (!IsRunning() && !is_changing_notification_permission_ &&
+      !content_type_set.ContainsAllTypes() &&
       content_type_set.GetType() == ContentSettingsType::NOTIFICATIONS &&
       content_settings::PatternAppliesToSingleOrigin(primary_pattern,
                                                      secondary_pattern)) {
@@ -387,27 +388,7 @@
         hcsm_->GetContentSetting(url, url,
                                  ContentSettingsType::NOTIFICATIONS) ==
             ContentSetting::CONTENT_SETTING_ALLOW) {
-      base::AutoReset<bool> is_regrant_running(&is_regrant_or_undo_running_,
-                                               true);
-
-      revocation_entry->revocation_state = RevocationState::kIgnore;
-      // Clear the lifetime so that this won't expire.
-      revocation_entry->lifetime = base::TimeDelta();
-      ContentSettingHelper(*hcsm_).PersistRevocationEntry(url,
-                                                          *revocation_entry);
-
-      base::UmaHistogramCounts100(
-          "Settings.SafetyHub.DisruptiveNotificationRevocations.UserRegrant."
-          "DaysSinceProposedRevocation",
-          (clock_->Now() - revocation_entry->timestamp).InDays());
-      base::UmaHistogramCounts100(
-          "Settings.SafetyHub.DisruptiveNotificationRevocations.UserRegrant."
-          "NewSiteEngagement",
-          site_engagement_service_->GetScore(url));
-      base::UmaHistogramCounts100(
-          "Settings.SafetyHub.DisruptiveNotificationRevocations.UserRegrant."
-          "PreviousNotificationCount",
-          revocation_entry->daily_notification_count);
+      OnPermissionRegranted(url, *revocation_entry);
     }
   }
 }
@@ -441,7 +422,7 @@
 }
 
 bool DisruptiveNotificationPermissionsManager::IsRunning() {
-  return is_revocation_running_ || is_regrant_or_undo_running_;
+  return is_revocation_running_;
 }
 
 void DisruptiveNotificationPermissionsManager::RegrantPermissionForUrl(
@@ -456,14 +437,30 @@
     return;
   }
 
-  base::AutoReset<bool> is_regrant_running(&is_regrant_or_undo_running_, true);
+  UpdateNotificationPermission(url, ContentSetting::CONTENT_SETTING_ALLOW);
+  OnPermissionRegranted(url, *revocation_entry);
+}
 
-  UpdateNotificationPermission(hcsm_.get(), url,
-                               ContentSetting::CONTENT_SETTING_ALLOW);
-  revocation_entry->revocation_state = RevocationState::kIgnore;
+void DisruptiveNotificationPermissionsManager::OnPermissionRegranted(
+    const GURL& url,
+    RevocationEntry revocation_entry) {
+  revocation_entry.revocation_state = RevocationState::kIgnore;
   // Clear the lifetime so that this won't expire.
-  revocation_entry->lifetime = base::TimeDelta();
-  ContentSettingHelper(*hcsm_).PersistRevocationEntry(url, *revocation_entry);
+  revocation_entry.lifetime = base::TimeDelta();
+  ContentSettingHelper(*hcsm_).PersistRevocationEntry(url, revocation_entry);
+
+  base::UmaHistogramCounts100(
+      "Settings.SafetyHub.DisruptiveNotificationRevocations.UserRegrant."
+      "DaysSinceProposedRevocation",
+      (clock_->Now() - revocation_entry.timestamp).InDays());
+  base::UmaHistogramCounts100(
+      "Settings.SafetyHub.DisruptiveNotificationRevocations.UserRegrant."
+      "NewSiteEngagement",
+      site_engagement_service_->GetScore(url));
+  base::UmaHistogramCounts100(
+      "Settings.SafetyHub.DisruptiveNotificationRevocations.UserRegrant."
+      "PreviousNotificationCount",
+      revocation_entry.daily_notification_count);
 }
 
 void DisruptiveNotificationPermissionsManager::UndoRegrantPermissionForUrl(
@@ -485,10 +482,7 @@
     return;
   }
 
-  base::AutoReset<bool> is_regrant_running(&is_regrant_or_undo_running_, true);
-
-  UpdateNotificationPermission(hcsm_.get(), url,
-                               ContentSetting::CONTENT_SETTING_DEFAULT);
+  UpdateNotificationPermission(url, ContentSetting::CONTENT_SETTING_DEFAULT);
   revocation_entry->revocation_state = RevocationState::kRevoked;
   revocation_entry->created_at =
       constraints.expiration() - constraints.lifetime();
@@ -666,6 +660,18 @@
          revocation_entry->revocation_state == RevocationState::kRevoked;
 }
 
+void DisruptiveNotificationPermissionsManager::UpdateNotificationPermission(
+    const GURL& url,
+    ContentSetting setting_value) {
+  base::AutoReset<bool> is_changing_notification_permission(
+      &is_changing_notification_permission_, true);
+
+  hcsm_->SetContentSettingCustomScope(
+      ContentSettingsPattern::FromURLNoWildcard(url),
+      ContentSettingsPattern::Wildcard(), ContentSettingsType::NOTIFICATIONS,
+      setting_value);
+}
+
 void DisruptiveNotificationPermissionsManager::SetClockForTesting(
     base::Clock* clock) {
   clock_ = clock;
diff --git a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.h b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.h
index f5fe4264..4618139c 100644
--- a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.h
+++ b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager.h
@@ -235,7 +235,8 @@
   // otherwise.
   bool HandleExistingValueAndMaybeRevoke(
       const GURL& url,
-      const RevocationEntry& revocation_entry);
+      const RevocationEntry& revocation_entry,
+      bool is_disruptive);
 
   // If the notifications should be revoked based on whether the metrics were
   // already reported or the cooldown period has run out.
@@ -258,6 +259,13 @@
   // notification informing the users about revoked notification permissions.
   void UpdateNotificationCount();
 
+  // Updates content settings for notification permissions.
+  void UpdateNotificationPermission(const GURL& url,
+                                    ContentSetting setting_value);
+
+  // Ignores this url for future revocation and reports regrant metrics.
+  void OnPermissionRegranted(const GURL& url, RevocationEntry revocation_entry);
+
   scoped_refptr<HostContentSettingsMap> hcsm_;
 
   raw_ptr<site_engagement::SiteEngagementService> site_engagement_service_;
@@ -268,11 +276,11 @@
 
   raw_ptr<base::Clock> clock_ = base::DefaultClock::GetInstance();
 
-  // Returns true if the revocation of disruptive notification
-  // permissions is happening.
   bool is_revocation_running_ = false;
 
-  bool is_regrant_or_undo_running_ = false;
+  // Track whether this service is responsible for changing notification
+  // permissions, in order to ignore this case inside OnContentSettingChanged.
+  bool is_changing_notification_permission_ = false;
 
   std::unique_ptr<SafetyHubNotificationWrapper> notification_wrapper_;
 };
diff --git a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager_unittest.cc b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager_unittest.cc
index 935854d..d68a5e3 100644
--- a/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager_unittest.cc
+++ b/chrome/browser/ui/safety_hub/disruptive_notification_permissions_manager_unittest.cc
@@ -188,6 +188,10 @@
                   .name,
           "3.0"},
          {features::
+              kSafetyHubDisruptiveNotificationRevocationWaitingTimeAsProposed
+                  .name,
+          "1d"},
+         {features::
               kSafetyHubDisruptiveNotificationRevocationWaitingForMetricsDays
                   .name,
           "7"}});
@@ -258,6 +262,49 @@
 }
 
 TEST_F(DisruptiveNotificationPermissionsManagerRevocationTest,
+       DoNotRevokeDisruptivePermissionBeforeWaitingTime) {
+  base::HistogramTester t;
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+  GURL url("https://www.example.com");
+  SetNotificationPermission(url, CONTENT_SETTING_ALLOW);
+  SetDailyAverageNotificationCount(url, 3);
+  site_engagement_service()->ResetBaseScoreForURL(url, 0);
+
+  manager()->RevokeDisruptiveNotifications();
+  EXPECT_EQ(
+      CONTENT_SETTING_ALLOW,
+      hcsm()->GetContentSetting(url, url, ContentSettingsType::NOTIFICATIONS));
+
+  EXPECT_THAT(ContentSettingHelper(*hcsm()).GetRevocationEntry(url),
+              Optional(Field(&RevocationEntry::revocation_state,
+                             RevocationState::kProposed)));
+  t.ExpectBucketCount(kRevocationResultHistogram,
+                      RevocationResult::kProposedRevoke, 1);
+
+  // Log metrics (happens when a notification is shown).
+  ukm::SourceId source_id = ukm::UkmRecorder::GetNewSourceID();
+  ukm_recorder.UpdateSourceURL(source_id, GURL(url));
+  DisruptiveNotificationPermissionsManager::LogMetrics(profile(), url,
+                                                       source_id);
+
+  // The waiting time of 1 day has not passed yet so the notification permission
+  // won't be revoked.
+  manager()->RevokeDisruptiveNotifications();
+  EXPECT_EQ(
+      CONTENT_SETTING_ALLOW,
+      hcsm()->GetContentSetting(url, url, ContentSettingsType::NOTIFICATIONS));
+  EXPECT_EQ(GetRevokedPermissionsCount(), 1);
+  t.ExpectBucketCount(kRevocationResultHistogram,
+                      RevocationResult::kProposedRevoke, 1);
+  t.ExpectBucketCount(kRevocationResultHistogram,
+                      RevocationResult::kAlreadyInProposedRevokeList, 1);
+
+  EXPECT_THAT(ContentSettingHelper(*hcsm()).GetRevocationEntry(url),
+              Optional(Field(&RevocationEntry::revocation_state,
+                             RevocationState::kProposed)));
+}
+
+TEST_F(DisruptiveNotificationPermissionsManagerRevocationTest,
        RevokeDisruptivePermissionHaventReportedMetrics) {
   base::HistogramTester t;
   ukm::TestAutoSetUkmRecorder ukm_recorder;
@@ -375,14 +422,14 @@
 
   site_engagement_service()->ResetBaseScoreForURL(url, 10);
 
-  // On the next run, site stays proposed because it is not disruptive anymore.
+  // On the next run, the revocation entry has been cleaned up because the site
+  // is not disruptive anymore.
   manager()->RevokeDisruptiveNotifications();
   EXPECT_EQ(
       CONTENT_SETTING_ALLOW,
       hcsm()->GetContentSetting(url, url, ContentSettingsType::NOTIFICATIONS));
   EXPECT_THAT(ContentSettingHelper(*hcsm()).GetRevocationEntry(url),
-              Optional(Field(&RevocationEntry::revocation_state,
-                             RevocationState::kProposed)));
+              Eq(std::nullopt));
   t.ExpectBucketCount(kRevocationResultHistogram,
                       RevocationResult::kProposedRevoke, 1);
   t.ExpectBucketCount(kRevocationResultHistogram, RevocationResult::kRevoke, 0);
@@ -617,9 +664,12 @@
                .revocation_state = RevocationState::kRevoked,
                .site_engagement = 0.0,
                .daily_notification_count = 3,
+               .timestamp = clock()->Now(),
                .lifetime = safety_hub_util::GetCleanUpThreshold(),
            });
 
+  clock()->Advance(base::Days(5));
+
   manager()->RegrantPermissionForUrl(url);
   // Notifications are again allowed.
   EXPECT_EQ(
@@ -641,6 +691,19 @@
   EXPECT_THAT(revocation_entry, Optional(Field(&RevocationEntry::lifetime,
                                                Eq(base::TimeDelta()))));
 
+  t.ExpectUniqueSample(
+      "Settings.SafetyHub.DisruptiveNotificationRevocations.UserRegrant."
+      "DaysSinceProposedRevocation",
+      5, 1);
+  t.ExpectUniqueSample(
+      "Settings.SafetyHub.DisruptiveNotificationRevocations.UserRegrant."
+      "NewSiteEngagement",
+      0, 1);
+  t.ExpectUniqueSample(
+      "Settings.SafetyHub.DisruptiveNotificationRevocations.UserRegrant."
+      "PreviousNotificationCount",
+      3, 1);
+
   manager()->RevokeDisruptiveNotifications();
   // The site is reported as ignored for revocation and not revoked.
   t.ExpectBucketCount(kRevocationResultHistogram, RevocationResult::kIgnore, 1);
diff --git a/chrome/browser/ui/safety_hub/revoked_permissions_service.cc b/chrome/browser/ui/safety_hub/revoked_permissions_service.cc
index d41a0ce..564e298 100644
--- a/chrome/browser/ui/safety_hub/revoked_permissions_service.cc
+++ b/chrome/browser/ui/safety_hub/revoked_permissions_service.cc
@@ -450,12 +450,21 @@
 
   if (IsUnusedSiteAutoRevocationEnabled() ||
       IsAbusiveNotificationAutoRevocationEnabled()) {
-    StartRepeatedUpdates();
+    hcsm()->EnsureSettingsUpToDate(
+        base::BindOnce(&RevokedPermissionsService::MaybeStartRepeatedUpdates,
+                       weak_factory_.GetWeakPtr()));
   }
 }
 
 RevokedPermissionsService::~RevokedPermissionsService() = default;
 
+void RevokedPermissionsService::MaybeStartRepeatedUpdates() {
+  if (IsUnusedSiteAutoRevocationEnabled() ||
+      IsAbusiveNotificationAutoRevocationEnabled()) {
+    StartRepeatedUpdates();
+  }
+}
+
 std::unique_ptr<SafetyHubService::Result>
 RevokedPermissionsService::InitializeLatestResultImpl() {
   return GetRevokedPermissions();
diff --git a/chrome/browser/ui/safety_hub/revoked_permissions_service.h b/chrome/browser/ui/safety_hub/revoked_permissions_service.h
index e655d15..8aac437 100644
--- a/chrome/browser/ui/safety_hub/revoked_permissions_service.h
+++ b/chrome/browser/ui/safety_hub/revoked_permissions_service.h
@@ -275,6 +275,8 @@
     return HostContentSettingsMapFactory::GetForProfile(browser_context_.get());
   }
 
+  void MaybeStartRepeatedUpdates();
+
   // SafetyHubService implementation
 
   std::unique_ptr<SafetyHubService::Result> InitializeLatestResultImpl()
diff --git a/chrome/browser/ui/startup/infobar_utils.cc b/chrome/browser/ui/startup/infobar_utils.cc
index 3c804646..d52ef581 100644
--- a/chrome/browser/ui/startup/infobar_utils.cc
+++ b/chrome/browser/ui/startup/infobar_utils.cc
@@ -144,41 +144,44 @@
   // These info bars are not shown when the browser is being controlled by
   // automated tests, so that they don't interfere with tests that assume no
   // info bars.
-  if (!startup_command_line.HasSwitch(switches::kTestType) &&
-      !IsAutomationEnabled()) {
-    // The below info bars are only added to the first profile which is
-    // launched. Other profiles might be restoring the browsing sessions
-    // asynchronously, so we cannot add the info bars to the focused tabs here.
-    //
-    // We cannot use `chrome::startup::IsProcessStartup` to determine whether
-    // this is the first profile that launched: The browser may be started
-    // without a startup window (`kNoStartupWindow`), or open the profile
-    // picker, which means that `chrome::startup::IsProcessStartup` will already
-    // be `kNo` when the first browser window is opened.
-    static bool infobars_shown = false;
-    if (infobars_shown) {
-      return;
-    }
-    infobars_shown = true;
+  if (startup_command_line.HasSwitch(switches::kTestType) ||
+      IsAutomationEnabled()) {
+    return;
+  }
 
-    if (show_bad_flags_security_warnings) {
-      ShowBadFlagsPrompt(web_contents);
-    }
+  // The below info bars are only added to the first profile which is
+  // launched. Other profiles might be restoring the browsing sessions
+  // asynchronously, so we cannot add the info bars to the focused tabs here.
+  //
+  // We cannot use `chrome::startup::IsProcessStartup` to determine whether
+  // this is the first profile that launched: The browser may be started
+  // without a startup window (`kNoStartupWindow`), or open the profile
+  // picker, which means that `chrome::startup::IsProcessStartup` will already
+  // be `kNo` when the first browser window is opened.
+  static bool infobars_shown = false;
+  if (infobars_shown) {
+    return;
+  }
+  infobars_shown = true;
 
-    infobars::ContentInfoBarManager* infobar_manager =
-        infobars::ContentInfoBarManager::FromWebContents(web_contents);
+  if (show_bad_flags_security_warnings) {
+    ShowBadFlagsPrompt(web_contents);
+  }
 
-    if (!google_apis::HasAPIKeyConfigured()) {
-      GoogleApiKeysInfoBarDelegate::Create(infobar_manager);
-    }
+  infobars::ContentInfoBarManager* infobar_manager =
+      infobars::ContentInfoBarManager::FromWebContents(web_contents);
 
-    if (ObsoleteSystem::IsObsoleteNowOrSoon()) {
-      PrefService* local_state = g_browser_process->local_state();
-      if (!local_state ||
-          !local_state->GetBoolean(prefs::kSuppressUnsupportedOSWarning)) {
-        ObsoleteSystemInfoBarDelegate::Create(infobar_manager);
-      }
+  if (!google_apis::HasAPIKeyConfigured()) {
+    GoogleApiKeysInfoBarDelegate::Create(infobar_manager);
+  }
+
+  if (ObsoleteSystem::IsObsoleteNowOrSoon()) {
+    PrefService* local_state = g_browser_process->local_state();
+    if (!local_state ||
+        !local_state->GetBoolean(prefs::kSuppressUnsupportedOSWarning)) {
+      ObsoleteSystemInfoBarDelegate::Create(infobar_manager);
     }
+  }
 
 #if BUILDFLAG(IS_WIN) && BUILDFLAG(GOOGLE_CHROME_BRANDING)
     if (auto* controller = g_browser_process->GetFeatures()
@@ -188,24 +191,26 @@
 #endif
 
 #if !BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_ANDROID)
-    if (!is_web_app &&
-        !startup_command_line.HasSwitch(switches::kNoDefaultBrowserCheck)) {
-      base::OnceCallback<void(bool)> default_browser_prompt_shown_callback =
-          base::DoNothing();
+  if (is_web_app ||
+      startup_command_line.HasSwitch(switches::kNoDefaultBrowserCheck) ||
+      startup_command_line.HasSwitch(switches::kNoFirstRun)) {
+    return;
+  }
+
+  base::OnceCallback<void(bool)> default_browser_prompt_shown_callback =
+      base::DoNothing();
 #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
-      if (base::FeatureList::IsEnabled(features::kPdfInfoBar)) {
-        default_browser_prompt_shown_callback =
-            base::BindOnce(&PdfInfoBarController::MaybeShowInfoBarAtStartup,
-                           browser->GetWeakPtr());
-      }
+  if (base::FeatureList::IsEnabled(features::kPdfInfoBar)) {
+    default_browser_prompt_shown_callback =
+        base::BindOnce(&PdfInfoBarController::MaybeShowInfoBarAtStartup,
+                       browser->GetWeakPtr());
+  }
 #endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
 
-      // The default browser prompt should only be shown after the first run.
-      if (is_first_run == chrome::startup::IsFirstRun::kNo) {
-        ShowDefaultBrowserPrompt(
-            profile, std::move(default_browser_prompt_shown_callback));
-      }
-    }
-#endif
+  // The default browser prompt should only be shown after the first run.
+  if (is_first_run == chrome::startup::IsFirstRun::kNo) {
+    ShowDefaultBrowserPrompt(profile,
+                             std::move(default_browser_prompt_shown_callback));
   }
+#endif
 }
diff --git a/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate_unittest.cc b/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate_unittest.cc
index fdf4ef64e..a15a1e3 100644
--- a/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate_unittest.cc
+++ b/chrome/browser/ui/tab_sharing/tab_sharing_infobar_delegate_unittest.cc
@@ -15,6 +15,7 @@
 #include "components/url_formatter/elide_url.h"
 #include "components/vector_icons/vector_icons.h"
 #include "content/public/common/content_features.h"
+#include "media/capture/capture_switches.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -24,6 +25,8 @@
 
 using TabRole = ::TabSharingInfoBarDelegate::TabRole;
 using ::content::GlobalRenderFrameHostId;
+using ::vector_icons::kScreenShareIcon;
+using ::vector_icons::kScreenShareOldIcon;
 
 const std::u16string kSharedTabName = u"example.com";
 const std::u16string kAppName = u"sharing.com";
@@ -53,7 +56,7 @@
 
 class TabSharingInfoBarDelegateTest
     : public BrowserWithTestWindowTest,
-      public ::testing::WithParamInterface<bool> {
+      public ::testing::WithParamInterface<std::tuple<bool, bool>> {
  public:
   struct Preferences {
     GlobalRenderFrameHostId shared_tab_id;
@@ -69,7 +72,11 @@
   };
 
   TabSharingInfoBarDelegateTest()
-      : captured_surface_control_active_(GetParam()) {}
+      : captured_surface_control_active_(std::get<0>(GetParam())),
+        enable_tab_capture_infobar_links_(std::get<1>(GetParam())) {
+    scoped_feature_list_.InitWithFeatureState(
+        features::kTabCaptureInfobarLinks, enable_tab_capture_infobar_links_);
+  }
 
   infobars::InfoBar* CreateInfobar(const Preferences& prefs) {
     content::WebContents* const web_contents =
@@ -113,14 +120,18 @@
 
  protected:
   const bool captured_surface_control_active_;
+  const bool enable_tab_capture_infobar_links_;
 
  private:
+  base::test::ScopedFeatureList scoped_feature_list_;
   MockTabSharingUIViews mock_ui;
 };
 
-INSTANTIATE_TEST_SUITE_P(,
-                         TabSharingInfoBarDelegateTest,
-                         /*captured_surface_control_active=*/testing::Bool());
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    TabSharingInfoBarDelegateTest,
+    ::testing::Combine(/*captured_surface_control_active=*/::testing::Bool(),
+                       /*enable_tab_capture_infobar_links=*/::testing::Bool()));
 
 TEST_P(TabSharingInfoBarDelegateTest, StartSharingOnCancel) {
   AddTab(browser(), GURL("about:blank"));
@@ -162,8 +173,9 @@
                       .tab_index = 1,
                       .focus_target = GlobalRenderFrameHostId{GetGlobalId(0)}});
 
-  EXPECT_STREQ(delegate->GetVectorIcon().name,
-               vector_icons::kScreenShareOldIcon.name);
+  EXPECT_STREQ(delegate->GetVectorIcon().name, enable_tab_capture_infobar_links_
+                                                   ? kScreenShareIcon.name
+                                                   : kScreenShareOldIcon.name);
 
   const int expected_buttons =
       TabSharingInfoBarDelegate::kStop | TabSharingInfoBarDelegate::kQuickNav |
@@ -200,8 +212,9 @@
                       .tab_index = 0,
                       .focus_target = GlobalRenderFrameHostId{GetGlobalId(1)}});
 
-  EXPECT_STREQ(delegate->GetVectorIcon().name,
-               vector_icons::kScreenShareOldIcon.name);
+  EXPECT_STREQ(delegate->GetVectorIcon().name, enable_tab_capture_infobar_links_
+                                                   ? kScreenShareIcon.name
+                                                   : kScreenShareOldIcon.name);
   EXPECT_EQ(delegate->GetButtons(), TabSharingInfoBarDelegate::kStop |
                                         TabSharingInfoBarDelegate::kQuickNav);
   EXPECT_EQ(delegate->GetButtonLabel(TabSharingInfoBarDelegate::kStop),
@@ -220,8 +233,9 @@
                       .capturer_name = kAppName,
                       .role = TabRole::kOtherTab,
                       .can_share_instead = true});
-  EXPECT_STREQ(delegate->GetVectorIcon().name,
-               vector_icons::kScreenShareOldIcon.name);
+  EXPECT_STREQ(delegate->GetVectorIcon().name, enable_tab_capture_infobar_links_
+                                                   ? kScreenShareIcon.name
+                                                   : kScreenShareOldIcon.name);
   EXPECT_EQ(delegate->GetButtons(),
             TabSharingInfoBarDelegate::kStop |
                 TabSharingInfoBarDelegate::kShareThisTabInstead);
@@ -278,8 +292,9 @@
                       .tab_index = 0,
                       .focus_target = GlobalRenderFrameHostId{GetGlobalId(1)}});
 
-  EXPECT_STREQ(delegate->GetVectorIcon().name,
-               vector_icons::kScreenShareOldIcon.name);
+  EXPECT_STREQ(delegate->GetVectorIcon().name, enable_tab_capture_infobar_links_
+                                                   ? kScreenShareIcon.name
+                                                   : kScreenShareOldIcon.name);
 
   // Correct number of buttons.
   EXPECT_EQ(delegate->GetButtons(),
@@ -349,8 +364,9 @@
       .can_share_instead = true,
       .capture_type = TabSharingInfoBarDelegate::TabShareType::CAST};
   TabSharingInfoBarDelegate* const delegate = CreateDelegate(preferences);
-  EXPECT_STREQ(delegate->GetVectorIcon().name,
-               vector_icons::kScreenShareOldIcon.name);
+  EXPECT_STREQ(delegate->GetVectorIcon().name, enable_tab_capture_infobar_links_
+                                                   ? kScreenShareIcon.name
+                                                   : kScreenShareOldIcon.name);
   EXPECT_EQ(delegate->GetButtons(),
             TabSharingInfoBarDelegate::kStop |
                 TabSharingInfoBarDelegate::kShareThisTabInstead);
@@ -373,8 +389,9 @@
       .can_share_instead = false,
       .capture_type = TabSharingInfoBarDelegate::TabShareType::CAST};
   TabSharingInfoBarDelegate* const delegate = CreateDelegate(preferences);
-  EXPECT_STREQ(delegate->GetVectorIcon().name,
-               vector_icons::kScreenShareOldIcon.name);
+  EXPECT_STREQ(delegate->GetVectorIcon().name, enable_tab_capture_infobar_links_
+                                                   ? kScreenShareIcon.name
+                                                   : kScreenShareOldIcon.name);
   EXPECT_EQ(delegate->GetButtons(), TabSharingInfoBarDelegate::kStop);
   EXPECT_EQ(delegate->GetButtonLabel(TabSharingInfoBarDelegate::kStop),
             l10n_util::GetStringUTF16(IDS_TAB_CASTING_INFOBAR_STOP_BUTTON));
diff --git a/chrome/browser/ui/tabs/BUILD.gn b/chrome/browser/ui/tabs/BUILD.gn
index 27e2b31..5024f5ec 100644
--- a/chrome/browser/ui/tabs/BUILD.gn
+++ b/chrome/browser/ui/tabs/BUILD.gn
@@ -337,6 +337,7 @@
       "//chrome/browser/fingerprinting_protection",
       "//chrome/browser/image_fetcher",
       "//chrome/browser/media/webrtc",
+      "//chrome/browser/privacy_sandbox/incognito:tab_observer",
       "//chrome/browser/profiles",
       "//chrome/browser/profiles:profile",
       "//chrome/browser/search",
diff --git a/chrome/browser/ui/tabs/public/tab_features.h b/chrome/browser/ui/tabs/public/tab_features.h
index 5249d4fc..b6cef5f 100644
--- a/chrome/browser/ui/tabs/public/tab_features.h
+++ b/chrome/browser/ui/tabs/public/tab_features.h
@@ -73,6 +73,7 @@
 
 namespace privacy_sandbox {
 class PrivacySandboxTabObserver;
+class PrivacySandboxIncognitoTabObserver;
 }  // namespace privacy_sandbox
 
 namespace metrics {
@@ -165,6 +166,11 @@
     return privacy_sandbox_tab_observer_.get();
   }
 
+  privacy_sandbox::PrivacySandboxIncognitoTabObserver*
+  privacy_sandbox_incognito_tab_observer() {
+    return privacy_sandbox_incognito_tab_observer_.get();
+  }
+
   metrics::DwaWebContentsObserver* dwa_web_contents_observer() {
     return dwa_web_contents_observer_.get();
   }
@@ -298,6 +304,9 @@
   std::unique_ptr<privacy_sandbox::PrivacySandboxTabObserver>
       privacy_sandbox_tab_observer_;
 
+  std::unique_ptr<privacy_sandbox::PrivacySandboxIncognitoTabObserver>
+      privacy_sandbox_incognito_tab_observer_;
+
   std::unique_ptr<metrics::DwaWebContentsObserver>
       dwa_web_contents_observer_;
 
diff --git a/chrome/browser/ui/tabs/tab_features.cc b/chrome/browser/ui/tabs/tab_features.cc
index 9da0c88..94b553c 100644
--- a/chrome/browser/ui/tabs/tab_features.cc
+++ b/chrome/browser/ui/tabs/tab_features.cc
@@ -20,6 +20,7 @@
 #include "chrome/browser/image_fetcher/image_fetcher_service_factory.h"
 #include "chrome/browser/loader/from_gws_navigation_and_keep_alive_request_observer.h"
 #include "chrome/browser/passage_embeddings/embedder_tab_observer.h"
+#include "chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_tab_observer.h"
 #include "chrome/browser/privacy_sandbox/privacy_sandbox_tab_observer.h"
 #include "chrome/browser/privacy_sandbox/tracking_protection_settings_factory.h"
 #include "chrome/browser/profiles/profile.h"
@@ -238,6 +239,10 @@
         std::make_unique<privacy_sandbox::PrivacySandboxTabObserver>(
             tab.GetContents());
 
+    privacy_sandbox_incognito_tab_observer_ =
+        std::make_unique<privacy_sandbox::PrivacySandboxIncognitoTabObserver>(
+            tab.GetContents());
+
     dwa_web_contents_observer_ =
         std::make_unique<metrics::DwaWebContentsObserver>(
             tab.GetContents());
@@ -400,6 +405,13 @@
             new_contents);
   }
 
+  if (privacy_sandbox_incognito_tab_observer_) {
+    privacy_sandbox_incognito_tab_observer_.reset();
+    privacy_sandbox_incognito_tab_observer_ =
+        std::make_unique<privacy_sandbox::PrivacySandboxIncognitoTabObserver>(
+            new_contents);
+  }
+
   if (dwa_web_contents_observer_) {
     dwa_web_contents_observer_.reset();
     dwa_web_contents_observer_ =
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 5e907e3..7cc118b 100644
--- a/chrome/browser/ui/views/autofill/popup/popup_view_utils.cc
+++ b/chrome/browser/ui/views/autofill/popup/popup_view_utils.cc
@@ -460,6 +460,7 @@
     case SuggestionType::kDevtoolsTestAddresses:
     case SuggestionType::kFillAutofillAi:
     case SuggestionType::kLoyaltyCardEntry:
+    case SuggestionType::kHomeAndWorkAddressEntry:
     case SuggestionType::kPasswordEntry:
       return true;
     case SuggestionType::kAccountStoragePasswordEntry:
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_unittest.cc b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_unittest.cc
index 24b3065..475b5f1 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_unittest.cc
+++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_unittest.cc
@@ -221,7 +221,7 @@
   std::unique_ptr<CookieControlsBubbleViewController> view_controller_;
   CookieControlsEnforcement enforcement_ =
       CookieControlsEnforcement::kNoEnforcement;
-  CookieControlsState controls_state_ = CookieControlsState::k3pcsBlocked;
+  CookieControlsState controls_state_ = CookieControlsState::kBlocked3pc;
   CookieBlocking3pcdStatus blocking_status_ =
       CookieBlocking3pcdStatus::kNotIn3pcd;
 };
@@ -264,7 +264,7 @@
               UpdateTitle(l10n_util::GetStringUTF16(
                   IDS_COOKIE_CONTROLS_BUBBLE_COOKIES_ALLOWED_TITLE)));
   blocking_status_ = CookieBlocking3pcdStatus::kLimited;
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   OnStatusChanged(kDaysToExpiration);
 }
 
@@ -283,7 +283,7 @@
               SetCookiesLabel(l10n_util::GetStringUTF16(
                   IDS_TRACKING_PROTECTION_BUBBLE_3PC_ALLOWED_SUBTITLE)));
   blocking_status_ = GetParam();
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   OnStatusChanged();
 }
 
@@ -307,7 +307,7 @@
        FeedbackSectionIsVisibleWhenSiteHasExceptionAndNoEnforcement) {
   EXPECT_CALL(*mock_content_view(), SetFeedbackSectionVisibility(true));
   blocking_status_ = GetParam();
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   OnStatusChanged();
 }
 
@@ -343,7 +343,7 @@
           l10n_util::GetStringUTF16(
               IDS_TRACKING_PROTECTION_BUBBLE_BLOCKING_RESTART_DESCRIPTION)));
   blocking_status_ = CookieBlocking3pcdStatus::kLimited;
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   OnStatusChanged(kDaysToExpiration);
 }
 
@@ -358,7 +358,7 @@
           l10n_util::GetStringUTF16(
               IDS_TRACKING_PROTECTION_BUBBLE_BLOCKING_RESTART_DESCRIPTION)));
   blocking_status_ = CookieBlocking3pcdStatus::kAll;
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   OnStatusChanged(kDaysToExpiration);
 }
 
@@ -372,7 +372,7 @@
           l10n_util::GetStringUTF16(
               IDS_TRACKING_PROTECTION_BUBBLE_PERMANENT_ALLOWED_DESCRIPTION)));
   blocking_status_ = GetParam();
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   OnStatusChanged();
 }
 
@@ -415,7 +415,7 @@
               IDS_TRACKING_PROTECTION_BUBBLE_PERMANENT_ALLOWED_DESCRIPTION)));
   blocking_status_ = testing::get<0>(GetParam());
   enforcement_ = CookieControlsEnforcement::kEnforcedByCookieSetting;
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   OnStatusChanged(testing::get<1>(GetParam()) ? kDaysToExpiration : 0);
 }
 
@@ -426,7 +426,7 @@
                           IDS_PAGE_INFO_PERMISSION_MANAGED_BY_POLICY);
   blocking_status_ = testing::get<0>(GetParam());
   enforcement_ = CookieControlsEnforcement::kEnforcedByPolicy;
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   OnStatusChanged(testing::get<1>(GetParam()) ? kDaysToExpiration : 0);
 }
 
@@ -436,7 +436,7 @@
                           IDS_PAGE_INFO_PERMISSION_MANAGED_BY_EXTENSION);
   blocking_status_ = testing::get<0>(GetParam());
   enforcement_ = CookieControlsEnforcement::kEnforcedByExtension;
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   OnStatusChanged(testing::get<1>(GetParam()) ? kDaysToExpiration : 0);
 }
 
@@ -499,7 +499,7 @@
   EXPECT_CALL(*mock_content_view(),
               SetToggleIcon(testing::Field(&gfx::VectorIcon::name,
                                            views::kEyeRefreshIcon.name)));
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   OnStatusChanged();
 }
 
@@ -524,7 +524,7 @@
   EXPECT_CALL(*mock_content_view(),
               SetToggleIcon(testing::Field(&gfx::VectorIcon::name,
                                            views::kEyeRefreshIcon.name)));
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
 
   OnStatusChanged(kDaysToExpiration);
 }
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.cc b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.cc
index 36343cf..04d5ce9e 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.cc
+++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.cc
@@ -165,8 +165,8 @@
     CookieControlsEnforcement enforcement,
     base::Time expiration) {
   // TODO(crbug.com/388294499): Add support for ACT UI separately.
-  bool tpcs_allowed = controls_state_ == CookieControlsState::k3pcsAllowed ||
-                      controls_state_ == CookieControlsState::kTpPaused;
+  bool tpcs_allowed = controls_state_ == CookieControlsState::kAllowed3pc ||
+                      controls_state_ == CookieControlsState::kPausedTp;
   if (tpcs_allowed) {
     ApplyThirdPartyCookiesAllowedState(enforcement, expiration);
   } else {
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.h b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.h
index 146ec61..acb7e212 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.h
+++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_controller.h
@@ -89,7 +89,7 @@
       CookieBlocking3pcdStatus::kNotIn3pcd;
 
   // The state of the controls to display.
-  CookieControlsState controls_state_ = CookieControlsState::k3pcsBlocked;
+  CookieControlsState controls_state_ = CookieControlsState::kBlocked3pc;
 
   raw_ptr<CookieControlsBubbleView> bubble_view_ = nullptr;
 
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_pixel_test.cc b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_pixel_test.cc
index 20813226..82b6ae21 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_pixel_test.cc
+++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_bubble_view_pixel_test.cc
@@ -196,7 +196,7 @@
  protected:
   CookieControlsEnforcement enforcement_ =
       CookieControlsEnforcement::kNoEnforcement;
-  CookieControlsState controls_state_ = CookieControlsState::k3pcsBlocked;
+  CookieControlsState controls_state_ = CookieControlsState::kBlocked3pc;
 
   int days_to_expiration_ = 0;
   // Overriding `base::Time::Now()` to obtain a consistent X days until
@@ -220,35 +220,35 @@
 IN_PROC_BROWSER_TEST_P(CookieControlsBubbleViewPixelTest,
                        InvokeUi_PermanentException) {
   set_baseline("6229914");
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   ShowAndVerifyUi();
 }
 
 IN_PROC_BROWSER_TEST_P(CookieControlsBubbleViewPixelTest,
                        InvokeUi_TemporaryException) {
   set_baseline("6229914");
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   days_to_expiration_ = 90;
   ShowAndVerifyUi();
 }
 
 IN_PROC_BROWSER_TEST_P(CookieControlsBubbleViewPixelTest,
                        InvokeUi_EnforcedByCookieSetting) {
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   enforcement_ = CookieControlsEnforcement::kEnforcedByCookieSetting;
   ShowAndVerifyUi();
 }
 
 IN_PROC_BROWSER_TEST_P(CookieControlsBubbleViewPixelTest,
                        InvokeUi_EnforcedByPolicy) {
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   enforcement_ = CookieControlsEnforcement::kEnforcedByPolicy;
   ShowAndVerifyUi();
 }
 
 IN_PROC_BROWSER_TEST_P(CookieControlsBubbleViewPixelTest,
                        InvokeUi_EnforcedByExtension) {
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   enforcement_ = CookieControlsEnforcement::kEnforcedByExtension;
   ShowAndVerifyUi();
 }
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view.cc b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view.cc
index f69e48e1..41791171 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view.cc
+++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view.cc
@@ -44,7 +44,7 @@
   if (!icon_visible) {
     base::RecordAction(
         base::UserMetricsAction("CookieControls.Bubble.UnknownState.Opened"));
-  } else if (controls_state == CookieControlsState::k3pcsBlocked) {
+  } else if (controls_state == CookieControlsState::kBlocked3pc) {
     base::RecordAction(
         base::UserMetricsAction("CookieControls.Bubble.CookiesBlocked.Opened"));
   } else {
@@ -184,7 +184,7 @@
 }
 
 int CookieControlsIconView::GetLabelForStatus() const {
-  if (controls_state_ == CookieControlsState::k3pcsAllowed) {
+  if (controls_state_ == CookieControlsState::kAllowed3pc) {
     return IDS_COOKIE_CONTROLS_PAGE_ACTION_COOKIES_ALLOWED_LABEL;
   } else if (blocking_status_ == CookieBlocking3pcdStatus::kLimited) {
     return IDS_COOKIE_CONTROLS_PAGE_ACTION_COOKIES_LIMITED_LABEL;
@@ -250,7 +250,7 @@
   custom_tooltip_text_ = l10n_util::GetStringUTF16(GetLabelForStatus());
   SetTooltipText(custom_tooltip_text_);
 
-  if (controls_state_ == CookieControlsState::k3pcsBlocked &&
+  if (controls_state_ == CookieControlsState::kBlocked3pc &&
       should_highlight_) {
     if (blocking_status_ == CookieBlocking3pcdStatus::kNotIn3pcd) {
       MaybeShowIPH();
@@ -333,7 +333,7 @@
 }
 
 const gfx::VectorIcon& CookieControlsIconView::GetVectorIcon() const {
-  return controls_state_ == CookieControlsState::k3pcsBlocked
+  return controls_state_ == CookieControlsState::kBlocked3pc
              ? views::kEyeCrossedRefreshIcon
              : views::kEyeRefreshIcon;
 }
diff --git a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view_unittest.cc b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view_unittest.cc
index 9be861c..1905a0d 100644
--- a/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view_unittest.cc
+++ b/chrome/browser/ui/views/location_bar/cookie_controls/cookie_controls_icon_view_unittest.cc
@@ -158,7 +158,7 @@
 TEST_P(CookieControlsIconViewUnitTest,
        IconAnimatesWhenShouldHighlightIsTrueAndProtectionsAreOn) {
   view_->OnCookieControlsIconStatusChanged(
-      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*icon_visible=*/true, CookieControlsState::kBlocked3pc, GetParam(),
       /*should_highlight=*/true);
   FlushEvents();
   EXPECT_TRUE(Visible());
@@ -181,7 +181,7 @@
 TEST_P(CookieControlsIconViewUnitTest,
        IconAnimatesOnPageReloadWithChangedSettings) {
   view_->OnCookieControlsIconStatusChanged(
-      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*icon_visible=*/true, CookieControlsState::kBlocked3pc, GetParam(),
       /*should_highlight=*/false);
   FlushEvents();
   ExecuteIcon();
@@ -200,7 +200,7 @@
 TEST_P(CookieControlsIconViewUnitTest,
        IconAnimationTextDoesNotResetWhenProtectionsDoNotChange) {
   view_->OnCookieControlsIconStatusChanged(
-      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*icon_visible=*/true, CookieControlsState::kBlocked3pc, GetParam(),
       /*should_highlight=*/true);
   FlushEvents();
   EXPECT_TRUE(Visible());
@@ -208,7 +208,7 @@
   EXPECT_EQ(LabelText(), In3pcd() ? SiteNotWorkingLabel() : BlockedLabel());
 
   view_->OnCookieControlsIconStatusChanged(
-      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*icon_visible=*/true, CookieControlsState::kBlocked3pc, GetParam(),
       /*should_highlight=*/true);
   FlushEvents();
   EXPECT_EQ(LabelText(), In3pcd() ? SiteNotWorkingLabel() : BlockedLabel());
@@ -217,7 +217,7 @@
 TEST_P(CookieControlsIconViewUnitTest,
        IconAnimationTextUpdatesWhenProtectionsChange) {
   view_->OnCookieControlsIconStatusChanged(
-      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*icon_visible=*/true, CookieControlsState::kBlocked3pc, GetParam(),
       /*should_highlight=*/true);
   FlushEvents();
   EXPECT_TRUE(Visible());
@@ -225,7 +225,7 @@
   EXPECT_EQ(LabelText(), In3pcd() ? SiteNotWorkingLabel() : BlockedLabel());
 
   view_->OnCookieControlsIconStatusChanged(
-      /*icon_visible=*/true, CookieControlsState::k3pcsAllowed, GetParam(),
+      /*icon_visible=*/true, CookieControlsState::kAllowed3pc, GetParam(),
       /*should_highlight=*/true);
   FlushEvents();
   EXPECT_EQ(LabelText(), AllowedLabel());
@@ -233,7 +233,7 @@
 
 TEST_P(CookieControlsIconViewUnitTest, IconAnimationIsResetOnWebContentChange) {
   view_->OnCookieControlsIconStatusChanged(
-      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*icon_visible=*/true, CookieControlsState::kBlocked3pc, GetParam(),
       /*should_highlight=*/true);
   FlushEvents();
   EXPECT_TRUE(Visible());
@@ -246,7 +246,7 @@
   // Simulate a change in web content.
   view_->UpdateImpl();
   view_->OnCookieControlsIconStatusChanged(
-      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*icon_visible=*/true, CookieControlsState::kBlocked3pc, GetParam(),
       /*should_highlight=*/false);
   FlushEvents();
   ExecuteIcon();
@@ -261,7 +261,7 @@
 
 TEST_P(CookieControlsIconViewUnitTest, HidingIconDoesNotRetriggerA11yReadOut) {
   view_->OnCookieControlsIconStatusChanged(
-      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*icon_visible=*/true, CookieControlsState::kBlocked3pc, GetParam(),
       /*should_highlight=*/true);
   FlushEvents();
   EXPECT_TRUE(Visible());
@@ -277,7 +277,7 @@
   EXPECT_EQ(user_actions_.GetActionCount(kUMAIconAnimated), 1);
   EXPECT_EQ(user_actions_.GetActionCount(kUMAIconShown), 0);
   view_->OnCookieControlsIconStatusChanged(
-      /*icon_visible=*/false, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*icon_visible=*/false, CookieControlsState::kBlocked3pc, GetParam(),
       /*should_highlight=*/false);
   FlushEvents();
   EXPECT_FALSE(Visible());
@@ -297,7 +297,7 @@
 TEST_P(CookieControlsIconViewUnitTest,
        IconDoesNotAnimateWhenShouldHighlightIsFalse) {
   view_->OnCookieControlsIconStatusChanged(
-      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*icon_visible=*/true, CookieControlsState::kBlocked3pc, GetParam(),
       /*should_highlight=*/false);
   FlushEvents();
   EXPECT_TRUE(Visible());
@@ -309,7 +309,7 @@
 
 TEST_P(CookieControlsIconViewUnitTest, IconHiddenWhenIconVisibleIsFalse) {
   view_->OnCookieControlsIconStatusChanged(
-      /*icon_visible=*/false, CookieControlsState::k3pcsAllowed, GetParam(),
+      /*icon_visible=*/false, CookieControlsState::kAllowed3pc, GetParam(),
       /*should_highlight=*/false);
   FlushEvents();
   EXPECT_FALSE(Visible());
@@ -326,7 +326,7 @@
 TEST_P(CookieControlsIconViewUnitTest,
        RecordsIconOpenMetricWhenProtectionsAreOff) {
   view_->OnCookieControlsIconStatusChanged(
-      /*icon_visible=*/true, CookieControlsState::k3pcsAllowed, GetParam(),
+      /*icon_visible=*/true, CookieControlsState::kAllowed3pc, GetParam(),
       /*should_highlight=*/false);
   FlushEvents();
   EXPECT_TRUE(Visible());
@@ -341,7 +341,7 @@
 TEST_P(CookieControlsIconViewUnitTest,
        RecordsIconOpenMetricWhenProtectionsAreOn) {
   view_->OnCookieControlsIconStatusChanged(
-      /*icon_visible=*/true, CookieControlsState::k3pcsBlocked, GetParam(),
+      /*icon_visible=*/true, CookieControlsState::kBlocked3pc, GetParam(),
       /*should_highlight=*/false);
   FlushEvents();
   EXPECT_EQ(TooltipText(),
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_dialog_browsertest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_dialog_browsertest.cc
index 399d735..996f5a5 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_dialog_browsertest.cc
@@ -840,7 +840,7 @@
       CookieControlsEnforcement::kNoEnforcement;
   CookieBlocking3pcdStatus blocking_status_ =
       CookieBlocking3pcdStatus::kNotIn3pcd;
-  CookieControlsState controls_state_ = CookieControlsState::k3pcsBlocked;
+  CookieControlsState controls_state_ = CookieControlsState::kBlocked3pc;
   bool rws_enabled_ = false;
   bool rws_managed_ = false;
   bool is_temporary_exception_ = false;
@@ -884,7 +884,7 @@
 IN_PROC_BROWSER_TEST_P(PageInfoBubbleViewCookiesSubpageBrowserTest,
                        InvokeUi_CookiesAllowedByCookieSetting) {
   blocking_status_ = GetParam();
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   enforcement_ = CookieControlsEnforcement::kEnforcedByCookieSetting;
   ShowAndVerifyUi();
 }
@@ -893,7 +893,7 @@
                        InvokeUi_TemporaryException) {
   is_temporary_exception_ = true;
   blocking_status_ = GetParam();
-  controls_state_ = CookieControlsState::k3pcsAllowed;
+  controls_state_ = CookieControlsState::kAllowed3pc;
   ShowAndVerifyUi();
 }
 
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
index fd21e73..0129aa99 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
@@ -1260,8 +1260,8 @@
 INSTANTIATE_TEST_SUITE_P(
     All,
     PageInfoBubbleViewCookiesSubpageTitleTest,
-    testing::Combine(testing::Values(CookieControlsState::k3pcsAllowed,
-                                     CookieControlsState::k3pcsBlocked),
+    testing::Combine(testing::Values(CookieControlsState::kAllowed3pc,
+                                     CookieControlsState::kBlocked3pc),
                      testing::Values(CookieBlocking3pcdStatus::kNotIn3pcd,
                                      CookieBlocking3pcdStatus::kAll),
                      /*is_otr*/ testing::Bool()));
diff --git a/chrome/browser/ui/views/page_info/page_info_cookies_content_view.cc b/chrome/browser/ui/views/page_info/page_info_cookies_content_view.cc
index a8ff5cf..e92b86a 100644
--- a/chrome/browser/ui/views/page_info/page_info_cookies_content_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_cookies_content_view.cc
@@ -52,8 +52,8 @@
 // TODO(crbug.com/388294499): Move this logic into the privacy_sandbox/
 // directory.
 bool IsActUi(CookieControlsState controls_state) {
-  return controls_state == CookieControlsState::kTpActive ||
-         controls_state == CookieControlsState::kTpPaused;
+  return controls_state == CookieControlsState::kActiveTp ||
+         controls_state == CookieControlsState::kPausedTp;
 }
 
 class ThirdPartyCookieLabelWrapper : public views::BoxLayoutView {
@@ -288,13 +288,13 @@
   std::u16string title_text;
   int description;
   switch (controls_state) {
-    case CookieControlsState::k3pcsBlocked:
+    case CookieControlsState::kBlocked3pc:
       title_text = l10n_util::GetStringUTF16(
           IDS_PAGE_INFO_COOKIES_SITE_NOT_WORKING_TITLE);
       description =
           IDS_PAGE_INFO_TRACKING_PROTECTION_SITE_NOT_WORKING_DESCRIPTION_TEMPORARY;
       break;
-    case CookieControlsState::k3pcsAllowed:
+    case CookieControlsState::kAllowed3pc:
       if (expiration.is_null() ||
           enforcement == CookieControlsEnforcement::kEnforcedByCookieSetting) {
         // Handle permanent site exception.
@@ -313,13 +313,13 @@
             IDS_PAGE_INFO_TRACKING_PROTECTION_COOKIES_RESTART_DESCRIPTION;
       }
       break;
-    case CookieControlsState::kTpActive:
+    case CookieControlsState::kActiveTp:
       title_text = l10n_util::GetStringUTF16(
           IDS_PAGE_INFO_COOKIES_SITE_NOT_WORKING_TITLE);
       description =
           IDS_TRACKING_PROTECTION_BUBBLE_ACTIVE_PROTECTIONS_DESCRIPTION;
       break;
-    case CookieControlsState::kTpPaused:
+    case CookieControlsState::kPausedTp:
       title_text = l10n_util::GetStringUTF16(
           IDS_TRACKING_PROTECTION_BUBBLE_PAUSED_PROTECTIONS_TITLE);
       description =
@@ -337,7 +337,7 @@
     CookieControlsState controls_state,
     CookieBlocking3pcdStatus blocking_status) {
   std::u16string subtitle;
-  if (controls_state == CookieControlsState::k3pcsBlocked) {
+  if (controls_state == CookieControlsState::kBlocked3pc) {
     subtitle = l10n_util::GetStringUTF16(
         blocking_status == CookieBlocking3pcdStatus::kLimited
             ? IDS_TRACKING_PROTECTION_BUBBLE_3PC_LIMITED_SUBTITLE
@@ -347,7 +347,7 @@
         IDS_TRACKING_PROTECTION_BUBBLE_3PC_ALLOWED_SUBTITLE);
   }
   third_party_cookies_toggle_->SetIsOn(controls_state ==
-                                       CookieControlsState::k3pcsAllowed);
+                                       CookieControlsState::kAllowed3pc);
   third_party_cookies_toggle_->SetID(
       PageInfoViewFactory::VIEW_ID_PAGE_INFO_THIRD_PARTY_COOKIES_TOGGLE);
   third_party_cookies_toggle_->GetViewAccessibility().SetName(subtitle);
@@ -357,7 +357,7 @@
 void PageInfoCookiesContentView::SetTrackingProtectionButtonLabel(
     CookieControlsState controls_state) {
   auto label = l10n_util::GetStringUTF16(
-      controls_state == CookieControlsState::kTpPaused
+      controls_state == CookieControlsState::kPausedTp
           ? IDS_TRACKING_PROTECTION_BUBBLE_RESUME_PROTECTIONS_LABEL
           : IDS_TRACKING_PROTECTION_BUBBLE_PAUSE_PROTECTIONS_LABEL);
   tracking_protection_button_->SetText(label);
@@ -368,7 +368,7 @@
     CookieControlsEnforcement enforcement,
     CookieControlsState controls_state) {
   // No description exists for when protections are paused.
-  if (controls_state == CookieControlsState::kTpPaused) {
+  if (controls_state == CookieControlsState::kPausedTp) {
     cookies_description_label_->SetVisible(false);
     return;
   }
@@ -436,7 +436,7 @@
                                           blocking_status, expiration);
   SetThirdPartyCookiesToggle(controls_state, blocking_status);
   third_party_cookies_row_->SetIcon(GetThirdPartyCookiesIcon(
-      controls_state == CookieControlsState::k3pcsAllowed));
+      controls_state == CookieControlsState::kAllowed3pc));
   third_party_cookies_row_->SetID(
       PageInfoViewFactory::VIEW_ID_PAGE_INFO_THIRD_PARTY_COOKIES_ROW);
 
diff --git a/chrome/browser/ui/views/page_info/page_info_cookies_content_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_cookies_content_view_unittest.cc
index d467c83..7213bcf9 100644
--- a/chrome/browser/ui/views/page_info/page_info_cookies_content_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_cookies_content_view_unittest.cc
@@ -53,7 +53,7 @@
   cookie_info.expiration =
       days_to_expiration ? base::Time::Now() + base::Days(days_to_expiration)
                          : base::Time();
-  cookie_info.controls_state = CookieControlsState::k3pcsBlocked;
+  cookie_info.controls_state = CookieControlsState::kBlocked3pc;
   cookie_info.enforcement = CookieControlsEnforcement::kNoEnforcement;
   cookie_info.blocking_status = CookieBlocking3pcdStatus::kNotIn3pcd;
   cookie_info.is_incognito = false;
@@ -219,7 +219,7 @@
        ThirdPartyCookiesAllowedPermanent) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   content_view()->SetCookieInfo(cookie_info);
 
   // Third-party cookies section:
@@ -254,7 +254,7 @@
        ThirdPartyCookiesAllowedTemporary) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests(kDaysToExpiration);
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   content_view()->SetCookieInfo(cookie_info);
 
   // Third-party cookies section:
@@ -322,7 +322,7 @@
        ThirdPartyCookiesAllowedByPolicy) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   cookie_info.enforcement = CookieControlsEnforcement::kEnforcedByPolicy;
   content_view()->SetCookieInfo(cookie_info);
 
@@ -389,7 +389,7 @@
        ThirdPartyCookiesAllowedByExtension) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   cookie_info.enforcement = CookieControlsEnforcement::kEnforcedByExtension;
   content_view()->SetCookieInfo(cookie_info);
 
@@ -459,7 +459,7 @@
        ThirdPartyCookiesAllowedBySetting) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   cookie_info.enforcement = CookieControlsEnforcement::kEnforcedByCookieSetting;
   content_view()->SetCookieInfo(cookie_info);
 
@@ -498,7 +498,7 @@
        DisplaysTitleAndDescriptionWhenCookiesLimitedWithTemporaryException) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests(kDaysToExpiration);
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   cookie_info.blocking_status = CookieBlocking3pcdStatus::kLimited;
   content_view()->SetCookieInfo(cookie_info);
 
@@ -515,7 +515,7 @@
        DisplaysTitleAndDescriptionWhenCookiesBlockedWithTemporaryException) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests(kDaysToExpiration);
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   cookie_info.blocking_status = CookieBlocking3pcdStatus::kAll;
   content_view()->SetCookieInfo(cookie_info);
 
@@ -583,7 +583,7 @@
        DisplaysLabelsWhenCookiesAllowedInIncognitoMode) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   cookie_info.blocking_status = CookieBlocking3pcdStatus::kAll;
   cookie_info.is_incognito = true;
   content_view()->SetCookieInfo(cookie_info);
@@ -607,7 +607,7 @@
        DisplaysDescriptionWhenCookiesAllowedEnforcedByTpcdGrant) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   cookie_info.blocking_status = CookieBlocking3pcdStatus::kLimited;
   cookie_info.enforcement = CookieControlsEnforcement::kEnforcedByTpcdGrant;
   cookie_info.controls_state = CookieControlsState::kHidden;
@@ -640,7 +640,7 @@
        DisplaysTitleAndDescriptionWhenCookiesAllowedWithPermanentException) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   cookie_info.blocking_status = GetParam();
   content_view()->SetCookieInfo(cookie_info);
 
@@ -753,7 +753,7 @@
        DisplaysOnToggleWhenCookiesAllowedInIncognitoMode) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   cookie_info.blocking_status = CookieBlocking3pcdStatus::kAll;
   cookie_info.is_incognito = true;
   content_view()->SetCookieInfo(cookie_info);
@@ -775,7 +775,7 @@
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
   cookie_info.blocking_status = GetParam();
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   content_view()->SetCookieInfo(cookie_info);
 
   EXPECT_TRUE(third_party_cookies_label_wrapper()->GetVisible());
@@ -795,7 +795,7 @@
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
   cookie_info.enforcement = CookieControlsEnforcement::kEnforcedByCookieSetting;
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   cookie_info.blocking_status = GetParam();
   content_view()->SetCookieInfo(cookie_info);
 
@@ -822,7 +822,7 @@
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
   cookie_info.enforcement = CookieControlsEnforcement::kEnforcedByPolicy;
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   cookie_info.blocking_status = GetParam();
   content_view()->SetCookieInfo(cookie_info);
 
@@ -842,7 +842,7 @@
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
   cookie_info.enforcement = CookieControlsEnforcement::kEnforcedByExtension;
-  cookie_info.controls_state = CookieControlsState::k3pcsAllowed;
+  cookie_info.controls_state = CookieControlsState::kAllowed3pc;
   cookie_info.blocking_status = GetParam();
   content_view()->SetCookieInfo(cookie_info);
 
@@ -869,7 +869,7 @@
        DisplaysPauseProtectionsUiWhenProtectionsActive) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
-  cookie_info.controls_state = CookieControlsState::kTpActive;
+  cookie_info.controls_state = CookieControlsState::kActiveTp;
   content_view()->SetCookieInfo(cookie_info);
 
   EXPECT_TRUE(third_party_cookies_label_wrapper()->GetVisible());
@@ -894,7 +894,7 @@
     DisplaysPauseProtectionsUiWhenProtectionsActiveAnd3pcsAllowedByEnterprisePolicy) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
-  cookie_info.controls_state = CookieControlsState::kTpActive;
+  cookie_info.controls_state = CookieControlsState::kActiveTp;
   cookie_info.enforcement = CookieControlsEnforcement::kEnforcedByPolicy;
   content_view()->SetCookieInfo(cookie_info);
 
@@ -921,7 +921,7 @@
     DisplaysPauseProtectionsUiWhenProtectionsActiveAnd3pcsAllowedByExtension) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
-  cookie_info.controls_state = CookieControlsState::kTpActive;
+  cookie_info.controls_state = CookieControlsState::kActiveTp;
   cookie_info.enforcement = CookieControlsEnforcement::kEnforcedByExtension;
   content_view()->SetCookieInfo(cookie_info);
 
@@ -948,7 +948,7 @@
     DisplaysPauseProtectionsUiWhenProtectionsActiveAnd3pcsAllowedByCookieSetting) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
-  cookie_info.controls_state = CookieControlsState::kTpActive;
+  cookie_info.controls_state = CookieControlsState::kActiveTp;
   cookie_info.enforcement = CookieControlsEnforcement::kEnforcedByCookieSetting;
   content_view()->SetCookieInfo(cookie_info);
 
@@ -973,7 +973,7 @@
        DisplaysResumeProtectionsUiWhenProtectionsPaused) {
   PageInfoCookiesContentView::CookiesNewInfo cookie_info =
       DefaultCookieInfoForTests();
-  cookie_info.controls_state = CookieControlsState::kTpPaused;
+  cookie_info.controls_state = CookieControlsState::kPausedTp;
   content_view()->SetCookieInfo(cookie_info);
 
   EXPECT_TRUE(third_party_cookies_label_wrapper()->GetVisible());
diff --git a/chrome/browser/ui/views/profiles/history_sync_optin_ui_browsertest.cc b/chrome/browser/ui/views/profiles/history_sync_optin_ui_browsertest.cc
new file mode 100644
index 0000000..624d10c
--- /dev/null
+++ b/chrome/browser/ui/views/profiles/history_sync_optin_ui_browsertest.cc
@@ -0,0 +1,78 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/signin/signin_browser_test_base.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/test/test_browser_dialog.h"
+#include "chrome/browser/ui/test/test_browser_ui.h"
+#include "chrome/browser/ui/views/profiles/profiles_pixel_test_utils.h"
+#include "chrome/common/webui_url_constants.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "components/signin/public/base/signin_switches.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "ui/views/widget/any_widget_observer.h"
+
+// Tests for the chrome://history-sync-optin WebUI page.
+namespace {
+
+std::string ParamToTestSuffix(
+    const testing::TestParamInfo<PixelTestParam>& info) {
+  return info.param.test_suffix;
+}
+
+const PixelTestParam kDialogTestParams[] = {
+    {.test_suffix = "Regular"},
+    {.test_suffix = "DarkTheme", .use_dark_theme = true},
+    /* TODO(crbug.com/406751006): Until the strings are translatable the RTL
+       language does not fully apply. */
+    {.test_suffix = "Rtl", .use_right_to_left_language = true},
+};
+}  // namespace
+
+class HistorySyncOptinUIDialogPixelTest
+    : public ProfilesPixelTestBaseT<DialogBrowserTest>,
+      public testing::WithParamInterface<PixelTestParam> {
+ public:
+  HistorySyncOptinUIDialogPixelTest()
+      : ProfilesPixelTestBaseT<DialogBrowserTest>(GetParam()) {}
+
+  ~HistorySyncOptinUIDialogPixelTest() override = default;
+
+  // DialogBrowserTest:
+  void ShowUi(const std::string& name) override {
+    DCHECK(browser());
+
+    SignInWithAccount();
+    auto target_url = GURL(chrome::kChromeUIHistorySyncOptinURL);
+    content::TestNavigationObserver observer(target_url);
+    observer.StartWatchingNewWebContents();
+
+    // ShowUi() can sometimes return before the dialog widget is shown because
+    // the call to show the latter is asynchronous. Adding
+    // NamedWidgetShownWaiter will prevent that from happening.
+    views::NamedWidgetShownWaiter widget_waiter(
+        views::test::AnyWidgetTestPasskey{},
+        "SigninViewControllerDelegateViews");
+
+    auto* controller = browser()->signin_view_controller();
+    controller->ShowModalHistorySyncOptInDialog();
+    widget_waiter.WaitIfNeededAndGet();
+    observer.Wait();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list{
+      switches::kEnableHistorySyncOptin};
+};
+
+IN_PROC_BROWSER_TEST_P(HistorySyncOptinUIDialogPixelTest, InvokeUi_default) {
+  ShowAndVerifyUi();
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+                         HistorySyncOptinUIDialogPixelTest,
+                         testing::ValuesIn(kDialogTestParams),
+                         &ParamToTestSuffix);
diff --git a/chrome/browser/ui/views/web_apps/force_installed_deprecated_apps_dialog_view_browsertest.cc b/chrome/browser/ui/views/web_apps/force_installed_deprecated_apps_dialog_view_browsertest.cc
index 1363d19..2da01de4 100644
--- a/chrome/browser/ui/views/web_apps/force_installed_deprecated_apps_dialog_view_browsertest.cc
+++ b/chrome/browser/ui/views/web_apps/force_installed_deprecated_apps_dialog_view_browsertest.cc
@@ -5,6 +5,7 @@
 #include "base/feature_list.h"
 #include "base/strings/stringprintf.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
+#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/views/web_apps/force_installed_preinstalled_deprecated_app_dialog_view.h"
 #include "chrome/browser/ui/webui/app_home/app_home.mojom.h"
 #include "chrome/browser/ui/webui/app_home/app_home_page_handler.h"
diff --git a/chrome/browser/ui/webui/app_home/BUILD.gn b/chrome/browser/ui/webui/app_home/BUILD.gn
index 8f1488ef..b56d6553 100644
--- a/chrome/browser/ui/webui/app_home/BUILD.gn
+++ b/chrome/browser/ui/webui/app_home/BUILD.gn
@@ -42,18 +42,16 @@
 
 source_set("app_home") {
   sources = [
-    "app_home_page_handler.cc",
     "app_home_page_handler.h",
-    "app_home_ui.cc",
     "app_home_ui.h",
   ]
 
   public_deps = [
     ":mojo_bindings",
     "//base",
-    "//chrome/browser:browser_public_dependencies",
     "//chrome/browser/extensions",
     "//chrome/browser/profiles:profile",
+    "//chrome/browser/ui/extensions:extension_enable_flow_delegate",
     "//chrome/browser/web_applications",
     "//chrome/browser/web_applications/mojom:mojom_web_apps_enum",
     "//chrome/common",
@@ -66,19 +64,39 @@
     "//ui/base",
     "//ui/webui",
   ]
+}
+
+source_set("impl") {
+  sources = [
+    "app_home_page_handler.cc",
+    "app_home_ui.cc",
+  ]
+
+  public_deps = [ "//chrome/browser:browser_public_dependencies" ]
 
   deps = [
-    ":mojo_bindings_shared_cpp_sources",
+    ":app_home",
+    ":mojo_bindings",
+    "//base",
     "//chrome/app:generated_resources",
     "//chrome/app/theme:theme_resources",
     "//chrome/browser/apps/app_service",
     "//chrome/browser/apps/app_service:constants",
+    "//chrome/browser/extensions",
+    "//chrome/browser/profiles:profile",
     "//chrome/browser/resources/app_home:resources",
-    "//chrome/browser/resources/app_home:resources_grit",
+    "//chrome/browser/web_applications",
+    "//chrome/browser/web_applications/mojom:mojom_web_apps_enum",
     "//chrome/browser/web_applications/proto",
+    "//chrome/common",
     "//chrome/common:chrome_features",
+    "//chrome/common/extensions",
     "//components/webapps/browser:constants",
+    "//content/public/browser",
+    "//extensions/browser",
     "//net",
+    "//ui/base",
+    "//ui/webui",
     "//url",
   ]
 }
@@ -92,6 +110,8 @@
       "mock_app_home_page.h",
     ]
 
+    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
+
     public_deps = [
       ":mojo_bindings",
       "//mojo/public/cpp/bindings",
@@ -118,7 +138,5 @@
       "//ui/views",
       "//ui/views:test_support",
     ]
-
-    defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
   }
 }
diff --git a/chrome/browser/ui/webui/app_home/app_home_page_handler.cc b/chrome/browser/ui/webui/app_home/app_home_page_handler.cc
index 784282ad..d36a4c2 100644
--- a/chrome/browser/ui/webui/app_home/app_home_page_handler.cc
+++ b/chrome/browser/ui/webui/app_home/app_home_page_handler.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/extensions/extension_ui_util.h"
 #include "chrome/browser/extensions/launch_util.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
diff --git a/chrome/browser/ui/webui/app_home/app_home_page_handler.h b/chrome/browser/ui/webui/app_home/app_home_page_handler.h
index 3ebc6a48..101fbc3 100644
--- a/chrome/browser/ui/webui/app_home/app_home_page_handler.h
+++ b/chrome/browser/ui/webui/app_home/app_home_page_handler.h
@@ -9,7 +9,6 @@
 #include "base/memory/raw_ref.h"
 #include "chrome/browser/extensions/extension_uninstall_dialog.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/extensions/extension_enable_flow_delegate.h"
 #include "chrome/browser/ui/webui/app_home/app_home.mojom.h"
 #include "chrome/browser/web_applications/mojom/user_display_mode.mojom.h"
@@ -26,6 +25,7 @@
 
 static_assert(BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX));
 
+class Browser;
 class ExtensionEnableFlow;
 
 namespace content {
diff --git a/chrome/browser/ui/webui/app_home/app_home_page_handler_browsertest.cc b/chrome/browser/ui/webui/app_home/app_home_page_handler_browsertest.cc
index af8ae4f3..573a956e 100644
--- a/chrome/browser/ui/webui/app_home/app_home_page_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/app_home/app_home_page_handler_browsertest.cc
@@ -11,6 +11,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/test_future.h"
 #include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/views/create_application_shortcut_view_test_support.h"
 #include "chrome/browser/ui/web_applications/web_app_browsertest_base.h"
diff --git a/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.cc b/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.cc
index a2811b47..4b90c21 100644
--- a/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.cc
+++ b/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler.cc
@@ -245,6 +245,12 @@
   }
 
   Profile* profile = Profile::FromWebUI(web_ui());
+  // Incognito profile should not show the upload button. The action is
+  // possible, but it should not be promoted.
+  if (profile->IsOffTheRecord()) {
+    return false;
+  }
+
   bookmarks::BookmarkModel* model =
       BookmarkModelFactory::GetForBrowserContext(profile);
   const bookmarks::BookmarkNode* node =
diff --git a/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler_browsertest.cc b/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler_browsertest.cc
index 39444cf..3b2b0f3 100644
--- a/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler_browsertest.cc
+++ b/chrome/browser/ui/webui/bookmarks/bookmarks_message_handler_browsertest.cc
@@ -34,14 +34,7 @@
   void SetUpOnMainThread() override {
     InProcessBrowserTest::SetUpOnMainThread();
 
-    webui_contents_ = content::WebContents::Create(
-        content::WebContents::CreateParams(browser()->profile()));
-    web_ui_.set_web_contents(webui_contents_.get());
-
-    auto handler_owner = std::make_unique<BookmarksMessageHandler>();
-    handler_ = handler_owner.get();
-    web_ui_.AddMessageHandler(std::move(handler_owner));
-
+    ResetWithProfile(browser()->profile());
     SignInAndEnableAccountBookmarkNodes(browser()->profile());
   }
 
@@ -60,6 +53,16 @@
     handler_->HandleSingleUploadClicked(args);
   }
 
+  void ResetWithProfile(Profile* profile) {
+    webui_contents_ = content::WebContents::Create(
+        content::WebContents::CreateParams(profile));
+    web_ui_.set_web_contents(webui_contents_.get());
+
+    auto handler_owner = std::make_unique<BookmarksMessageHandler>();
+    handler_ = handler_owner.get();
+    web_ui_.AddMessageHandler(std::move(handler_owner));
+  }
+
  private:
   base::test::ScopedFeatureList scoped_feature_list_{
       switches::kSyncEnableBookmarksInTransportMode};
@@ -204,6 +207,25 @@
 }
 
 IN_PROC_BROWSER_TEST_F(BookmarkMessageHandlerTest,
+                       CanNotUploadInIncognitoMode) {
+  // Add a bookmark that can be uploaded.
+  Profile* original_profile = browser()->profile();
+  bookmarks::BookmarkModel* model =
+      BookmarkModelFactory::GetForBrowserContext(original_profile);
+  bookmarks::test::WaitForBookmarkModelToLoad(model);
+  const bookmarks::BookmarkNode* node = model->AddURL(
+      model->other_node(), 0, std::u16string(), GURL("http://test.com"));
+  const std::string id_string = base::NumberToString(node->id());
+  ASSERT_TRUE(SendCanUploadBookmarkToAccountStorage(id_string));
+
+  // Simulate opening the webui in Incognito mode.
+  Profile* otr_profile =
+      original_profile->GetPrimaryOTRProfile(/*create_if_needed*/ true);
+  ResetWithProfile(otr_profile);
+  EXPECT_FALSE(SendCanUploadBookmarkToAccountStorage(id_string));
+}
+
+IN_PROC_BROWSER_TEST_F(BookmarkMessageHandlerTest,
                        SingleUploadClickedOpensDialog) {
   // Add a bookmark that can be uploaded.
   bookmarks::BookmarkModel* model =
diff --git a/chrome/browser/user_bypass/OWNERS b/chrome/browser/user_bypass/OWNERS
index efc5da0..96b16d3e 100644
--- a/chrome/browser/user_bypass/OWNERS
+++ b/chrome/browser/user_bypass/OWNERS
@@ -1,6 +1,5 @@
 bcb@chromium.org
 bcl@chromium.org
-bingler@chromium.org
 kyraseevers@chromium.org
 njeunje@chromium.org
 rtarpine@chromium.org
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt
index 59d45ab..a908884 100644
--- a/chrome/build/linux.pgo.txt
+++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@
-chrome-linux-main-1747611665-f7c17e948ffe1ec63515ac197fb69fa41b7c7062-eb31723f6288dbc2adbecede6503736c1efd24f4.profdata
+chrome-linux-main-1747634276-b8247d5a3b655bb61025b7de8e8f38edbde0eb69-be030cf7971d80122800ccf737c4610b0ac12844.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt
index 8f23dfd..8f0a696c 100644
--- a/chrome/build/mac-arm.pgo.txt
+++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@
-chrome-mac-arm-main-1747634276-79d16d88f9eb8dfd1a36e5e43a3adf2cbb8c7b7c-be030cf7971d80122800ccf737c4610b0ac12844.profdata
+chrome-mac-arm-main-1747655833-af543b571b37b1a178f69bac76b0ecf0cbe877a2-f2498918734bb923018de2507fd64012ac5e4a9a.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 1018db3..9f568248 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1747611665-b9b05a9ca768f3e79bd661947406ff88b013fcb2-eb31723f6288dbc2adbecede6503736c1efd24f4.profdata
+chrome-mac-main-1747634276-d4ffdd15e1303db77e49fefb18fe7997f4530d78-be030cf7971d80122800ccf737c4610b0ac12844.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index bfdbb0b..6008109 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1747611665-dd46886f68784027e73e2ac238d8d4ffd884de75-eb31723f6288dbc2adbecede6503736c1efd24f4.profdata
+chrome-win32-main-1747634276-1b72df698cb87b68b6da0ffe1252e0ffa52165d8-be030cf7971d80122800ccf737c4610b0ac12844.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 088183a..253288d 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1747600991-0b219de4f5f5b6c42bc29552f518032b057898de-b755dc472e5411f7b49a54c998acada19c8c68d3.profdata
+chrome-win64-main-1747611665-22bc6c14e48389ca46d871e2315c17d583a66d68-eb31723f6288dbc2adbecede6503736c1efd24f4.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index c93d4f38..262a8ff 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -1164,6 +1164,11 @@
         &kSafetyHubDisruptiveNotificationRevocation,
         /*name=*/"max_engagement_score", /*default_value=*/0.0};
 
+constexpr base::FeatureParam<base::TimeDelta>
+    kSafetyHubDisruptiveNotificationRevocationWaitingTimeAsProposed{
+        &kSafetyHubDisruptiveNotificationRevocation,
+        /*name=*/"waiting_time_as_proposed", /*default_value=*/base::Days(0)};
+
 constexpr base::FeatureParam<int>
     kSafetyHubDisruptiveNotificationRevocationNotificationTimeoutSeconds{
         &kSafetyHubDisruptiveNotificationRevocation,
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index 25e4731..02b18dc 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -706,6 +706,14 @@
 extern const base::FeatureParam<double>
     kSafetyHubDisruptiveNotificationRevocationMaxEngagementScore;
 
+// The waiting time for a website classified as sending disruptive notifications
+// before notification permission is revoked. The website has to satisfy the
+// disruptive requirements for this amount of time before the revocation is
+// actually enforced.
+COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::FeatureParam<base::TimeDelta>
+    kSafetyHubDisruptiveNotificationRevocationWaitingTimeAsProposed;
+
 // Timeout in seconds for the Safety Hub OS notification informing users about
 // revoked notification permissions.
 COMPONENT_EXPORT(CHROME_FEATURES)
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index e977b01..7f4082db 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4191,6 +4191,7 @@
 
     if (is_win || is_linux) {
       sources += [
+        "../browser/ui/views/profiles/history_sync_optin_ui_browsertest.cc",
         "../browser/ui/views/profiles/managed_user_profile_notice_ui_browsertest.cc",
         "../browser/ui/views/profiles/profile_menu_view_ui_browsertest.cc",
         "../browser/ui/views/profiles/profile_picker_ui_browsertest.cc",
@@ -4486,7 +4487,10 @@
         "../browser/user_bypass/user_bypass_web_contents_observer_browsertest.cc",
       ]
 
-      public_deps += [ "//chrome/test/supervised_user:live_tests" ]
+      public_deps += [
+        "//chrome/browser/ui/extensions:extension_enable_flow_delegate",
+        "//chrome/test/supervised_user:live_tests",
+      ]
 
       data += [
         "//chrome/test/data/controlled_frame/resources/expected_properties.json",
@@ -6640,6 +6644,7 @@
     "//chrome/browser/privacy_budget:unit_tests",
     "//chrome/browser/privacy_sandbox",
     "//chrome/browser/privacy_sandbox:unit_tests",
+    "//chrome/browser/privacy_sandbox/incognito:unit_tests",
     "//chrome/browser/profiles",
 
     # TODO(413315837): Remove this dep when privacy_sandbox_service_impl_unittest.cc
diff --git a/chrome/test/base/test_launcher_utils.cc b/chrome/test/base/test_launcher_utils.cc
index 9bcf639f..b3d4b14 100644
--- a/chrome/test/base/test_launcher_utils.cc
+++ b/chrome/test/base/test_launcher_utils.cc
@@ -29,13 +29,9 @@
 namespace test_launcher_utils {
 
 void PrepareBrowserCommandLineForTests(base::CommandLine* command_line) {
-  // Don't show the first run ui.
+  // Don't show the first run ui and disable the default browser check.
   command_line->AppendSwitch(switches::kNoFirstRun);
 
-  // No default browser check, it would create an info-bar (if we are not the
-  // default browser) that could conflicts with some tests expectations.
-  command_line->AppendSwitch(switches::kNoDefaultBrowserCheck);
-
   // Enable info level logging to stderr by default so that we can see when bad
   // stuff happens, but honor the flags specified from the command line. Use the
   // default logging level (INFO) instead of explicitly passing
diff --git a/clank b/clank
index 2d55e0d..0f0cb77b 160000
--- a/clank
+++ b/clank
@@ -1 +1 @@
-Subproject commit 2d55e0d9182a400258fe4f8cd1c5ba1f882cf328
+Subproject commit 0f0cb77b3c4c1d3eda905d257d2a612ceae472e1
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 35dc664..9d9799773 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -595,10 +595,10 @@
     "studies/autofill_experiments.h",
     "suggestions/addresses/address_suggestion_generator.cc",
     "suggestions/addresses/address_suggestion_generator.h",
+    "suggestions/payments/iban_suggestion_generator.cc",
+    "suggestions/payments/iban_suggestion_generator.h",
     "suggestions/payments/payments_suggestion_generator.cc",
     "suggestions/payments/payments_suggestion_generator.h",
-    "suggestions/single_fields/iban_suggestion_generator.cc",
-    "suggestions/single_fields/iban_suggestion_generator.h",
     "suggestions/suggestion.cc",
     "suggestions/suggestion.h",
     "suggestions/suggestion_generator.cc",
@@ -1432,8 +1432,8 @@
     "studies/autofill_ablation_study_unittest.cc",
     "studies/autofill_experiments_unittest.cc",
     "suggestions/addresses/address_suggestion_generator_unittest.cc",
+    "suggestions/payments/iban_suggestion_generator_unittest.cc",
     "suggestions/payments/payments_suggestion_generator_unittest.cc",
-    "suggestions/single_fields/iban_suggestion_generator_unittest.cc",
     "suggestions/valuables/valuable_suggestion_generator_unittest.cc",
     "ui/addresses/autofill_address_util_unittest.cc",
     "ui/autofill_external_delegate_unittest.cc",
diff --git a/components/autofill/core/browser/filling/filling_product.cc b/components/autofill/core/browser/filling/filling_product.cc
index 73bebc1e..240c504 100644
--- a/components/autofill/core/browser/filling/filling_product.cc
+++ b/components/autofill/core/browser/filling/filling_product.cc
@@ -52,6 +52,7 @@
     case SuggestionType::kDevtoolsTestAddressByCountry:
     case SuggestionType::kDevtoolsTestAddressEntry:
     case SuggestionType::kManageAddress:
+    case SuggestionType::kHomeAndWorkAddressEntry:
       return FillingProduct::kAddress;
     case SuggestionType::kBnplEntry:
     case SuggestionType::kCreditCardEntry:
diff --git a/components/autofill/core/browser/form_parsing/internal_resources b/components/autofill/core/browser/form_parsing/internal_resources
index 3fb8783..0ba678b 160000
--- a/components/autofill/core/browser/form_parsing/internal_resources
+++ b/components/autofill/core/browser/form_parsing/internal_resources
@@ -1 +1 @@
-Subproject commit 3fb8783d75611c5ada2ee094ad2be8331e9bd233
+Subproject commit 0ba678b370b060940d11c7b7b19edfc37addd02a
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager.cc b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
index ed5bedd..0af868e 100644
--- a/components/autofill/core/browser/foundations/browser_autofill_manager.cc
+++ b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
@@ -125,8 +125,8 @@
 #include "components/autofill/core/browser/single_field_fillers/payments/merchant_promo_code_manager.h"
 #include "components/autofill/core/browser/studies/autofill_experiments.h"
 #include "components/autofill/core/browser/suggestions/addresses/address_suggestion_generator.h"
+#include "components/autofill/core/browser/suggestions/payments/iban_suggestion_generator.h"
 #include "components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h"
-#include "components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.h"
 #include "components/autofill/core/browser/suggestions/suggestion.h"
 #include "components/autofill/core/browser/suggestions/suggestion_hiding_reason.h"
 #include "components/autofill/core/browser/suggestions/suggestion_type.h"
@@ -308,6 +308,7 @@
     case SuggestionType::kDevtoolsTestAddressEntry:
     case SuggestionType::kFillAutofillAi:
     case SuggestionType::kPendingStateSignin:
+    case SuggestionType::kHomeAndWorkAddressEntry:
       NOTREACHED();
   }
   NOTREACHED();
diff --git a/components/autofill/core/browser/payments/iban_manager.cc b/components/autofill/core/browser/payments/iban_manager.cc
index 8235f54..fef4ca3 100644
--- a/components/autofill/core/browser/payments/iban_manager.cc
+++ b/components/autofill/core/browser/payments/iban_manager.cc
@@ -11,8 +11,8 @@
 #include "components/autofill/core/browser/foundations/browser_autofill_manager.h"
 #include "components/autofill/core/browser/integrators/optimization_guide/autofill_optimization_guide.h"
 #include "components/autofill/core/browser/metrics/payments/iban_metrics.h"
+#include "components/autofill/core/browser/suggestions/payments/iban_suggestion_generator.h"
 #include "components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.h"
-#include "components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.h"
 #include "components/autofill/core/common/autofill_clock.h"
 
 namespace autofill {
diff --git a/components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.cc b/components/autofill/core/browser/suggestions/payments/iban_suggestion_generator.cc
similarity index 97%
rename from components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.cc
rename to components/autofill/core/browser/suggestions/payments/iban_suggestion_generator.cc
index 4402e06..567c909a 100644
--- a/components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/payments/iban_suggestion_generator.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.h"
+#include "components/autofill/core/browser/suggestions/payments/iban_suggestion_generator.h"
 
 #include "base/containers/to_vector.h"
 #include "base/barrier_callback.h"
diff --git a/components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.h b/components/autofill/core/browser/suggestions/payments/iban_suggestion_generator.h
similarity index 85%
rename from components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.h
rename to components/autofill/core/browser/suggestions/payments/iban_suggestion_generator.h
index f1488843..23efa27 100644
--- a/components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.h
+++ b/components/autofill/core/browser/suggestions/payments/iban_suggestion_generator.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 COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTIONS_SINGLE_FIELDS_IBAN_SUGGESTION_GENERATOR_H_
-#define COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTIONS_SINGLE_FIELDS_IBAN_SUGGESTION_GENERATOR_H_
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTIONS_PAYMENTS_IBAN_SUGGESTION_GENERATOR_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTIONS_PAYMENTS_IBAN_SUGGESTION_GENERATOR_H_
 
 #include "components/autofill/core/browser/suggestions/suggestion_generator.h"
 
@@ -51,4 +51,4 @@
 
 }  // namespace autofill
 
-#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTIONS_SINGLE_FIELDS_IBAN_SUGGESTION_GENERATOR_H_
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_SUGGESTIONS_PAYMENTS_IBAN_SUGGESTION_GENERATOR_H_
diff --git a/components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator_unittest.cc b/components/autofill/core/browser/suggestions/payments/iban_suggestion_generator_unittest.cc
similarity index 98%
rename from components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator_unittest.cc
rename to components/autofill/core/browser/suggestions/payments/iban_suggestion_generator_unittest.cc
index 0a9749bd..200eb66e 100644
--- a/components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator_unittest.cc
+++ b/components/autofill/core/browser/suggestions/payments/iban_suggestion_generator_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "components/autofill/core/browser/suggestions/single_fields/iban_suggestion_generator.h"
+#include "components/autofill/core/browser/suggestions/payments/iban_suggestion_generator.h"
 
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_feature_list.h"
diff --git a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc
index 18971ad..7c3b745d 100644
--- a/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc
+++ b/components/autofill/core/browser/suggestions/payments/payments_suggestion_generator.cc
@@ -1636,6 +1636,7 @@
     case SuggestionType::kBnplEntry:
     case SuggestionType::kPendingStateSignin:
     case SuggestionType::kLoyaltyCardEntry:
+    case SuggestionType::kHomeAndWorkAddressEntry:
       return false;
   }
 }
diff --git a/components/autofill/core/browser/suggestions/suggestion_type.cc b/components/autofill/core/browser/suggestions/suggestion_type.cc
index bb104e3..8521710 100644
--- a/components/autofill/core/browser/suggestions/suggestion_type.cc
+++ b/components/autofill/core/browser/suggestions/suggestion_type.cc
@@ -110,6 +110,8 @@
       return "kPendingStateSignin";
     case SuggestionType::kLoyaltyCardEntry:
       return "kLoyaltyCardEntry";
+    case SuggestionType::kHomeAndWorkAddressEntry:
+      return "kHomeAndWorkAddressEntry";
   }
   NOTREACHED();
 }
diff --git a/components/autofill/core/browser/suggestions/suggestion_type.h b/components/autofill/core/browser/suggestions/suggestion_type.h
index 1c2c5ef..5c3fc17 100644
--- a/components/autofill/core/browser/suggestions/suggestion_type.h
+++ b/components/autofill/core/browser/suggestions/suggestion_type.h
@@ -120,6 +120,9 @@
   // Loyalty card suggestions.
   kLoyaltyCardEntry = 67,
 
+  // Home & Work suggestions.
+  kHomeAndWorkAddressEntry = 69,
+
   // Webauthn suggestions.
   kWebauthnCredential = 43,
   kWebauthnSignInWithAnotherDevice = 44,
@@ -155,8 +158,8 @@
   // state. On click the user will be directed to sign in.
   kPendingStateSignin = 65,
 
-  // Next ID: 69
-  kMaxValue = kManageLoyaltyCard
+  // Next ID: 70
+  kMaxValue = kHomeAndWorkAddressEntry
 };
 // LINT.ThenChange(/tools/metrics/histograms/metadata/autofill/enums.xml:SuggestionType)
 
diff --git a/components/autofill/core/browser/ui/autofill_external_delegate.cc b/components/autofill/core/browser/ui/autofill_external_delegate.cc
index 00de715..c6111434 100644
--- a/components/autofill/core/browser/ui/autofill_external_delegate.cc
+++ b/components/autofill/core/browser/ui/autofill_external_delegate.cc
@@ -231,6 +231,7 @@
     case SuggestionType::kAddressEntry:
     case SuggestionType::kAddressEntryOnTyping:
     case SuggestionType::kAddressFieldByFieldFilling:
+    case SuggestionType::kHomeAndWorkAddressEntry:
     case SuggestionType::kCreditCardEntry:
     case SuggestionType::kDevtoolsTestAddresses:
     case SuggestionType::kSaveAndFillCreditCardEntry:
@@ -549,6 +550,7 @@
 #endif
       break;
     case SuggestionType::kAddressEntry:
+    case SuggestionType::kHomeAndWorkAddressEntry:
     case SuggestionType::kCreditCardEntry:
     case SuggestionType::kDevtoolsTestAddressEntry:
       FillAutofillFormData(
@@ -697,6 +699,7 @@
     case SuggestionType::kAddressEntry:
     case SuggestionType::kAddressFieldByFieldFilling:
     case SuggestionType::kDevtoolsTestAddressEntry:
+    case SuggestionType::kHomeAndWorkAddressEntry:
       DidAcceptAddressSuggestion(suggestion, metadata);
       break;
     case SuggestionType::kCreditCardEntry:
@@ -932,6 +935,7 @@
     // These SuggestionTypes are various types which can appear in the first
     // level suggestion to fill an address or credit card field.
     case SuggestionType::kAddressEntry:
+    case SuggestionType::kHomeAndWorkAddressEntry:
     case SuggestionType::kAddressFieldByFieldFilling: {
       const std::string guid =
           std::get<Suggestion::AutofillProfilePayload>(suggestion.payload)
diff --git a/components/background_task_scheduler/OWNERS b/components/background_task_scheduler/OWNERS
index 1d85804..a1d7de6 100644
--- a/components/background_task_scheduler/OWNERS
+++ b/components/background_task_scheduler/OWNERS
@@ -1,6 +1,5 @@
 dtrainor@chromium.org
 hanxi@chromium.org
-mheikal@chromium.org
 nyquist@chromium.org
 peter@chromium.org
 shaktisahu@chromium.org
diff --git a/components/bookmarks/browser/bookmark_model.cc b/components/bookmarks/browser/bookmark_model.cc
index 1fb8a60..4a04d88 100644
--- a/components/bookmarks/browser/bookmark_model.cc
+++ b/components/bookmarks/browser/bookmark_model.cc
@@ -907,10 +907,15 @@
   return url_index_ && url_index_->HasBookmarks();
 }
 
-bool BookmarkModel::HasNoUserCreatedBookmarksOrFolders() const {
+bool BookmarkModel::HasUserCreatedBookmarksOrFolders() const {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  return bookmark_bar_node_->children().empty() &&
-         other_node_->children().empty() && mobile_node_->children().empty();
+  return !bookmark_bar_node_->children().empty() ||
+         !other_node_->children().empty() ||
+         !mobile_node_->children().empty() ||
+         (account_bookmark_bar_node_ &&
+          !account_bookmark_bar_node_->children().empty()) ||
+         (account_other_node_ && !account_other_node_->children().empty()) ||
+         (account_mobile_node_ && !account_mobile_node_->children().empty());
 }
 
 bool BookmarkModel::IsBookmarked(const GURL& url) const {
diff --git a/components/bookmarks/browser/bookmark_model.h b/components/bookmarks/browser/bookmark_model.h
index 7e8ff72..683feda 100644
--- a/components/bookmarks/browser/bookmark_model.h
+++ b/components/bookmarks/browser/bookmark_model.h
@@ -310,8 +310,11 @@
   // Returns true if there are bookmarks, otherwise returns false.
   bool HasBookmarks() const;
 
-  // Returns true is there is no user created bookmarks or folders.
-  bool HasNoUserCreatedBookmarksOrFolders() const;
+  // Returns true is there is at least one user-created bookmark or folder. This
+  // includes bookmarks downloaded via Sync but excludes managed nodes
+  // (enterprise) as well as, on Android, partner bookmarks (which are not
+  // included in BookmarkModel).
+  bool HasUserCreatedBookmarksOrFolders() const;
 
   // Returns true if the specified URL is bookmarked.
   bool IsBookmarked(const GURL& url) const;
diff --git a/components/commerce/core/product_specifications/product_specifications_sync_bridge_unittest.cc b/components/commerce/core/product_specifications/product_specifications_sync_bridge_unittest.cc
index 6c9e98fc..38c7ae7c 100644
--- a/components/commerce/core/product_specifications/product_specifications_sync_bridge_unittest.cc
+++ b/components/commerce/core/product_specifications/product_specifications_sync_bridge_unittest.cc
@@ -234,26 +234,8 @@
   }
 
   std::map<std::string, sync_pb::ProductComparisonSpecifics> GetAllStoreData() {
-    base::RunLoop loop;
-    std::map<std::string, sync_pb::ProductComparisonSpecifics>
-        storage_key_to_specifics;
-    bridge_->store_->ReadAllData(base::BindOnce(
-        [](base::RunLoop* loop,
-           std::map<std::string, sync_pb::ProductComparisonSpecifics>*
-               storage_key_to_specifics,
-           const std::optional<syncer::ModelError>& error,
-           std::unique_ptr<syncer::DataTypeStore::RecordList> data_records) {
-          for (auto& record : *data_records.get()) {
-            sync_pb::ProductComparisonSpecifics specifics;
-            specifics.ParseFromString(record.value);
-            storage_key_to_specifics->emplace(specifics.uuid(), specifics);
-          }
-          loop->Quit();
-        },
-        &loop, &storage_key_to_specifics));
-    loop.Run();
-
-    return storage_key_to_specifics;
+    return syncer::DataTypeStoreTestUtil::ReadAllDataAsProtoAndWait<
+        sync_pb::ProductComparisonSpecifics>(*store_);
   }
 
   void VerifySpecificsExists(
diff --git a/components/content_settings/android/cookie_controls_bridge.cc b/components/content_settings/android/cookie_controls_bridge.cc
index 4259041..a2dccff9 100644
--- a/components/content_settings/android/cookie_controls_bridge.cc
+++ b/components/content_settings/android/cookie_controls_bridge.cc
@@ -79,8 +79,8 @@
   Java_CookieControlsBridge_onStatusChanged(
       env, jobject_,
       static_cast<bool>(controls_state_ != CookieControlsState::kHidden),
-      static_cast<bool>(controls_state_ == CookieControlsState::kTpActive ||
-                        controls_state_ == CookieControlsState::k3pcsBlocked),
+      static_cast<bool>(controls_state_ == CookieControlsState::kActiveTp ||
+                        controls_state_ == CookieControlsState::kBlocked3pc),
       static_cast<int>(enforcement_), static_cast<int>(blocking_status),
       expiration.InMillisecondsSinceUnixEpoch());
 }
diff --git a/components/content_settings/browser/ui/cookie_controls_controller.cc b/components/content_settings/browser/ui/cookie_controls_controller.cc
index 7f33a79..5dcdea4 100644
--- a/components/content_settings/browser/ui/cookie_controls_controller.cc
+++ b/components/content_settings/browser/ui/cookie_controls_controller.cc
@@ -221,11 +221,11 @@
     controls_state =
         tracking_protection_settings_->HasTrackingProtectionException(url,
                                                                       &info)
-            ? CookieControlsState::kTpPaused
-            : CookieControlsState::kTpActive;
+            ? CookieControlsState::kPausedTp
+            : CookieControlsState::kActiveTp;
   } else {
-    controls_state = cookies_allowed ? CookieControlsState::k3pcsAllowed
-                                     : CookieControlsState::k3pcsBlocked;
+    controls_state = cookies_allowed ? CookieControlsState::kAllowed3pc
+                                     : CookieControlsState::kBlocked3pc;
   }
 
   return {controls_state, enforcement, blocking_status,
@@ -461,7 +461,7 @@
   // exception, update the last visited time, otherwise clear it.
   base::Value::Dict metadata = GetMetadata(settings_map_, url);
   auto status = GetStatus(GetWebContents());
-  if (status.controls_state == CookieControlsState::k3pcsAllowed) {
+  if (status.controls_state == CookieControlsState::kAllowed3pc) {
     metadata.Set(kLastVisitedActiveException,
                  base::TimeToValue(base::Time::Now()));
   } else {
@@ -579,7 +579,7 @@
   // Highlighting is meant to draw attention to bypassing, so just return if
   // bypass has already happened.
   if (controls_state == CookieControlsState::kHidden ||
-      controls_state == CookieControlsState::k3pcsAllowed) {
+      controls_state == CookieControlsState::kAllowed3pc) {
     return false;
   }
 
@@ -634,8 +634,8 @@
   // allow the user to opt into sending SameSite=None cookies again in those
   // contexts.
   return HasOriginSandboxedTopLevelDocument() ||
-         controls_state == CookieControlsState::k3pcsAllowed ||
-         controls_state == CookieControlsState::kTpPaused ||
+         controls_state == CookieControlsState::kAllowed3pc ||
+         controls_state == CookieControlsState::kPausedTp ||
          // If no 3P sites have attempted to access site data, nor were any
          // stateful bounces recorded, the icon should not be displayed. Take
          // into account both allow and blocked counts, since the breakage might
diff --git a/components/content_settings/core/browser/host_content_settings_map.cc b/components/content_settings/core/browser/host_content_settings_map.cc
index 8daac17..ec5b1d6 100644
--- a/components/content_settings/core/browser/host_content_settings_map.cc
+++ b/components/content_settings/core/browser/host_content_settings_map.cc
@@ -11,6 +11,7 @@
 #include <utility>
 #include <vector>
 
+#include "base/barrier_closure.h"
 #include "base/check.h"
 #include "base/command_line.h"
 #include "base/containers/contains.h"
@@ -1300,3 +1301,12 @@
                        base::Unretained(this), content_setting_type));
   }
 }
+
+void HostContentSettingsMap::EnsureSettingsUpToDate(
+    base::OnceClosure callback) {
+  base::RepeatingClosure barrier_closure = base::BarrierClosure(
+      user_modifiable_providers_.size(), std::move(callback));
+  for (auto&& provider : user_modifiable_providers_) {
+    provider->EnsureUpdatedSettings(barrier_closure);
+  }
+}
diff --git a/components/content_settings/core/browser/host_content_settings_map.h b/components/content_settings/core/browser/host_content_settings_map.h
index 1c38627..6e26cccf 100644
--- a/components/content_settings/core/browser/host_content_settings_map.h
+++ b/components/content_settings/core/browser/host_content_settings_map.h
@@ -14,6 +14,7 @@
 #include <vector>
 
 #include "base/check_is_test.h"
+#include "base/functional/callback_forward.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/ref_counted.h"
@@ -27,7 +28,6 @@
 #include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/content_settings/core/browser/content_settings_utils.h"
 #include "components/content_settings/core/browser/user_modifiable_provider.h"
-
 #include "components/content_settings/core/common/content_settings_constraints.h"
 #include "components/content_settings/core/common/content_settings_metadata.h"
 #include "components/content_settings/core/common/content_settings_pattern.h"
@@ -346,6 +346,12 @@
   void AddObserver(content_settings::Observer* observer);
   void RemoveObserver(content_settings::Observer* observer);
 
+  // Forces a sync of content settings and invokes callback when the sync is
+  // done. Useful for ensuring that NOTIFICATIONS content settings are
+  // up-to-date on Android, where they reflect the state of the corresponding OS
+  // channels and need to be initialized from the OS.
+  void EnsureSettingsUpToDate(base::OnceClosure callback);
+
   // Schedules any pending lossy website settings to be written to disk.
   void FlushLossyWebsiteSettings();
 
diff --git a/components/content_settings/core/browser/user_modifiable_provider.cc b/components/content_settings/core/browser/user_modifiable_provider.cc
index b2ffe183..a4327518 100644
--- a/components/content_settings/core/browser/user_modifiable_provider.cc
+++ b/components/content_settings/core/browser/user_modifiable_provider.cc
@@ -14,4 +14,9 @@
   SetWebsiteSetting(primary_pattern, secondary_pattern, content_settings_type,
                     base::Value(), {}, partition_key);
 }
+
+void UserModifiableProvider::EnsureUpdatedSettings(base::OnceClosure callback) {
+  std::move(callback).Run();
+}
+
 }  // namespace content_settings
diff --git a/components/content_settings/core/browser/user_modifiable_provider.h b/components/content_settings/core/browser/user_modifiable_provider.h
index ac884c91..3023006 100644
--- a/components/content_settings/core/browser/user_modifiable_provider.h
+++ b/components/content_settings/core/browser/user_modifiable_provider.h
@@ -66,6 +66,10 @@
       const ContentSettingsPattern& secondary_pattern,
       ContentSettingsType content_settings_type,
       const PartitionKey& partition_key);
+
+  // Triggers an update of settings from the backends and calls `callback` upon
+  // completion. Currently only used for notification permissions on Android.
+  virtual void EnsureUpdatedSettings(base::OnceClosure callback);
 };
 
 }  // namespace content_settings
diff --git a/components/content_settings/core/common/cookie_controls_state.h b/components/content_settings/core/common/cookie_controls_state.h
index b34bdee..0ab19d1 100644
--- a/components/content_settings/core/common/cookie_controls_state.h
+++ b/components/content_settings/core/common/cookie_controls_state.h
@@ -8,17 +8,17 @@
 // Enum representing the state of in-context cookie controls.
 // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.content_settings
 enum class CookieControlsState {
-  // Controls not visible
+  // Controls not visible.
   kHidden = 0,
-  // Third-party cookies UI with 3PC toggle off
-  k3pcsBlocked = 1,
-  // Third-party cookies UI with 3PC toggle on
-  k3pcsAllowed = 2,
-  // Tracking protections UI with tracking protections active button
-  kTpActive = 3,
-  // Tracking protections UI with tracking protections paused button
-  kTpPaused = 4,
-  kMaxValue = kTpPaused,
+  // Third-party cookies UI with toggle off.
+  kBlocked3pc = 1,
+  // Third-party cookies UI with toggle on.
+  kAllowed3pc = 2,
+  // Tracking protections UI with button to pause protections.
+  kActiveTp = 3,
+  // Tracking protections UI with button to resume protections.
+  kPausedTp = 4,
+  kMaxValue = kPausedTp,
 };
 
 #endif  // COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_COOKIE_CONTROLS_STATE_H_
diff --git a/components/externalauth/android/OWNERS b/components/externalauth/android/OWNERS
index 16cbd4b..f5d6db69 100644
--- a/components/externalauth/android/OWNERS
+++ b/components/externalauth/android/OWNERS
@@ -1,2 +1 @@
 peconn@chromium.org
-eirage@chromium.org
diff --git a/components/metrics/cloned_install_detector.cc b/components/metrics/cloned_install_detector.cc
index a29566c4..f9a7a61 100644
--- a/components/metrics/cloned_install_detector.cc
+++ b/components/metrics/cloned_install_detector.cc
@@ -57,16 +57,19 @@
   if (!MachineIdProvider::HasId())
     return;
 
+  base::Time check_initiated_timestamp = base::Time::Now();
   base::ThreadPool::PostTaskAndReplyWithResult(
       FROM_HERE,
       {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
        base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
       base::BindOnce(&MachineIdProvider::GetMachineId),
       base::BindOnce(&ClonedInstallDetector::SaveMachineId,
-                     weak_ptr_factory_.GetWeakPtr(), local_state));
+                     weak_ptr_factory_.GetWeakPtr(), local_state,
+                     check_initiated_timestamp));
 }
 
 void ClonedInstallDetector::SaveMachineId(PrefService* local_state,
+                                          base::Time check_initiated_timestamp,
                                           const std::string& raw_id) {
   if (raw_id.empty()) {
     LogMachineIdState(ID_GENERATION_FAILED);
@@ -82,7 +85,11 @@
       DCHECK(!detected_this_session_);
       id_state = ID_CHANGED;
       detected_this_session_ = true;
+
       local_state->SetBoolean(prefs::kMetricsResetIds, true);
+      local_state->SetInt64(prefs::kSessionStartTimestampForLastClonedDetection,
+                            check_initiated_timestamp.ToTimeT());
+
       callback_list_.Notify();
     } else {
       id_state = ID_UNCHANGED;
@@ -126,7 +133,7 @@
 
 void ClonedInstallDetector::SaveMachineIdForTesting(PrefService* local_state,
                                                     const std::string& raw_id) {
-  SaveMachineId(local_state, raw_id);
+  SaveMachineId(local_state, base::Time::Now(), raw_id);
 }
 
 // static
@@ -136,11 +143,15 @@
   registry->RegisterIntegerPref(prefs::kClonedResetCount, 0);
   registry->RegisterInt64Pref(prefs::kFirstClonedResetTimestamp, 0);
   registry->RegisterInt64Pref(prefs::kLastClonedResetTimestamp, 0);
+  registry->RegisterInt64Pref(
+      prefs::kSessionStartTimestampForLastClonedDetection, 0);
 }
 
 ClonedInstallInfo ClonedInstallDetector::ReadClonedInstallInfo(
     PrefService* local_state) {
   return ClonedInstallInfo{
+      .last_detection_timestamp = local_state->GetInt64(
+          prefs::kSessionStartTimestampForLastClonedDetection),
       .last_reset_timestamp =
           local_state->GetInt64(prefs::kLastClonedResetTimestamp),
       .first_reset_timestamp =
@@ -152,8 +163,10 @@
   local_state->ClearPref(prefs::kClonedResetCount);
   local_state->ClearPref(prefs::kFirstClonedResetTimestamp);
   local_state->ClearPref(prefs::kLastClonedResetTimestamp);
+  local_state->ClearPref(prefs::kSessionStartTimestampForLastClonedDetection);
 }
 
+// static
 void ClonedInstallDetector::RecordClonedInstallInfo(PrefService* local_state) {
   ClonedInstallInfo cloned = ReadClonedInstallInfo(local_state);
 
@@ -168,6 +181,20 @@
   }
   local_state->SetInt64(prefs::kLastClonedResetTimestamp, time);
   local_state->SetInteger(prefs::kClonedResetCount, cloned.reset_count + 1);
+
+  if (!cloned.last_detection_timestamp) {
+    // `prefs::kSessionStartTimestampForLastClonedDetection` (which is read to
+    // `last_detection_timestamp`) is expected to be set when we flag the
+    // client as a clone to be reset. This timestamp might be missing,
+    // especially on older milestones that did not set the timestamp yet, or if
+    // the user toggled UMA opt-in during the session that detected the clone.
+    // In this case, we can use the reset time as a fallback value. We might
+    // then mistakenly attribute some events that happened on the current
+    // (cloned) install as having taken place on the original install. That's
+    // acceptable.
+    local_state->SetInt64(prefs::kSessionStartTimestampForLastClonedDetection,
+                          time);
+  }
 }
 
 }  // namespace metrics
diff --git a/components/metrics/cloned_install_detector.h b/components/metrics/cloned_install_detector.h
index 4b80943..8964d4c 100644
--- a/components/metrics/cloned_install_detector.h
+++ b/components/metrics/cloned_install_detector.h
@@ -18,6 +18,10 @@
 // A struct that holds cloned install related fields in prefs that need to be
 // reported in the system_profile.
 struct ClonedInstallInfo {
+  // The start of the latest session during which the install was detected as
+  // cloned.
+  int64_t last_detection_timestamp;
+
   int64_t last_reset_timestamp;
   int64_t first_reset_timestamp;
   int reset_count;
@@ -84,10 +88,14 @@
   FRIEND_TEST_ALL_PREFIXES(MetricsServiceTest,
                            PurgeLogsOnClonedInstallDetected);
 
-  // Converts raw_id into a 24-bit hash and stores the hash in |local_state|.
-  // |raw_id| is not a const ref because it's passed from a cross-thread post
-  // task.
-  void SaveMachineId(PrefService* local_state, const std::string& raw_id);
+  // Converts `raw_id` into a 24-bit hash and stores the hash in `local_state`.
+  // This function is called via a cross-thread, low priority posted task, and
+  // `check_initiated_timestamp` corresponds to the time when the request to
+  // check (get and then save) the machine ID was initiated. It will be written
+  // to `local_state` if a machine ID mismatch is detected.
+  void SaveMachineId(PrefService* local_state,
+                     base::Time check_initiated_timestamp,
+                     const std::string& raw_id);
 
   // Indicates that we detected a cloned install during the current session.
   bool detected_this_session_ = false;
diff --git a/components/metrics/cloned_install_detector_unittest.cc b/components/metrics/cloned_install_detector_unittest.cc
index 3914ce8..5b83e8d 100644
--- a/components/metrics/cloned_install_detector_unittest.cc
+++ b/components/metrics/cloned_install_detector_unittest.cc
@@ -29,9 +29,11 @@
   ClonedInstallDetector::RegisterPrefs(prefs.registry());
 
   ClonedInstallDetector detector;
-  detector.SaveMachineId(&prefs, kTestRawId);
+  detector.SaveMachineId(&prefs, base::Time::Now(), kTestRawId);
 
   EXPECT_EQ(kTestHashedId, prefs.GetInteger(prefs::kMetricsMachineId));
+  EXPECT_EQ(
+      0, prefs.GetInt64(prefs::kSessionStartTimestampForLastClonedDetection));
 }
 
 TEST(ClonedInstallDetectorTest, DetectClone) {
@@ -41,11 +43,15 @@
   // Save a machine id that will cause a clone to be detected.
   prefs.SetInteger(prefs::kMetricsMachineId, kTestHashedId + 1);
 
+  base::Time check_requested_time = base::Time::Now();
   ClonedInstallDetector detector;
-  detector.SaveMachineId(&prefs, kTestRawId);
+  detector.SaveMachineId(&prefs, check_requested_time, kTestRawId);
 
   EXPECT_TRUE(prefs.GetBoolean(prefs::kMetricsResetIds));
   EXPECT_TRUE(detector.ShouldResetClientIds(&prefs));
+  EXPECT_EQ(
+      check_requested_time.ToTimeT(),
+      prefs.GetInt64(prefs::kSessionStartTimestampForLastClonedDetection));
 }
 
 TEST(ClonedInstallDetectorTest, ShouldResetClientIds) {
@@ -57,7 +63,7 @@
 
   // Save a machine id that will cause a clone to be detected.
   prefs.SetInteger(prefs::kMetricsMachineId, kTestHashedId + 1);
-  detector.SaveMachineId(&prefs, kTestRawId);
+  detector.SaveMachineId(&prefs, base::Time::Now(), kTestRawId);
 
   // Multiple different services may call into the cloned install detector, it
   // needs to continue supporting giving the same answer more than once
@@ -75,7 +81,7 @@
 
   // Save a machine id that will cause a clone to be detected.
   prefs.SetInteger(prefs::kMetricsMachineId, kTestHashedId + 1);
-  detector.SaveMachineId(&prefs, kTestRawId);
+  detector.SaveMachineId(&prefs, base::Time::Now(), kTestRawId);
 
   // Ensure that the current session call returns true both before things are
   // modified by ShouldResetClientIds and after
@@ -99,13 +105,13 @@
 
   // Save a machine id that will not cause a clone to be detected.
   prefs.SetInteger(prefs::kMetricsMachineId, kTestHashedId);
-  detector.SaveMachineId(&prefs, kTestRawId);
+  detector.SaveMachineId(&prefs, base::Time::Now(), kTestRawId);
   EXPECT_FALSE(detector.ClonedInstallDetectedInCurrentSession());
   EXPECT_FALSE(callback_called);
 
   // Save a machine id that will cause a clone to be detected.
   prefs.SetInteger(prefs::kMetricsMachineId, kTestHashedId + 1);
-  detector.SaveMachineId(&prefs, kTestRawId);
+  detector.SaveMachineId(&prefs, base::Time::Now(), kTestRawId);
   EXPECT_TRUE(detector.ClonedInstallDetectedInCurrentSession());
   EXPECT_TRUE(callback_called);
 
diff --git a/components/metrics/metrics_pref_names.cc b/components/metrics/metrics_pref_names.cc
index e3fea318..62b0fe7 100644
--- a/components/metrics/metrics_pref_names.cc
+++ b/components/metrics/metrics_pref_names.cc
@@ -143,6 +143,16 @@
 // updated every time we reset the client due to cloned install.
 const char kLastClonedResetTimestamp[] = "cloned_install.last_timestamp";
 
+// The start of the latest session during which the install was detected as
+// cloned. Not to be mixed up with the metrics ID reset moment recorded in
+// `kLastClonedResetTimestamp`, which requires a restart after the clone
+// detection. This one is intended as a reference point to check whether some
+// other event happened on the original machine or on the clone. This will be
+// updated every time a cloned install is detected, and cleared alongside the
+// other `cloned_install` prefs on UMA opt-out.
+const char kSessionStartTimestampForLastClonedDetection[] =
+    "cloned_install.session_start_last_detection_timestamp";
+
 // A time stamp at which time the browser was known to be alive. Used to
 // evaluate whether the browser crash was due to a whole system crash.
 // At minimum this is updated each time the "exited_cleanly" preference is
diff --git a/components/metrics/metrics_pref_names.h b/components/metrics/metrics_pref_names.h
index a0f3bf8..2afd1fb 100644
--- a/components/metrics/metrics_pref_names.h
+++ b/components/metrics/metrics_pref_names.h
@@ -37,6 +37,7 @@
 extern const char kClonedResetCount[];
 extern const char kFirstClonedResetTimestamp[];
 extern const char kLastClonedResetTimestamp[];
+extern const char kSessionStartTimestampForLastClonedDetection[];
 
 // For finding out whether metrics and crash reporting is enabled use the
 // relevant embedder-specific subclass of MetricsServiceAccessor instead of
diff --git a/components/metrics/metrics_service_unittest.cc b/components/metrics/metrics_service_unittest.cc
index 948371f..2abab58 100644
--- a/components/metrics/metrics_service_unittest.cc
+++ b/components/metrics/metrics_service_unittest.cc
@@ -1740,14 +1740,16 @@
 
   // Save a machine id that will not cause a clone to be detected.
   GetLocalState()->SetInteger(prefs::kMetricsMachineId, kTestHashedId);
-  cloned_install_detector->SaveMachineId(GetLocalState(), kTestRawId);
+  cloned_install_detector->SaveMachineId(GetLocalState(), base::Time::Now(),
+                                         kTestRawId);
   // Verify that the logs are still present.
   EXPECT_TRUE(test_log_store->has_staged_log());
   EXPECT_TRUE(test_log_store->has_unsent_logs());
 
   // Save a machine id that will cause a clone to be detected.
   GetLocalState()->SetInteger(prefs::kMetricsMachineId, kTestHashedId + 1);
-  cloned_install_detector->SaveMachineId(GetLocalState(), kTestRawId);
+  cloned_install_detector->SaveMachineId(GetLocalState(), base::Time::Now(),
+                                         kTestRawId);
   // Verify that the logs were purged.
   EXPECT_FALSE(test_log_store->has_staged_log());
   EXPECT_FALSE(test_log_store->has_unsent_logs());
diff --git a/components/metrics/metrics_state_manager_unittest.cc b/components/metrics/metrics_state_manager_unittest.cc
index 6cb80f8..a0b4372a 100644
--- a/components/metrics/metrics_state_manager_unittest.cc
+++ b/components/metrics/metrics_state_manager_unittest.cc
@@ -702,7 +702,8 @@
   // Set the pref through SaveMachineId and expect previous to do nothing and
   // current to log the histogram
   prefs_.SetInteger(prefs::kMetricsMachineId, 2216820);
-  state_manager->cloned_install_detector_.SaveMachineId(&prefs_, "test");
+  state_manager->cloned_install_detector_.SaveMachineId(
+      &prefs_, base::Time::Now(), "test");
   provider->ProvideCurrentSessionData(&uma_proto);
   histogram_tester.ExpectUniqueSample("UMA.IsClonedInstall", 1, 2);
 }
diff --git a/components/network_time/network_time_tracker.cc b/components/network_time/network_time_tracker.cc
index 2d767ab..0cb23bb 100644
--- a/components/network_time/network_time_tracker.cc
+++ b/components/network_time/network_time_tracker.cc
@@ -473,7 +473,7 @@
 }
 
 bool NetworkTimeTracker::UpdateTimeFromResponse(
-    std::unique_ptr<std::string> response_body) {
+    std::optional<std::string> response_body) {
   int response_code = 0;
   if (time_fetcher_->ResponseInfo() && time_fetcher_->ResponseInfo()->headers) {
     response_code = time_fetcher_->ResponseInfo()->headers->response_code();
@@ -524,7 +524,7 @@
 }
 
 void NetworkTimeTracker::OnURLLoaderComplete(
-    std::unique_ptr<std::string> response_body) {
+    std::optional<std::string> response_body) {
   DCHECK(thread_checker_.CalledOnValidThread());
   DCHECK(time_fetcher_);
 
diff --git a/components/network_time/network_time_tracker.h b/components/network_time/network_time_tracker.h
index 70533779..7574894 100644
--- a/components/network_time/network_time_tracker.h
+++ b/components/network_time/network_time_tracker.h
@@ -195,10 +195,10 @@
 
   // Updates network time from a time server response, returning true
   // if successful.
-  bool UpdateTimeFromResponse(std::unique_ptr<std::string> response_body);
+  bool UpdateTimeFromResponse(std::optional<std::string> response_body);
 
   // Called to process responses from the secure time service.
-  void OnURLLoaderComplete(std::unique_ptr<std::string> response_body);
+  void OnURLLoaderComplete(std::optional<std::string> response_body);
 
   // Sets the next time query to be run at the specified time.
   void QueueCheckTime(base::TimeDelta delay);
diff --git a/components/page_image_annotation/OWNERS b/components/page_image_annotation/OWNERS
index 0c0b852..fa3c42b 100644
--- a/components/page_image_annotation/OWNERS
+++ b/components/page_image_annotation/OWNERS
@@ -1,2 +1 @@
 amoylan@chromium.org
-robsc@google.com
diff --git a/components/page_info/page_info.h b/components/page_info/page_info.h
index 2dfcff8..7e4a99f 100644
--- a/components/page_info/page_info.h
+++ b/components/page_info/page_info.h
@@ -506,7 +506,7 @@
   CookieBlocking3pcdStatus blocking_status_ =
       CookieBlocking3pcdStatus::kNotIn3pcd;
 
-  CookieControlsState controls_state_ = CookieControlsState::k3pcsBlocked;
+  CookieControlsState controls_state_ = CookieControlsState::kBlocked3pc;
 
   base::Time cookie_exception_expiration_;
 
diff --git a/components/password_manager/core/browser/features/password_features.cc b/components/password_manager/core/browser/features/password_features.cc
index bdee31a2..fe21fb0 100644
--- a/components/password_manager/core/browser/features/password_features.cc
+++ b/components/password_manager/core/browser/features/password_features.cc
@@ -72,6 +72,12 @@
              "fill-on-account-select",
              base::FEATURE_DISABLED_BY_DEFAULT);
 
+#if BUILDFLAG(IS_ANDROID)
+BASE_FEATURE(kFillRecoveryPassword,
+             "FillRecoveryPassword",
+             base::FEATURE_DISABLED_BY_DEFAULT);
+#endif
+
 #if BUILDFLAG(IS_IOS)
 BASE_FEATURE(kIosCleanupHangingPasswordFormExtractionRequests,
              "IosCleanupHangingPasswordFormExtractionRequests",
diff --git a/components/password_manager/core/browser/features/password_features.h b/components/password_manager/core/browser/features/password_features.h
index 1d9c21f2..ff9cae43 100644
--- a/components/password_manager/core/browser/features/password_features.h
+++ b/components/password_manager/core/browser/features/password_features.h
@@ -66,6 +66,11 @@
 // selection, rather than autofilling on page load, with highlighting of fields.
 BASE_DECLARE_FEATURE(kFillOnAccountSelect);
 
+#if BUILDFLAG(IS_ANDROID)
+// Allows filling from a secondary recovery password saved as a backup.
+BASE_DECLARE_FEATURE(kFillRecoveryPassword);
+#endif
+
 #if BUILDFLAG(IS_IOS)
 
 // Enables the clean up of hanging form extraction requests made by the
diff --git a/components/reading_list/core/reading_list_sync_bridge_unittest.cc b/components/reading_list/core/reading_list_sync_bridge_unittest.cc
index f533d63e..fb0dccb 100644
--- a/components/reading_list/core/reading_list_sync_bridge_unittest.cc
+++ b/components/reading_list/core/reading_list_sync_bridge_unittest.cc
@@ -106,21 +106,6 @@
   return clock->Now();
 }
 
-syncer::DataTypeStore::RecordList ReadAllDataFromDataTypeStore(
-    syncer::DataTypeStore* store) {
-  syncer::DataTypeStore::RecordList result;
-  base::RunLoop loop;
-  store->ReadAllData(base::BindLambdaForTesting(
-      [&](const std::optional<syncer::ModelError>& error,
-          std::unique_ptr<syncer::DataTypeStore::RecordList> records) {
-        EXPECT_FALSE(error.has_value()) << error->ToString();
-        result = std::move(*records);
-        loop.Quit();
-      }));
-  loop.Run();
-  return result;
-}
-
 }  // namespace
 
 class ReadingListSyncBridgeTest : public testing::Test {
@@ -409,12 +394,14 @@
           }));
   loop.Run();
 
-  ASSERT_THAT(ReadAllDataFromDataTypeStore(underlying_in_memory_store_),
+  ASSERT_THAT(syncer::DataTypeStoreTestUtil::ReadAllDataAndWait(
+                  *underlying_in_memory_store_),
               SizeIs(1));
 
   bridge()->ApplyDisableSyncChanges(bridge()->CreateMetadataChangeList());
 
-  EXPECT_THAT(ReadAllDataFromDataTypeStore(underlying_in_memory_store_),
+  EXPECT_THAT(syncer::DataTypeStoreTestUtil::ReadAllDataAndWait(
+                  *underlying_in_memory_store_),
               SizeIs(0));
 }
 
diff --git a/components/saved_tab_groups/internal/saved_tab_group_sync_bridge_unittest.cc b/components/saved_tab_groups/internal/saved_tab_group_sync_bridge_unittest.cc
index a7eded96..46ae0e6 100644
--- a/components/saved_tab_groups/internal/saved_tab_group_sync_bridge_unittest.cc
+++ b/components/saved_tab_groups/internal/saved_tab_group_sync_bridge_unittest.cc
@@ -266,16 +266,10 @@
   }
 
   void VerifyEntriesCount(size_t expected_count) {
-    std::unique_ptr<syncer::DataTypeStore::RecordList> entries;
-    store_->ReadAllData(base::BindLambdaForTesting(
-        [&](const std::optional<syncer::ModelError>& error,
-            std::unique_ptr<syncer::DataTypeStore::RecordList> data) {
-          entries = std::move(data);
-        }));
-    task_environment_.RunUntilIdle();
+    const syncer::DataTypeStore::RecordList records =
+        syncer::DataTypeStoreTestUtil::ReadAllDataAndWait(*store_);
 
-    ASSERT_TRUE(entries);
-    EXPECT_EQ(expected_count, entries->size());
+    EXPECT_EQ(expected_count, records.size());
   }
 
   std::optional<proto::SavedTabGroupData> ReadSavedTabGroupDataFromStore(
@@ -1290,20 +1284,13 @@
   task_environment_.RunUntilIdle();
 
   // Read the migrated data from the store.
-  std::unique_ptr<syncer::DataTypeStore::RecordList> entries;
-  store_->ReadAllData(base::BindLambdaForTesting(
-      [&](const std::optional<syncer::ModelError>& error,
-          std::unique_ptr<syncer::DataTypeStore::RecordList> data) {
-        entries = std::move(data);
-      }));
-  task_environment_.RunUntilIdle();
+  const std::map<std::string, proto::SavedTabGroupData> data =
+      syncer::DataTypeStoreTestUtil::ReadAllDataAsProtoAndWait<
+          proto::SavedTabGroupData>(*store_);
 
   // Verify the migrated data
-  ASSERT_TRUE(entries);
-  EXPECT_EQ(entries->size(), 1u);
-  const syncer::DataTypeStore::Record& record = entries->at(0);
-  proto::SavedTabGroupData migrated_data;
-  ASSERT_TRUE(migrated_data.ParseFromString(record.value));
+  ASSERT_EQ(data.size(), 1u);
+  const proto::SavedTabGroupData& migrated_data = data.begin()->second;
 
   EXPECT_TRUE(AreGroupSpecificsEqual(migrated_data.specifics(), old_specifics));
 
@@ -1339,17 +1326,11 @@
   task_environment_.RunUntilIdle();
 
   // Read the migrated data from the store.
-  std::unique_ptr<syncer::DataTypeStore::RecordList> entries;
-  store_->ReadAllData(base::BindLambdaForTesting(
-      [&](const std::optional<syncer::ModelError>& error,
-          std::unique_ptr<syncer::DataTypeStore::RecordList> data) {
-        entries = std::move(data);
-      }));
-  task_environment_.RunUntilIdle();
+  const syncer::DataTypeStore::RecordList entries =
+      syncer::DataTypeStoreTestUtil::ReadAllDataAndWait(*store_);
 
   // Verify the migrated data
-  ASSERT_TRUE(entries);
-  EXPECT_EQ(entries->size(), 2u);
+  ASSERT_EQ(entries.size(), 2u);
   EXPECT_EQ(1u, saved_tab_group_model_.saved_tab_groups().size());
 
   // Verify the migrated data in the model.
@@ -1416,18 +1397,12 @@
   task_environment_.RunUntilIdle();
 
   // Read the migrated data from the store.
-  std::unique_ptr<syncer::DataTypeStore::RecordList> entries;
-  store_->ReadAllData(base::BindLambdaForTesting(
-      [&](const std::optional<syncer::ModelError>& error,
-          std::unique_ptr<syncer::DataTypeStore::RecordList> data) {
-        entries = std::move(data);
-      }));
-  task_environment_.RunUntilIdle();
+  const syncer::DataTypeStore::RecordList entries =
+      syncer::DataTypeStoreTestUtil::ReadAllDataAndWait(*store_);
 
   // Verify the migrated data. It should match the original.
-  ASSERT_TRUE(entries);
-  EXPECT_EQ(entries->size(), 1u);
-  const syncer::DataTypeStore::Record& record = entries->at(0);
+  ASSERT_EQ(entries.size(), 1u);
+  const syncer::DataTypeStore::Record& record = entries.at(0);
   proto::SavedTabGroupData migrated_data;
   EXPECT_EQ(group_data.SerializeAsString(), record.value);
   ASSERT_TRUE(migrated_data.ParseFromString(record.value));
@@ -1457,18 +1432,12 @@
   task_environment_.RunUntilIdle();
 
   // Read the migrated data from the store.
-  std::unique_ptr<syncer::DataTypeStore::RecordList> entries;
-  store_->ReadAllData(base::BindLambdaForTesting(
-      [&](const std::optional<syncer::ModelError>& error,
-          std::unique_ptr<syncer::DataTypeStore::RecordList> data) {
-        entries = std::move(data);
-      }));
-  task_environment_.RunUntilIdle();
+  const syncer::DataTypeStore::RecordList entries =
+      syncer::DataTypeStoreTestUtil::ReadAllDataAndWait(*store_);
 
   // Verify the migrated data. It should match the original.
-  ASSERT_TRUE(entries);
-  EXPECT_EQ(entries->size(), 1u);
-  const syncer::DataTypeStore::Record& record = entries->at(0);
+  ASSERT_EQ(entries.size(), 1u);
+  const syncer::DataTypeStore::Record& record = entries.at(0);
   proto::SavedTabGroupData migrated_data;
   EXPECT_EQ(group_data.SerializeAsString(), record.value);
   ASSERT_TRUE(migrated_data.ParseFromString(record.value));
@@ -1506,17 +1475,11 @@
   task_environment_.RunUntilIdle();
 
   // Read the migrated data from the store.
-  std::unique_ptr<syncer::DataTypeStore::RecordList> entries;
-  store_->ReadAllData(base::BindLambdaForTesting(
-      [&](const std::optional<syncer::ModelError>& error,
-          std::unique_ptr<syncer::DataTypeStore::RecordList> data) {
-        entries = std::move(data);
-      }));
-  task_environment_.RunUntilIdle();
+  const syncer::DataTypeStore::RecordList entries =
+      syncer::DataTypeStoreTestUtil::ReadAllDataAndWait(*store_);
 
   // Verify the migrated data
-  ASSERT_TRUE(entries);
-  EXPECT_EQ(entries->size(), 2u);
+  ASSERT_EQ(entries.size(), 2u);
   EXPECT_EQ(1u, saved_tab_group_model_.saved_tab_groups().size());
 
   // Verify the migrated data in the model.
diff --git a/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge_unittest.cc b/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge_unittest.cc
index a3705ea..c3f7c05 100644
--- a/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge_unittest.cc
+++ b/components/saved_tab_groups/internal/shared_tab_group_account_data_sync_bridge_unittest.cc
@@ -186,24 +186,12 @@
   }
 
   size_t GetNumEntriesInStore(bool is_tab_details) {
-    std::unique_ptr<syncer::DataTypeStore::RecordList> entries;
-    base::RunLoop run_loop;
-    store_->ReadAllData(base::BindLambdaForTesting(
-        [&run_loop, &entries](
-            const std::optional<syncer::ModelError>& error,
-            std::unique_ptr<syncer::DataTypeStore::RecordList> data) {
-          entries = std::move(data);
-          run_loop.Quit();
-        }));
-    run_loop.Run();
+    std::map<std::string, sync_pb::SharedTabGroupAccountDataSpecifics> data =
+        syncer::DataTypeStoreTestUtil::ReadAllDataAsProtoAndWait<
+            sync_pb::SharedTabGroupAccountDataSpecifics>(*store_);
 
     size_t size = 0;
-    for (const auto& record : *entries) {
-      sync_pb::SharedTabGroupAccountDataSpecifics specifics;
-      if (!specifics.ParseFromString(record.value)) {
-        CHECK(false);
-      }
-
+    for (const auto& [storage_key, specifics] : data) {
       if (is_tab_details && specifics.has_shared_tab_details()) {
         ++size;
       }
diff --git a/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge_unittest.cc b/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge_unittest.cc
index 503c25d..36e1d4b0 100644
--- a/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge_unittest.cc
+++ b/components/saved_tab_groups/internal/shared_tab_group_data_sync_bridge_unittest.cc
@@ -75,7 +75,9 @@
 using testing::IsNull;
 using testing::Not;
 using testing::NotNull;
+using testing::Pair;
 using testing::Pointee;
+using testing::Property;
 using testing::Return;
 using testing::ReturnRef;
 using testing::Sequence;
@@ -454,41 +456,13 @@
     saved_tab_group_model_.reset();
   }
 
-  size_t GetNumEntriesInStore() const {
-    std::unique_ptr<syncer::DataTypeStore::RecordList> entries;
-    base::RunLoop run_loop;
-    store_->ReadAllData(base::BindLambdaForTesting(
-        [&run_loop, &entries](
-            const std::optional<syncer::ModelError>& error,
-            std::unique_ptr<syncer::DataTypeStore::RecordList> data) {
-          entries = std::move(data);
-          run_loop.Quit();
-        }));
-    run_loop.Run();
-    return entries->size();
+  size_t GetNumEntriesInStore() {
+    return syncer::DataTypeStoreTestUtil::ReadAllDataAndWait(store()).size();
   }
 
-  std::vector<sync_pb::SharedTabGroupDataSpecifics> GetAllSpecificsFromStore()
-      const {
-    std::unique_ptr<syncer::DataTypeStore::RecordList> entries;
-    base::RunLoop run_loop;
-    store_->ReadAllData(base::BindLambdaForTesting(
-        [&run_loop, &entries](
-            const std::optional<syncer::ModelError>& error,
-            std::unique_ptr<syncer::DataTypeStore::RecordList> data) {
-          entries = std::move(data);
-          run_loop.Quit();
-        }));
-    run_loop.Run();
-
-    std::vector<sync_pb::SharedTabGroupDataSpecifics> specifics;
-    for (const syncer::DataTypeStore::Record& record : *entries) {
-      proto::SharedTabGroupData local_proto;
-      bool success = local_proto.ParseFromString(record.value);
-      CHECK(success);
-      specifics.push_back(local_proto.specifics());
-    }
-    return specifics;
+  std::map<std::string, proto::SharedTabGroupData> GetAllLocalDataFromStore() {
+    return syncer::DataTypeStoreTestUtil::ReadAllDataAsProtoAndWait<
+        proto::SharedTabGroupData>(store());
   }
 
   // Generates and mocks unique positions for all the tabs in the `group`.
@@ -2622,10 +2596,13 @@
   ApplySingleEntityChange(CreateAddEntityChange(
       remote_tab_group_specifics.shared_tab_group_data(), kCollaborationId));
 
-  std::vector<sync_pb::SharedTabGroupDataSpecifics> stored_specifics =
-      GetAllSpecificsFromStore();
-  ASSERT_THAT(stored_specifics,
-              ElementsAre(GroupSpecificsHasUnsupportedField("extra_field")));
+  const std::string group_guid =
+      remote_tab_group_specifics.shared_tab_group_data().guid();
+  EXPECT_THAT(GetAllLocalDataFromStore(),
+              ElementsAre(Pair(
+                  group_guid,
+                  Property(&proto::SharedTabGroupData::specifics,
+                           GroupSpecificsHasUnsupportedField("extra_field")))));
 }
 
 TEST_F(SharedTabGroupDataSyncBridgeTest, ShouldNotPopulateKnownClearedFields) {
diff --git a/components/search_engines/template_url_service.cc b/components/search_engines/template_url_service.cc
index 583f0b6..7bb7686 100644
--- a/components/search_engines/template_url_service.cc
+++ b/components/search_engines/template_url_service.cc
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-
 #include "components/search_engines/template_url_service.h"
 
 #include <algorithm>
@@ -301,6 +300,30 @@
   NOTREACHED();
 }
 
+// Logs the number of changes of each type to the histogram
+// `histogram_prefix_{Type}` upon MergeDataAndStartSyncing and
+// ProcessSyncChanges.
+void LogSyncChangesToHistogram(const syncer::SyncChangeList& change_list,
+                               std::string_view histogram_prefix) {
+  auto counts = base::MakeFixedFlatMap<syncer::SyncChange::SyncChangeType, int>(
+      {{syncer::SyncChange::ACTION_ADD, 0},
+       {syncer::SyncChange::ACTION_UPDATE, 0},
+       {syncer::SyncChange::ACTION_DELETE, 0}});
+  for (const syncer::SyncChange& change : change_list) {
+    // No ADDs should be committed upon initial or incremental update.
+    CHECK(!base::FeatureList::IsEnabled(
+              syncer::kSeparateLocalAndAccountSearchEngines) ||
+          change.change_type() != syncer::SyncChange::ACTION_ADD);
+    ++counts.at(change.change_type());
+  }
+  for (const auto& [type, count] : counts) {
+    base::UmaHistogramCounts100(
+        base::StringPrintf("%s_%s", histogram_prefix,
+                           SyncChangeTypeToHistogramSuffix(type)),
+        count);
+  }
+}
+
 }  // namespace
 
 // TemplateURLService::LessWithPrefix -----------------------------------------
@@ -1901,6 +1924,8 @@
     return error;
   }
 
+  LogSyncChangesToHistogram(
+      new_changes, "Sync.SearchEngine.ChangesCommittedUponIncrementalUpdate");
   return sync_processor_->ProcessSyncChanges(from_here, new_changes);
 }
 
@@ -2024,8 +2049,8 @@
   // valid changes to sync_processor_.
   PruneSyncChanges(&sync_data_map, &new_changes);
 
-  base::UmaHistogramCounts100(
-      "Sync.SearchEngine.NewChangesCommittedUponSyncStart", new_changes.size());
+  LogSyncChangesToHistogram(new_changes,
+                            "Sync.SearchEngine.ChangesCommittedUponSyncStart");
   std::optional<syncer::ModelError> error =
       sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
   if (!error.has_value()) {
diff --git a/components/supervised_user/core/browser/supervised_user_metrics_service_unittest.cc b/components/supervised_user/core/browser/supervised_user_metrics_service_unittest.cc
index 66d716f..aa42cb18 100644
--- a/components/supervised_user/core/browser/supervised_user_metrics_service_unittest.cc
+++ b/components/supervised_user/core/browser/supervised_user_metrics_service_unittest.cc
@@ -57,7 +57,8 @@
         identity_test_env_.identity_manager(),
         test_url_loader_factory_.GetSafeWeakWrapper(), pref_service_,
         settings_service_, &sync_service_,
-        std::make_unique<FakeURLFilterDelegate>(),
+        std::make_unique<SupervisedUserURLFilter>(
+            pref_service_, std::make_unique<FakeURLFilterDelegate>()),
         std::make_unique<FakePlatformDelegate>());
     supervised_user_service_->Init();
   }
diff --git a/components/supervised_user/core/browser/supervised_user_service.cc b/components/supervised_user/core/browser/supervised_user_service.cc
index b8f4392..15470a0 100644
--- a/components/supervised_user/core/browser/supervised_user_service.cc
+++ b/components/supervised_user/core/browser/supervised_user_service.cc
@@ -108,11 +108,6 @@
   return url_filter_.get();
 }
 
-void SupervisedUserService::SetURLFilterForTesting(
-    std::unique_ptr<SupervisedUserURLFilter> test_filter) {
-  url_filter_ = std::move(test_filter);
-}
-
 std::optional<Custodian> SupervisedUserService::GetCustodian() const {
   return GetCustodianFromPrefs(user_prefs_.get(),
                                prefs::kSupervisedUserCustodianEmail,
@@ -154,17 +149,15 @@
     PrefService& user_prefs,
     SupervisedUserSettingsService& settings_service,
     syncer::SyncService* sync_service,
-    std::unique_ptr<SupervisedUserURLFilter::Delegate> url_filter_delegate,
+    std::unique_ptr<SupervisedUserURLFilter> url_filter,
     std::unique_ptr<SupervisedUserService::PlatformDelegate> platform_delegate)
     : user_prefs_(user_prefs),
       settings_service_(settings_service),
       sync_service_(sync_service),
       identity_manager_(identity_manager),
       url_loader_factory_(url_loader_factory),
-      platform_delegate_(std::move(platform_delegate)) {
-  url_filter_ = std::make_unique<SupervisedUserURLFilter>(
-      user_prefs, std::move(url_filter_delegate));
-}
+      platform_delegate_(std::move(platform_delegate)),
+      url_filter_(std::move(url_filter)) {}
 
 void SupervisedUserService::SetSettingsServiceActive(bool active) {
   settings_service_->SetActive(active);
diff --git a/components/supervised_user/core/browser/supervised_user_service.h b/components/supervised_user/core/browser/supervised_user_service.h
index 4ef79592..d4715a3 100644
--- a/components/supervised_user/core/browser/supervised_user_service.h
+++ b/components/supervised_user/core/browser/supervised_user_service.h
@@ -111,7 +111,7 @@
   // Returns the URL filter for filtering navigations and classifying sites in
   // the history view. Both this method and the returned filter may only be used
   // on the UI thread.
-  supervised_user::SupervisedUserURLFilter* GetURLFilter() const;
+  SupervisedUserURLFilter* GetURLFilter() const;
 
   std::optional<Custodian> GetCustodian() const;
   std::optional<Custodian> GetSecondCustodian() const;
@@ -142,49 +142,13 @@
       signin::IdentityManager* identity_manager,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
       PrefService& user_prefs,
-      supervised_user::SupervisedUserSettingsService& settings_service,
+      SupervisedUserSettingsService& settings_service,
       syncer::SyncService* sync_service,
-      std::unique_ptr<supervised_user::SupervisedUserURLFilter::Delegate>
-          url_filter_delegate,
-      std::unique_ptr<supervised_user::SupervisedUserService::PlatformDelegate>
+      std::unique_ptr<SupervisedUserURLFilter> url_filter,
+      std::unique_ptr<SupervisedUserService::PlatformDelegate>
           platform_delegate);
 
  private:
-  friend class SupervisedUserServiceExtensionTestBase;
-  friend class ::SupervisedUserServiceFactory;
-  friend class ClassifyUrlNavigationThrottleTest;
-  FRIEND_TEST_ALL_PREFIXES(
-      SupervisedUserServiceExtensionTest,
-      ExtensionManagementPolicyProviderWithoutSUInitiatedInstalls);
-  FRIEND_TEST_ALL_PREFIXES(
-      SupervisedUserServiceExtensionTest,
-      ExtensionManagementPolicyProviderWithSUInitiatedInstalls);
-  FRIEND_TEST_ALL_PREFIXES(SupervisedUserServiceTest, InterstitialBannerState);
-  FRIEND_TEST_ALL_PREFIXES(SupervisedUserNavigationThrottleTest,
-                           BlockedMatureSitesRecordedInBlockSafeSitesBucket);
-  FRIEND_TEST_ALL_PREFIXES(ClassifyUrlNavigationThrottleTest,
-                           BlockedMatureSitesRecordedInBlockSafeSitesBucket);
-  FRIEND_TEST_ALL_PREFIXES(ClassifyUrlNavigationThrottleTest,
-                           ClassificationIsFasterThanHttp);
-  FRIEND_TEST_ALL_PREFIXES(ClassifyUrlNavigationThrottleTest,
-                           ClassificationIsSlowerThanHttp);
-  FRIEND_TEST_ALL_PREFIXES(ClassifyUrlNavigationThrottleTest,
-                           ReverseOrderOfResponsesAfterContentIsReady);
-  FRIEND_TEST_ALL_PREFIXES(ClassifyUrlNavigationThrottleParallelizationTest,
-                           ClassificationIsFasterThanHttp);
-  FRIEND_TEST_ALL_PREFIXES(ClassifyUrlNavigationThrottleParallelizationTest,
-                           ClassificationIsSlowerThanHttp);
-  FRIEND_TEST_ALL_PREFIXES(ClassifyUrlNavigationThrottleParallelizationTest,
-                           ShortCircuitsSynchronousBlock);
-  FRIEND_TEST_ALL_PREFIXES(ClassifyUrlNavigationThrottleParallelizationTest,
-                           HandlesLateAsynchronousBlock);
-  FRIEND_TEST_ALL_PREFIXES(ClassifyUrlNavigationThrottleParallelizationTest,
-                           OutOfOrderClassification);
-
-  // Method used in testing to set the given test_filter as the url_filter_
-  void SetURLFilterForTesting(
-      std::unique_ptr<SupervisedUserURLFilter> test_filter);
-
   void SetActive(bool active);
 
   void SetSettingsServiceActive(bool active);
@@ -209,8 +173,7 @@
 
   const raw_ref<PrefService> user_prefs_;
 
-  const raw_ref<supervised_user::SupervisedUserSettingsService>
-      settings_service_;
+  const raw_ref<SupervisedUserSettingsService> settings_service_;
 
   const raw_ptr<syncer::SyncService> sync_service_;
 
diff --git a/components/supervised_user/core/browser/supervised_user_service_unittest.cc b/components/supervised_user/core/browser/supervised_user_service_unittest.cc
index e68705b..45529bc6 100644
--- a/components/supervised_user/core/browser/supervised_user_service_unittest.cc
+++ b/components/supervised_user/core/browser/supervised_user_service_unittest.cc
@@ -39,7 +39,6 @@
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
 #include "services/network/test/test_url_loader_factory.h"
-#include "supervised_user_metrics_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -80,7 +79,8 @@
         identity_test_env_.identity_manager(),
         test_url_loader_factory_.GetSafeWeakWrapper(), syncable_pref_service_,
         settings_service_, &sync_service_,
-        std::make_unique<FakeURLFilterDelegate>(),
+        std::make_unique<SupervisedUserURLFilter>(
+            syncable_pref_service_, std::make_unique<FakeURLFilterDelegate>()),
         std::make_unique<FakePlatformDelegate>());
     service_->Init();
 
diff --git a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
index 7113de65..b50f748e 100644
--- a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
+++ b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
@@ -1950,8 +1950,6 @@
 // Tests that the initial built keystore Nigori, includes initialized
 // Public-private key-pairs.
 TEST_F(NigoriSyncBridgeImplTest, ShouldInitKeystoreNigoriWithKeyPair) {
-  base::HistogramTester histogram_tester;
-
   const KeyParamsForTesting kKeystoreKeyParams =
       KeystoreKeyParamsForTesting(kRawKeystoreKey);
 
@@ -1984,14 +1982,10 @@
               HasPublicKeyVersionAndValue(0, key_value));
 
   EXPECT_THAT(bridge()->GetKeystoreMigrationTime(), Not(NullTime()));
-  histogram_tester.ExpectUniqueSample(
-      "Sync.CrossUserSharingPublicPrivateKeyInitSuccess", true, 1);
 }
 
 TEST_F(NigoriSyncBridgeImplTest,
        ShouldFailOnDifferentKeyInitializingKeystoreNigoriWithKeyPair) {
-  base::HistogramTester histogram_tester;
-
   const KeyParamsForTesting kKeystoreKeyParams =
       KeystoreKeyParamsForTesting(kRawKeystoreKey);
 
@@ -2023,15 +2017,11 @@
               Eq(std::nullopt));
   EXPECT_THAT(bridge()->GetDataForDebugging(), Not(HasKeystoreNigori()));
   EXPECT_THAT(bridge()->GetDataForDebugging(), Not(HasPublicKeyVersion(0)));
-  histogram_tester.ExpectUniqueSample(
-      "Sync.CrossUserSharingPublicPrivateKeyInitSuccess", false, 1);
 }
 
 // Tests that an existing Nigori will be initialized with Public-private
 // key-pairs.
 TEST_F(NigoriSyncBridgeImplTest, ShouldInitKeyPairForExistingNigori) {
-  base::HistogramTester histogram_tester;
-
   // Perform initial sync with simple keystore Nigori without key pair.
   const KeyParamsForTesting kKeystoreKeyParams =
       KeystoreKeyParamsForTesting(kRawKeystoreKey);
@@ -2059,14 +2049,10 @@
   // bridge.
   EXPECT_THAT(bridge()->GetDataForDebugging(),
               HasPublicKeyVersionAndValue(0, key_value));
-  histogram_tester.ExpectUniqueSample(
-      "Sync.CrossUserSharingPublicPrivateKeyInitSuccess", true, 1);
 }
 
 TEST_F(NigoriSyncBridgeImplTest,
        ShouldFailOnDifferentNigoriKeyInitializingKeyPairForExistingNigori) {
-  base::HistogramTester histogram_tester;
-
   // Perform initial sync with simple keystore Nigori without key pair.
   const KeyParamsForTesting kKeystoreKeyParams =
       KeystoreKeyParamsForTesting(kRawKeystoreKey);
@@ -2092,8 +2078,6 @@
   // passphrase Nigori. Bridge should not attempt to commit cross user sharing
   // key anymore, because it can't decrypt the custom passphrase Nigori yet.
   EXPECT_THAT(bridge()->GetDataForCommit(), Not(HasPublicKeyVersion(0)));
-  histogram_tester.ExpectUniqueSample(
-      "Sync.CrossUserSharingPublicPrivateKeyInitSuccess", false, 1);
 }
 
 TEST_F(NigoriSyncBridgeImplTest, ShouldRegenerateKeyPairIfCorrupted) {
diff --git a/components/sync/nigori/pending_local_nigori_commit.cc b/components/sync/nigori/pending_local_nigori_commit.cc
index 60528c8..e53ce017 100644
--- a/components/sync/nigori/pending_local_nigori_commit.cc
+++ b/components/sync/nigori/pending_local_nigori_commit.cc
@@ -48,11 +48,6 @@
   state->cryptographer->SelectDefaultCrossUserSharingKey(version);
 }
 
-void LogCrossUserSharingPublicPrivateKeyInit(bool is_succesful) {
-  base::UmaHistogramBoolean("Sync.CrossUserSharingPublicPrivateKeyInitSuccess",
-                            is_succesful);
-}
-
 class CustomPassphraseSetter : public PendingLocalNigoriCommit {
  public:
   explicit CustomPassphraseSetter(
@@ -167,12 +162,9 @@
                                       /*passphrase_time=*/base::Time());
     observer->OnCryptographerStateChanged(state.cryptographer.get(),
                                           /*has_pending_keys=*/false);
-    LogCrossUserSharingPublicPrivateKeyInit(true);
   }
 
-  void OnFailure(SyncEncryptionHandler::Observer* observer) override {
-    LogCrossUserSharingPublicPrivateKeyInit(false);
-  }
+  void OnFailure(SyncEncryptionHandler::Observer* observer) override {}
 
  private:
   std::optional<CrossUserSharingPublicPrivateKeyPair>
@@ -236,12 +228,9 @@
                  SyncEncryptionHandler::Observer* observer) override {
     observer->OnCryptographerStateChanged(state.cryptographer.get(),
                                           /*has_pending_keys=*/false);
-    LogCrossUserSharingPublicPrivateKeyInit(true);
   }
 
-  void OnFailure(SyncEncryptionHandler::Observer* observer) override {
-    LogCrossUserSharingPublicPrivateKeyInit(false);
-  }
+  void OnFailure(SyncEncryptionHandler::Observer* observer) override {}
 
  private:
   CrossUserSharingPublicPrivateKeyPair
diff --git a/components/sync/test/data_type_store_test_util.cc b/components/sync/test/data_type_store_test_util.cc
index 397fcdf..136f165 100644
--- a/components/sync/test/data_type_store_test_util.cc
+++ b/components/sync/test/data_type_store_test_util.cc
@@ -4,13 +4,16 @@
 
 #include "components/sync/test/data_type_store_test_util.h"
 
+#include <memory>
 #include <utility>
 
 #include "base/debug/leak_annotations.h"
 #include "base/functional/bind.h"
 #include "base/memory/raw_ptr.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/run_loop.h"
 #include "base/task/sequenced_task_runner.h"
+#include "base/test/bind.h"
 #include "components/sync/base/data_type.h"
 #include "components/sync/model/blocking_data_type_store_impl.h"
 #include "components/sync/model/data_type_store_backend.h"
@@ -123,4 +126,26 @@
       base::Unretained(target));
 }
 
+// static
+DataTypeStore::RecordList DataTypeStoreTestUtil::ReadAllDataAndWait(
+    DataTypeStore& store) {
+  DataTypeStore::RecordList result;
+  base::RunLoop loop;
+  store.ReadAllData(base::BindLambdaForTesting(
+      [&result, &loop](
+          const std::optional<ModelError>& error,
+          std::unique_ptr<DataTypeStore::RecordList> data_records) {
+        if (error.has_value()) {
+          ADD_FAILURE() << error->ToString();
+        } else if (!data_records) {
+          ADD_FAILURE() << "data_records is null";
+        } else {
+          result = std::move(*data_records);
+        }
+        loop.Quit();
+      }));
+  loop.Run();
+  return result;
+}
+
 }  // namespace syncer
diff --git a/components/sync/test/data_type_store_test_util.h b/components/sync/test/data_type_store_test_util.h
index 60ee73e..3a8e37f 100644
--- a/components/sync/test/data_type_store_test_util.h
+++ b/components/sync/test/data_type_store_test_util.h
@@ -5,10 +5,14 @@
 #ifndef COMPONENTS_SYNC_TEST_DATA_TYPE_STORE_TEST_UTIL_H_
 #define COMPONENTS_SYNC_TEST_DATA_TYPE_STORE_TEST_UTIL_H_
 
+#include <map>
 #include <memory>
+#include <string>
+#include <utility>
 
 #include "components/sync/base/storage_type.h"
 #include "components/sync/model/data_type_store.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace syncer {
 
@@ -34,6 +38,25 @@
   // created by the factory.
   static RepeatingDataTypeStoreFactory FactoryForForwardingStore(
       DataTypeStore* target);
+
+  // Reads and returns all data records from the `store`.
+  static DataTypeStore::RecordList ReadAllDataAndWait(DataTypeStore& store);
+
+  // Reads and returns all data records from the `store` as protos of type `T`.
+  // The returned map is keyed by storage keys.
+  template <typename T>
+  static std::map<std::string, T> ReadAllDataAsProtoAndWait(
+      DataTypeStore& store) {
+    std::map<std::string, T> result;
+    for (const DataTypeStore::Record& record : ReadAllDataAndWait(store)) {
+      T data;
+      if (!data.ParseFromString(record.value)) {
+        ADD_FAILURE() << "Failed to parse storage key: " << record.id;
+      }
+      result.emplace(record.id, std::move(data));
+    }
+    return result;
+  }
 };
 
 }  // namespace syncer
diff --git a/components/sync_bookmarks/bookmark_data_type_processor_unittest.cc b/components/sync_bookmarks/bookmark_data_type_processor_unittest.cc
index b41f8d1..a1c283e 100644
--- a/components/sync_bookmarks/bookmark_data_type_processor_unittest.cc
+++ b/components/sync_bookmarks/bookmark_data_type_processor_unittest.cc
@@ -1752,14 +1752,12 @@
 
   SimulateModelReadyToSyncWithInitialSyncDone();
   SimulateOnSyncStarting();
-  ASSERT_FALSE(bookmark_model()
-                   ->underlying_model()
-                   ->HasNoUserCreatedBookmarksOrFolders());
+  ASSERT_TRUE(
+      bookmark_model()->underlying_model()->HasUserCreatedBookmarksOrFolders());
 
   processor()->OnSyncStopping(syncer::CLEAR_METADATA);
-  EXPECT_TRUE(bookmark_model()
-                  ->underlying_model()
-                  ->HasNoUserCreatedBookmarksOrFolders());
+  EXPECT_FALSE(
+      bookmark_model()->underlying_model()->HasUserCreatedBookmarksOrFolders());
 
   // If the process is repeated, the result should be the same (bookmarks
   // deleted once again). This requires doing initial sync again.
@@ -1770,14 +1768,12 @@
   bookmark_model()->AddURL(bookmark_model()->bookmark_bar_node(), /*index=*/0,
                            u"foo", kUrl);
   ASSERT_TRUE(processor()->IsTrackingMetadata());
-  ASSERT_FALSE(bookmark_model()
-                   ->underlying_model()
-                   ->HasNoUserCreatedBookmarksOrFolders());
+  ASSERT_TRUE(
+      bookmark_model()->underlying_model()->HasUserCreatedBookmarksOrFolders());
 
   processor()->OnSyncStopping(syncer::CLEAR_METADATA);
-  EXPECT_TRUE(bookmark_model()
-                  ->underlying_model()
-                  ->HasNoUserCreatedBookmarksOrFolders());
+  EXPECT_FALSE(
+      bookmark_model()->underlying_model()->HasUserCreatedBookmarksOrFolders());
 }
 
 TEST_F(BookmarkDataTypeProcessorTest,
@@ -1845,9 +1841,8 @@
                            u"foo", GURL("http://www.example.com"));
 
   ASSERT_TRUE(processor()->IsTrackingMetadata());
-  ASSERT_FALSE(bookmark_model()
-                   ->underlying_model()
-                   ->HasNoUserCreatedBookmarksOrFolders());
+  ASSERT_TRUE(
+      bookmark_model()->underlying_model()->HasUserCreatedBookmarksOrFolders());
 
   base::HistogramTester histogram_tester;
 
@@ -1863,9 +1858,8 @@
       "Sync.ClearMetadataWhileStopped.ImmediateClear", 1);
 
   // Local bookmarks should have been deleted.
-  EXPECT_TRUE(bookmark_model()
-                  ->underlying_model()
-                  ->HasNoUserCreatedBookmarksOrFolders());
+  EXPECT_FALSE(
+      bookmark_model()->underlying_model()->HasUserCreatedBookmarksOrFolders());
 }
 
 TEST_F(
@@ -1893,9 +1887,8 @@
       GURL("http://www.example.com"));
 
   ASSERT_FALSE(processor()->IsTrackingMetadata());
-  ASSERT_FALSE(bookmark_model()
-                   ->underlying_model()
-                   ->HasNoUserCreatedBookmarksOrFolders());
+  ASSERT_TRUE(
+      bookmark_model()->underlying_model()->HasUserCreatedBookmarksOrFolders());
 
   sync_pb::BookmarkModelMetadata model_metadata =
       CreateMetadataForPermanentNodes(bookmark_model());
@@ -1927,18 +1920,16 @@
   bookmark_model()->AddURL(bookmark_model()->bookmark_bar_node(), /*index=*/0,
                            u"foo", GURL("http://www.example.com"));
 
-  ASSERT_FALSE(bookmark_model()
-                   ->underlying_model()
-                   ->HasNoUserCreatedBookmarksOrFolders());
+  ASSERT_TRUE(
+      bookmark_model()->underlying_model()->HasUserCreatedBookmarksOrFolders());
 
   SimulateOnSyncStarting("unexpected_cache_guid");
 
   EXPECT_FALSE(processor()->IsTrackingMetadata());
 
   // Local bookmarks should have been deleted.
-  EXPECT_TRUE(bookmark_model()
-                  ->underlying_model()
-                  ->HasNoUserCreatedBookmarksOrFolders());
+  EXPECT_FALSE(
+      bookmark_model()->underlying_model()->HasUserCreatedBookmarksOrFolders());
 }
 
 TEST_F(BookmarkDataTypeProcessorTest,
diff --git a/components/sync_device_info/device_info_sync_bridge_unittest.cc b/components/sync_device_info/device_info_sync_bridge_unittest.cc
index d987b07d..93eb0230 100644
--- a/components/sync_device_info/device_info_sync_bridge_unittest.cc
+++ b/components/sync_device_info/device_info_sync_bridge_unittest.cc
@@ -679,28 +679,8 @@
   }
 
   std::map<std::string, DeviceInfoSpecifics> ReadAllFromStore() {
-    std::unique_ptr<DataTypeStore::RecordList> records;
-    base::RunLoop loop;
-    store()->ReadAllData(base::BindOnce(
-        [](std::unique_ptr<DataTypeStore::RecordList>* output_records,
-           base::RunLoop* loop, const std::optional<syncer::ModelError>& error,
-           std::unique_ptr<DataTypeStore::RecordList> input_records) {
-          EXPECT_FALSE(error) << error->ToString();
-          EXPECT_THAT(input_records, NotNull());
-          *output_records = std::move(input_records);
-          loop->Quit();
-        },
-        &records, &loop));
-    loop.Run();
-    std::map<std::string, DeviceInfoSpecifics> result;
-    if (records) {
-      for (const DataTypeStore::Record& record : *records) {
-        DeviceInfoSpecifics specifics;
-        EXPECT_TRUE(specifics.ParseFromString(record.value));
-        result.emplace(record.id, specifics);
-      }
-    }
-    return result;
+    return DataTypeStoreTestUtil::ReadAllDataAsProtoAndWait<
+        DeviceInfoSpecifics>(*store());
   }
 
   std::map<std::string, sync_pb::EntitySpecifics> GetAllData() {
diff --git a/components/sync_sessions/session_store_unittest.cc b/components/sync_sessions/session_store_unittest.cc
index 6c0d09a7..32a8b4a 100644
--- a/components/sync_sessions/session_store_unittest.cc
+++ b/components/sync_sessions/session_store_unittest.cc
@@ -130,28 +130,8 @@
 
 std::map<std::string, SessionSpecifics> ReadAllPersistedDataFrom(
     DataTypeStore* store) {
-  std::unique_ptr<DataTypeStore::RecordList> records;
-  base::RunLoop loop;
-  store->ReadAllData(base::BindOnce(
-      [](std::unique_ptr<DataTypeStore::RecordList>* output_records,
-         base::RunLoop* loop, const std::optional<syncer::ModelError>& error,
-         std::unique_ptr<DataTypeStore::RecordList> input_records) {
-        EXPECT_FALSE(error) << error->ToString();
-        EXPECT_THAT(input_records, NotNull());
-        *output_records = std::move(input_records);
-        loop->Quit();
-      },
-      &records, &loop));
-  loop.Run();
-  std::map<std::string, SessionSpecifics> result;
-  if (records) {
-    for (const DataTypeStore::Record& record : *records) {
-      SessionSpecifics specifics;
-      EXPECT_TRUE(specifics.ParseFromString(record.value));
-      result.emplace(record.id, specifics);
-    }
-  }
-  return result;
+  return syncer::DataTypeStoreTestUtil::ReadAllDataAsProtoAndWait<
+      SessionSpecifics>(*store);
 }
 
 class SessionStoreOpenTest : public ::testing::Test {
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index 930a2c00..3b2a4442 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -1096,6 +1096,9 @@
 }
 
 TEST_F(AuthenticatorImplTest, GetClientCapabilities) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitWithFeatureState(device::kWebAuthnImmediateGet, false);
+
   NavigateAndCommit(GURL(kTestOrigin1));
 
   ClientCapabilitiesList capabilities = AuthenticatorGetClientCapabilities();
diff --git a/device/fido/features.cc b/device/fido/features.cc
index de747a55..34032819 100644
--- a/device/fido/features.cc
+++ b/device/fido/features.cc
@@ -193,9 +193,10 @@
                    "window_seconds",
                    kDefaultWindowSeconds);
 
-// Not yet enabled by default.
+// Enabled by default for the Origin Trial. Do not remove until the Origin Trial
+// expires.
 BASE_FEATURE(kWebAuthnImmediateGet,
              "WebAuthenticationImmediateGet",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 
 }  // namespace device
diff --git a/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json b/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json
index f09ba67..f9f9a36 100644
--- a/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json
+++ b/infra/config/generated/builders/ci/Win x64 Builder/targets/chromium.win.json
@@ -2015,7 +2015,7 @@
             "os": "Windows-10-19045"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 17
+          "shards": 19
         },
         "test": "headless_shell_wpt",
         "test_id_prefix": "ninja://:headless_shell_wpt/"
diff --git a/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json b/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json
index 8636ac0..d34bb54 100644
--- a/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json
+++ b/infra/config/generated/builders/ci/Win10 Tests x64/targets/chromium.win.json
@@ -1991,7 +1991,7 @@
             "os": "Windows-10-19045"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 17
+          "shards": 19
         },
         "test": "headless_shell_wpt",
         "test_id_prefix": "ninja://:headless_shell_wpt/"
diff --git a/infra/config/generated/builders/ci/android-15-x64-rel/targets/chromium.android.json b/infra/config/generated/builders/ci/android-15-x64-rel/targets/chromium.android.json
index 2f8beec..729d0e2 100644
--- a/infra/config/generated/builders/ci/android-15-x64-rel/targets/chromium.android.json
+++ b/infra/config/generated/builders/ci/android-15-x64-rel/targets/chromium.android.json
@@ -90,7 +90,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 10
+          "shards": 11
         },
         "test": "android_browsertests",
         "test_id_prefix": "ninja://chrome/test:android_browsertests/"
@@ -844,7 +844,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 50
+          "shards": 64
         },
         "test": "chrome_public_test_apk",
         "test_id_prefix": "ninja://chrome/android:chrome_public_test_apk/"
diff --git a/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/targets/chromium.android.desktop.json b/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/targets/chromium.android.desktop.json
index 71e975be..21853a95 100644
--- a/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/targets/chromium.android.desktop.json
+++ b/infra/config/generated/builders/ci/android-desktop-x64-compile-rel/targets/chromium.android.desktop.json
@@ -50,7 +50,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 114
+          "shards": 171
         },
         "test": "android_browsertests",
         "test_id_prefix": "ninja://chrome/test:android_browsertests/"
@@ -199,7 +199,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 4
+          "shards": 5
         },
         "test": "unit_tests",
         "test_id_prefix": "ninja://chrome/test:unit_tests/"
diff --git a/infra/config/generated/builders/ci/android-desktop-x64-rel-15-tests/targets/chromium.android.desktop.json b/infra/config/generated/builders/ci/android-desktop-x64-rel-15-tests/targets/chromium.android.desktop.json
index e267869..b2324c6 100644
--- a/infra/config/generated/builders/ci/android-desktop-x64-rel-15-tests/targets/chromium.android.desktop.json
+++ b/infra/config/generated/builders/ci/android-desktop-x64-rel-15-tests/targets/chromium.android.desktop.json
@@ -45,7 +45,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 114
+          "shards": 171
         },
         "test": "android_browsertests",
         "test_id_prefix": "ninja://chrome/test:android_browsertests/"
@@ -194,7 +194,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 4
+          "shards": 5
         },
         "test": "unit_tests",
         "test_id_prefix": "ninja://chrome/test:unit_tests/"
diff --git a/infra/config/generated/builders/try/android-15-x64-rel/targets/chromium.android.json b/infra/config/generated/builders/try/android-15-x64-rel/targets/chromium.android.json
index 2f8beec..729d0e2 100644
--- a/infra/config/generated/builders/try/android-15-x64-rel/targets/chromium.android.json
+++ b/infra/config/generated/builders/try/android-15-x64-rel/targets/chromium.android.json
@@ -90,7 +90,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 10
+          "shards": 11
         },
         "test": "android_browsertests",
         "test_id_prefix": "ninja://chrome/test:android_browsertests/"
@@ -844,7 +844,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 50
+          "shards": 64
         },
         "test": "chrome_public_test_apk",
         "test_id_prefix": "ninja://chrome/android:chrome_public_test_apk/"
diff --git a/infra/config/generated/builders/try/android-desktop-15-x64-rel/targets/chromium.android.desktop.json b/infra/config/generated/builders/try/android-desktop-15-x64-rel/targets/chromium.android.desktop.json
index 71e975be..21853a95 100644
--- a/infra/config/generated/builders/try/android-desktop-15-x64-rel/targets/chromium.android.desktop.json
+++ b/infra/config/generated/builders/try/android-desktop-15-x64-rel/targets/chromium.android.desktop.json
@@ -50,7 +50,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 114
+          "shards": 171
         },
         "test": "android_browsertests",
         "test_id_prefix": "ninja://chrome/test:android_browsertests/"
@@ -199,7 +199,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 4
+          "shards": 5
         },
         "test": "unit_tests",
         "test_id_prefix": "ninja://chrome/test:unit_tests/"
diff --git a/infra/config/generated/builders/try/android-desktop-x64-rel/targets/chromium.android.desktop.json b/infra/config/generated/builders/try/android-desktop-x64-rel/targets/chromium.android.desktop.json
index 71e975be..21853a95 100644
--- a/infra/config/generated/builders/try/android-desktop-x64-rel/targets/chromium.android.desktop.json
+++ b/infra/config/generated/builders/try/android-desktop-x64-rel/targets/chromium.android.desktop.json
@@ -50,7 +50,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 114
+          "shards": 171
         },
         "test": "android_browsertests",
         "test_id_prefix": "ninja://chrome/test:android_browsertests/"
@@ -199,7 +199,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 4
+          "shards": 5
         },
         "test": "unit_tests",
         "test_id_prefix": "ninja://chrome/test:unit_tests/"
diff --git a/infra/config/generated/builders/try/android-x64-rel/targets/chromium.android.json b/infra/config/generated/builders/try/android-x64-rel/targets/chromium.android.json
index 7ae20c9..816862d4 100644
--- a/infra/config/generated/builders/try/android-x64-rel/targets/chromium.android.json
+++ b/infra/config/generated/builders/try/android-x64-rel/targets/chromium.android.json
@@ -147,7 +147,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 10
+          "shards": 11
         },
         "test": "android_browsertests",
         "test_id_prefix": "ninja://chrome/test:android_browsertests/"
@@ -901,7 +901,7 @@
             }
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 50
+          "shards": 64
         },
         "test": "chrome_public_test_apk",
         "test_id_prefix": "ninja://chrome/android:chrome_public_test_apk/"
diff --git a/infra/config/generated/builders/try/win-rel/targets/chromium.win.json b/infra/config/generated/builders/try/win-rel/targets/chromium.win.json
index 73ce178a..8d94aaf 100644
--- a/infra/config/generated/builders/try/win-rel/targets/chromium.win.json
+++ b/infra/config/generated/builders/try/win-rel/targets/chromium.win.json
@@ -2015,7 +2015,7 @@
             "os": "Windows-10-19045"
           },
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 17
+          "shards": 19
         },
         "test": "headless_shell_wpt",
         "test_id_prefix": "ninja://:headless_shell_wpt/"
diff --git "a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Builder \050dbg\051/targets/chromium.webrtc.fyi.json" "b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Builder \050dbg\051/targets/chromium.webrtc.fyi.json"
index d4133530..c7d9f4db 100644
--- "a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Builder \050dbg\051/targets/chromium.webrtc.fyi.json"
+++ "b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Builder \050dbg\051/targets/chromium.webrtc.fyi.json"
@@ -25,7 +25,8 @@
             "device_type": "walleye",
             "os": "Android"
           },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 4
         },
         "test": "content_browsertests",
         "test_id_prefix": "ninja://content/test:content_browsertests/"
diff --git "a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Builder ARM64 \050dbg\051/targets/chromium.webrtc.fyi.json" "b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Builder ARM64 \050dbg\051/targets/chromium.webrtc.fyi.json"
index ab9250b..a1118c6d 100644
--- "a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Builder ARM64 \050dbg\051/targets/chromium.webrtc.fyi.json"
+++ "b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Builder ARM64 \050dbg\051/targets/chromium.webrtc.fyi.json"
@@ -25,7 +25,8 @@
             "device_type": "walleye",
             "os": "Android"
           },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 4
         },
         "test": "content_browsertests",
         "test_id_prefix": "ninja://content/test:content_browsertests/"
diff --git "a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Tests \050dbg\051/targets/chromium.webrtc.fyi.json" "b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Tests \050dbg\051/targets/chromium.webrtc.fyi.json"
index e7ec5b51..f0612dc 100644
--- "a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Tests \050dbg\051/targets/chromium.webrtc.fyi.json"
+++ "b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Tests \050dbg\051/targets/chromium.webrtc.fyi.json"
@@ -24,7 +24,8 @@
             "device_type": "walleye",
             "os": "Android"
           },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 4
         },
         "test": "content_browsertests",
         "test_id_prefix": "ninja://content/test:content_browsertests/"
diff --git "a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Tests ARM64 \050dbg\051/targets/chromium.webrtc.fyi.json" "b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Tests ARM64 \050dbg\051/targets/chromium.webrtc.fyi.json"
index 501db9b5..a2ed23ce 100644
--- "a/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Tests ARM64 \050dbg\051/targets/chromium.webrtc.fyi.json"
+++ "b/infra/config/generated/builders/webrtc.fyi/WebRTC Chromium FYI Android Tests ARM64 \050dbg\051/targets/chromium.webrtc.fyi.json"
@@ -24,7 +24,8 @@
             "device_type": "walleye",
             "os": "Android"
           },
-          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com",
+          "shards": 4
         },
         "test": "content_browsertests",
         "test_id_prefix": "ninja://content/test:content_browsertests/"
diff --git a/infra/config/subprojects/webrtc/webrtc.fyi.star b/infra/config/subprojects/webrtc/webrtc.fyi.star
index 798aa87..b98cf8e 100644
--- a/infra/config/subprojects/webrtc/webrtc.fyi.star
+++ b/infra/config/subprojects/webrtc/webrtc.fyi.star
@@ -213,6 +213,9 @@
                 args = [
                     "--test-launcher-filter-file=../../testing/buildbot/filters/chromium.webrtc.fyi.android.tests.dbg.content_browsertests.filter",
                 ],
+                swarming = targets.swarming(
+                    shards = 4,
+                ),
             ),
         },
     ),
@@ -257,6 +260,9 @@
                 args = [
                     "--test-launcher-filter-file=../../testing/buildbot/filters/chromium.webrtc.fyi.android.tests.dbg.content_browsertests.filter",
                 ],
+                swarming = targets.swarming(
+                    shards = 4,
+                ),
             ),
         },
     ),
diff --git a/infra/config/targets/autoshard_exceptions.json b/infra/config/targets/autoshard_exceptions.json
index 87d173d..a36e3fc 100644
--- a/infra/config/targets/autoshard_exceptions.json
+++ b/infra/config/targets/autoshard_exceptions.json
@@ -8,7 +8,11 @@
         },
         "android-15-x64-rel": {
             "android_browsertests": {
-                "shards": 10,
+                "shards": 11,
+                "try_builder": "android-x64-rel"
+            },
+            "chrome_public_test_apk": {
+                "shards": 64,
                 "try_builder": "android-x64-rel"
             },
             "components_browsertests": {
@@ -42,11 +46,11 @@
     "chromium.android.desktop": {
         "android-desktop-x64-rel-15-tests": {
             "android_browsertests": {
-                "shards": 114,
+                "shards": 171,
                 "try_builder": "android-desktop-x64-rel"
             },
             "unit_tests": {
-                "shards": 4,
+                "shards": 5,
                 "try_builder": "android-desktop-x64-rel"
             }
         }
@@ -222,7 +226,7 @@
                 "try_builder": "win-rel"
             },
             "headless_shell_wpt_tests": {
-                "shards": 17,
+                "shards": 19,
                 "try_builder": "win-rel"
             },
             "interactive_ui_tests": {
diff --git a/ios/chrome/app/application_delegate/metrics_mediator.mm b/ios/chrome/app/application_delegate/metrics_mediator.mm
index 8780ff0..6b555992 100644
--- a/ios/chrome/app/application_delegate/metrics_mediator.mm
+++ b/ios/chrome/app/application_delegate/metrics_mediator.mm
@@ -549,6 +549,7 @@
 
 #if BUILDFLAG(ENABLE_WIDGET_KIT_EXTENSION)
   [WidgetMetricsUtil logInstalledWidgets];
+  [WidgetMetricsUtil logWidgetDeletedUiCount];
 #endif
 
   // Create the first user action recorder and schedule a task to expire it
diff --git a/ios/chrome/app/profile/identity_confirmation_profile_agent.mm b/ios/chrome/app/profile/identity_confirmation_profile_agent.mm
index 289e105e..5094866 100644
--- a/ios/chrome/app/profile/identity_confirmation_profile_agent.mm
+++ b/ios/chrome/app/profile/identity_confirmation_profile_agent.mm
@@ -143,7 +143,10 @@
     return IdentityConfirmationSnackbarDecision::kDontShowSingleAccount;
   }
 
-  if (![self isStartSurfaceWithBrowser:browser]) {
+  // For non-managed accounts, show the snackbar only on top of Bling Start.
+  if (!authenticationService->HasPrimaryIdentityManaged(
+          signin::ConsentLevel::kSignin) &&
+      ![self isStartSurfaceWithBrowser:browser]) {
     return IdentityConfirmationSnackbarDecision::kDontShowNotOnStartPage;
   }
 
diff --git a/ios/chrome/app/resources/Settings.bundle/Experimental.plist b/ios/chrome/app/resources/Settings.bundle/Experimental.plist
index 09e7bbf..666a96a 100644
--- a/ios/chrome/app/resources/Settings.bundle/Experimental.plist
+++ b/ios/chrome/app/resources/Settings.bundle/Experimental.plist
@@ -176,6 +176,7 @@
 				<string>promos_manager::Promo::OmniboxPosition</string>
 				<string>promos_manager::Promo::DockingPromo</string>
 				<string>promos_manager::Promo::PostDefaultAbandonment</string>
+				<string>promos_manager::Promo::GLICPromo</string>
 			</array>
 			<key>Titles</key>
 			<array>
@@ -186,6 +187,7 @@
 				<string>Omnibox Position</string>
 				<string>Docking Promo</string>
 				<string>Post-Default Browser Abandonment Alert</string>
+				<string>GLIC Promo</string>
 			</array>
 		</dict>
 		<dict>
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 8913775..c119285 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -3826,6 +3826,9 @@
       <message name="IDS_IOS_OPTIONS_ACCOUNTS_SIGNIN_ACCESSIBILITY_LABEL" desc="This string is found on a button on a list of available Google Accounts that the user sees when they open the profile menu on Chrome on iOS. When the user taps this button, Chrome will sign the user in to the selected account (and sign them out of the previous account). This text is a verb phrase. The tone should be factual. [iOS only]">
         Sign in as <ph name="EMAIL">$1<ex>johndoe@gmail.com</ex></ph>
       </message>
+      <message name="IDS_IOS_OPTIONS_ACCOUNTS_SIGNIN_WITH_NAME_ACCESSIBILITY_LABEL" desc="This string is found on a button on a list of available Google Accounts that the user sees when they open the profile menu on Chrome on iOS. When the user taps this button, Chrome will sign the user in to the selected account (and sign them out of the previous account). This text is a verb phrase. The tone should be factual. [iOS only]">
+        Sign in as <ph name="NAME">$1<ex>John Doe</ex></ph> <ph name="EMAIL">$2<ex>johndoe@gmail.com</ex></ph>
+      </message>
       <message name="IDS_IOS_OPTIONS_ACCOUNTS_SYNC_IS_OFF" desc="Text informing the user that sync is off [iOS only] [60em]" meaning="The Chrome sync feature is off.">
         Off
       </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_OPTIONS_ACCOUNTS_SIGNIN_WITH_NAME_ACCESSIBILITY_LABEL.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_OPTIONS_ACCOUNTS_SIGNIN_WITH_NAME_ACCESSIBILITY_LABEL.png.sha1
new file mode 100644
index 0000000..959a375
--- /dev/null
+++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_OPTIONS_ACCOUNTS_SIGNIN_WITH_NAME_ACCESSIBILITY_LABEL.png.sha1
@@ -0,0 +1 @@
+eb7033f044a9bda463546d68018e1df7f5ca9f50
\ No newline at end of file
diff --git a/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.mm b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.mm
index 263c067..bbd80ee 100644
--- a/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/account_menu/account_menu_view_controller.mm
@@ -420,12 +420,19 @@
 
   cell.imageView.image = [self.dataSource imageForGaiaID:gaiaID];
   cell.textLabel.text = [self.dataSource nameForGaiaID:gaiaID];
+  NSString* name = [self.dataSource nameForGaiaID:gaiaID];
   NSString* email = [self.dataSource emailForGaiaID:gaiaID];
   cell.detailTextLabel.text = email;
   cell.detailTextLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
-  cell.accessibilityLabel = l10n_util::GetNSStringF(
-      IDS_IOS_OPTIONS_ACCOUNTS_SIGNIN_ACCESSIBILITY_LABEL,
-      base::SysNSStringToUTF16(email));
+  if (name) {
+    cell.accessibilityLabel = l10n_util::GetNSStringF(
+        IDS_IOS_OPTIONS_ACCOUNTS_SIGNIN_WITH_NAME_ACCESSIBILITY_LABEL,
+        base::SysNSStringToUTF16(name), base::SysNSStringToUTF16(email));
+  } else {
+    cell.accessibilityLabel = l10n_util::GetNSStringF(
+        IDS_IOS_OPTIONS_ACCOUNTS_SIGNIN_ACCESSIBILITY_LABEL,
+        base::SysNSStringToUTF16(email));
+  }
   cell.userInteractionEnabled = YES;
   cell.accessibilityIdentifier = kAccountMenuSecondaryAccountButtonId;
   // Set the enterprise icon. This may be replaced by the activity indicator
diff --git a/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.mm b/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.mm
index 863c38f..e507db4 100644
--- a/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.mm
+++ b/ios/chrome/browser/authentication/ui_bundled/signin/signin_history_sync/signin_and_history_sync_coordinator.mm
@@ -158,8 +158,6 @@
       _currentStep = [self nextStep];
       break;
     case SigninCoordinatorProfileSwitch:
-      // TODO(crbug.com/375605572): Open the history sync dialog after the
-      // continuation.
     case SigninCoordinatorResultInterrupted:
     case SigninCoordinatorResultCanceledByUser:
       _currentStep = SignInHistorySyncStep::kCompleted;
@@ -199,7 +197,6 @@
 - (void)createAndPresentStepChildCoordinator {
   switch (_currentStep) {
     case SignInHistorySyncStep::kFullscreenSignin: {
-      // TODO(crbug.com/375605572) Sends an actual continuation.
       _signinCoordinator = [[FullscreenSigninCoordinator alloc]
                  initWithBaseViewController:self.baseViewController
                                     browser:self.browser
@@ -232,7 +229,6 @@
       return;
     }
     case SignInHistorySyncStep::kInstantSignin: {
-      // TODO(crbug.com/375605572) Sends an actual continuation.
       _signinCoordinator = [[InstantSigninCoordinator alloc]
           initWithBaseViewController:self.baseViewController
                              browser:self.browser
diff --git a/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_home_mediator.mm b/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_home_mediator.mm
index 37a055d0..dc57217 100644
--- a/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_home_mediator.mm
+++ b/ios/chrome/browser/bookmarks/ui_bundled/home/bookmarks_home_mediator.mm
@@ -348,7 +348,7 @@
   // show the spinner backgound. Otherwise, check if we need to show the empty
   // background.
   if (self.consumer.isDisplayingBookmarkRoot) {
-    if (_bookmarkModel->HasNoUserCreatedBookmarksOrFolders() &&
+    if (!_bookmarkModel->HasUserCreatedBookmarksOrFolders() &&
         _syncedBookmarksObserver->IsPerformingInitialSync()) {
       [self.consumer
           updateTableViewBackgroundStyle:BookmarksHomeBackgroundStyleLoading];
diff --git a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
index eb847884..31d8fb9e 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
+++ b/ios/chrome/browser/browser_view/ui_bundled/browser_coordinator.mm
@@ -167,6 +167,7 @@
 #import "ios/chrome/browser/promos_manager/ui_bundled/promos_manager_coordinator.h"
 #import "ios/chrome/browser/qr_scanner/ui_bundled/qr_scanner_legacy_coordinator.h"
 #import "ios/chrome/browser/reader_mode/coordinator/reader_mode_coordinator.h"
+#import "ios/chrome/browser/reader_mode/model/reader_mode_browser_agent.h"
 #import "ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.h"
 #import "ios/chrome/browser/reading_list/model/reading_list_browser_agent.h"
 #import "ios/chrome/browser/reading_list/ui_bundled/reading_list_coordinator.h"
@@ -2628,22 +2629,6 @@
 #pragma mark - ReaderModeCommands
 
 - (void)showReaderMode {
-  web::WebState* activeWebState = self.activeWebState;
-  if (!activeWebState) {
-    return;
-  }
-  ReaderModeTabHelper* tabHelper =
-      ReaderModeTabHelper::FromWebState(activeWebState);
-  if (!tabHelper) {
-    return;
-  }
-  if (!tabHelper->IsActive()) {
-    // If Reader mode is not active yet in this tab, activate it first. When the
-    // distilled page is ready, -showReaderMode will be called again and the
-    // Reader mode UI can be presented.
-    tabHelper->SetActive(true);
-    return;
-  }
   if (_readerModeCoordinator) {
     // If the Reader mode UI is already presented then there is nothing to do.
     return;
@@ -2655,22 +2640,6 @@
 }
 
 - (void)hideReaderMode {
-  web::WebState* activeWebState = self.activeWebState;
-  if (!activeWebState) {
-    return;
-  }
-  ReaderModeTabHelper* tabHelper =
-      ReaderModeTabHelper::FromWebState(activeWebState);
-  if (!tabHelper) {
-    return;
-  }
-  if (tabHelper->IsActive()) {
-    // If Reader mode is active in this tab, deactivate it first. When it has
-    // been deactivated, -hideReaderMode will be called again and the Reader
-    // mode UI can be dismissed.
-    tabHelper->SetActive(false);
-    return;
-  }
   if (!_readerModeCoordinator) {
     // If the Reader mode UI is already dismissed then there is nothing to do.
     return;
@@ -2679,24 +2648,6 @@
   _readerModeCoordinator = nil;
 }
 
-- (void)toggleReaderMode {
-  web::WebState* activeWebState = self.activeWebState;
-  if (!activeWebState) {
-    return;
-  }
-  ReaderModeTabHelper* tabHelper =
-      ReaderModeTabHelper::FromWebState(activeWebState);
-  if (!tabHelper) {
-    return;
-  }
-  // If Reader mode is active in the current tab, hide it. Otherwise, show it.
-  if (tabHelper->IsActive()) {
-    [self hideReaderMode];
-  } else {
-    [self showReaderMode];
-  }
-}
-
 #pragma mark - FindInPageCommands
 
 - (void)openFindInPage {
@@ -3211,6 +3162,13 @@
             static_cast<id<SnackbarCommands>>(commandDispatcher),
             HandlerForProtocol(commandDispatcher, FeedCommands));
   }
+
+  ReaderModeBrowserAgent* readerModeBrowserAgent =
+      ReaderModeBrowserAgent::FromBrowser(self.browser);
+  if (readerModeBrowserAgent) {
+    readerModeBrowserAgent->SetReaderModeHandler(HandlerForProtocol(
+        self.browser->GetCommandDispatcher(), ReaderModeCommands));
+  }
 }
 
 // Installs delegates for self.profile
@@ -3246,6 +3204,12 @@
   if (FollowBrowserAgent::FromBrowser(self.browser)) {
     FollowBrowserAgent::FromBrowser(self.browser)->ClearUIProviders();
   }
+
+  ReaderModeBrowserAgent* readerModeBrowserAgent =
+      ReaderModeBrowserAgent::FromBrowser(self.browser);
+  if (readerModeBrowserAgent) {
+    readerModeBrowserAgent->SetReaderModeHandler(nil);
+  }
 }
 
 #pragma mark - ParentAccessCommands
diff --git a/ios/chrome/browser/browser_view/ui_bundled/tab_lifecycle_mediator.mm b/ios/chrome/browser/browser_view/ui_bundled/tab_lifecycle_mediator.mm
index 005f79d..5d706ee 100644
--- a/ios/chrome/browser/browser_view/ui_bundled/tab_lifecycle_mediator.mm
+++ b/ios/chrome/browser/browser_view/ui_bundled/tab_lifecycle_mediator.mm
@@ -159,13 +159,6 @@
         HandlerForProtocol(_commandDispatcher, SnackbarCommands));
   }
 
-  if (IsReaderModeAvailable()) {
-    ReaderModeTabHelper* readerModeTabHelper =
-        ReaderModeTabHelper::FromWebState(webState);
-    readerModeTabHelper->SetReaderModeHandler(
-        HandlerForProtocol(_commandDispatcher, ReaderModeCommands));
-  }
-
   DCHECK(_printCoordinator);
   PrintTabHelper::GetOrCreateForWebState(webState)->set_printer(
       _printCoordinator);
@@ -267,12 +260,6 @@
     readerModeTabHelper->SetSnackbarHandler(nil);
   }
 
-  if (IsReaderModeAvailable()) {
-    ReaderModeTabHelper* readerModeTabHelper =
-        ReaderModeTabHelper::FromWebState(webState);
-    readerModeTabHelper->SetReaderModeHandler(nil);
-  }
-
   PrintTabHelper::GetOrCreateForWebState(webState)->set_printer(nil);
 
   RepostFormTabHelper::FromWebState(webState)->SetDelegate(nil);
diff --git a/ios/chrome/browser/discover_feed/model/BUILD.gn b/ios/chrome/browser/discover_feed/model/BUILD.gn
index a7fa9ac4..3fe78ff 100644
--- a/ios/chrome/browser/discover_feed/model/BUILD.gn
+++ b/ios/chrome/browser/discover_feed/model/BUILD.gn
@@ -15,6 +15,9 @@
     "discover_feed_service.mm",
     "discover_feed_view_controller_configuration.h",
     "discover_feed_view_controller_configuration.mm",
+    "discover_feed_visibility_observer.h",
+    "discover_feed_visibility_provider_configuration.h",
+    "discover_feed_visibility_provider_configuration.mm",
     "feed_model_configuration.h",
     "feed_model_configuration.mm",
   ]
@@ -110,3 +113,22 @@
 source_set("discover_feed_refresher") {
   sources = [ "discover_feed_refresher.h" ]
 }
+
+source_set("discover_feed_visibility_browser_agent") {
+  sources = [
+    "discover_feed_visibility_browser_agent.h",
+    "discover_feed_visibility_browser_agent.mm",
+  ]
+  deps = [
+    ":discover_feed_factory",
+    "//base",
+    "//ios/chrome/app/application_delegate:app_state",
+    "//ios/chrome/app/profile",
+    "//ios/chrome/browser/regional_capabilities/model",
+    "//ios/chrome/browser/search_engines/model:template_url_service_factory",
+    "//ios/chrome/browser/shared/coordinator/scene:scene_state_header",
+    "//ios/chrome/browser/shared/model/browser",
+    "//ios/chrome/browser/shared/model/profile",
+    "//ios/public/provider/chrome/browser/discover_feed:discover_feed_api",
+  ]
+}
diff --git a/ios/chrome/browser/discover_feed/model/DEPS b/ios/chrome/browser/discover_feed/model/DEPS
index 9232173be..1d8c4cb 100644
--- a/ios/chrome/browser/discover_feed/model/DEPS
+++ b/ios/chrome/browser/discover_feed/model/DEPS
@@ -8,4 +8,5 @@
   "+ios/chrome/browser/start_surface/ui_bundled",
   "+ios/chrome/browser/sync/model",
   "+ios/chrome/browser/feature_engagement/model",
+  "+ios/chrome/browser/regional_capabilities/model",
 ]
diff --git a/ios/chrome/browser/discover_feed/model/discover_feed_service.h b/ios/chrome/browser/discover_feed/model/discover_feed_service.h
index 9c1cba0..cad54ee 100644
--- a/ios/chrome/browser/discover_feed/model/discover_feed_service.h
+++ b/ios/chrome/browser/discover_feed/model/discover_feed_service.h
@@ -59,12 +59,10 @@
       UIViewController* feed_view_controller) = 0;
 
   // Informs the service that the Discover content visibility state has changed.
-  // TODO(crbug.com/406544789): Remove implementation and make this strictly
-  // virtual.
   virtual void UpdateFeedViewVisibilityState(
       UICollectionView* collection_view,
       BrowserViewVisibilityState current_state,
-      BrowserViewVisibilityState previous_state);
+      BrowserViewVisibilityState previous_state) = 0;
 
   // Updates the feed's theme to match the user's theme (light/dark).
   virtual void UpdateTheme() = 0;
diff --git a/ios/chrome/browser/discover_feed/model/discover_feed_service.mm b/ios/chrome/browser/discover_feed/model/discover_feed_service.mm
index 642d22d9..3c54bea8 100644
--- a/ios/chrome/browser/discover_feed/model/discover_feed_service.mm
+++ b/ios/chrome/browser/discover_feed/model/discover_feed_service.mm
@@ -28,9 +28,3 @@
 }
 
 void DiscoverFeedService::BrowsingHistoryCleared() {}
-
-// TODO(crbug.com/406544789): Remove.
-void DiscoverFeedService::UpdateFeedViewVisibilityState(
-    UICollectionView* collection_view,
-    BrowserViewVisibilityState current_state,
-    BrowserViewVisibilityState previous_state) {}
diff --git a/ios/chrome/browser/discover_feed/model/discover_feed_visibility_browser_agent.h b/ios/chrome/browser/discover_feed/model/discover_feed_visibility_browser_agent.h
new file mode 100644
index 0000000..84d4742
--- /dev/null
+++ b/ios/chrome/browser/discover_feed/model/discover_feed_visibility_browser_agent.h
@@ -0,0 +1,53 @@
+// 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_DISCOVER_FEED_MODEL_DISCOVER_FEED_VISIBILITY_BROWSER_AGENT_H_
+#define IOS_CHROME_BROWSER_DISCOVER_FEED_MODEL_DISCOVER_FEED_VISIBILITY_BROWSER_AGENT_H_
+
+#import "base/memory/raw_ptr.h"
+#import "ios/chrome/browser/shared/model/browser/browser_user_data.h"
+
+class Browser;
+enum class DiscoverFeedEligibility;
+@protocol DiscoverFeedVisibilityObserver;
+@protocol DiscoverFeedVisibilityProvider;
+
+/// Browser agent that handles the of discover feed visibility.
+class DiscoverFeedVisibilityBrowserAgent
+    : public BrowserUserData<DiscoverFeedVisibilityBrowserAgent> {
+ public:
+  /// Not copyable or moveable
+  DiscoverFeedVisibilityBrowserAgent(
+      const DiscoverFeedVisibilityBrowserAgent&) = delete;
+  DiscoverFeedVisibilityBrowserAgent& operator=(
+      const DiscoverFeedVisibilityBrowserAgent&) = delete;
+  ~DiscoverFeedVisibilityBrowserAgent() override;
+
+  /// Enabled state accessors.
+  bool IsEnabled();
+  void SetEnabled(bool enabled);
+
+  /// Eligibility getter.
+  DiscoverFeedEligibility GetEligibility();
+
+  /// Convenience method that returns whether the feed should be visible.
+  bool ShouldBeVisible();
+
+  /// Adds or removes `observer` to/from a list of observers that gets notified
+  /// of Discover feed eligibility or visibility changes.
+  void AddObserver(id<DiscoverFeedVisibilityObserver> observer);
+  void RemoveObserver(id<DiscoverFeedVisibilityObserver> observer);
+
+ private:
+  friend class BrowserUserData<DiscoverFeedVisibilityBrowserAgent>;
+  explicit DiscoverFeedVisibilityBrowserAgent(Browser* browser);
+
+  /// The object that handles visibility updates. Lazy loaded.
+  id<DiscoverFeedVisibilityProvider> visibility_provider_;
+  /// Method to create and/or retrieve the visibility provider for lazy loading
+  /// purpose.
+  id<DiscoverFeedVisibilityProvider> GetVisibilityProvider();
+};
+
+#endif  // IOS_CHROME_BROWSER_DISCOVER_FEED_MODEL_DISCOVER_FEED_VISIBILITY_BROWSER_AGENT_H_
diff --git a/ios/chrome/browser/discover_feed/model/discover_feed_visibility_browser_agent.mm b/ios/chrome/browser/discover_feed/model/discover_feed_visibility_browser_agent.mm
new file mode 100644
index 0000000..60ce8fba
--- /dev/null
+++ b/ios/chrome/browser/discover_feed/model/discover_feed_visibility_browser_agent.mm
@@ -0,0 +1,78 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/discover_feed/model/discover_feed_visibility_browser_agent.h"
+
+#import "ios/chrome/app/application_delegate/app_state.h"
+#import "ios/chrome/app/profile/profile_state.h"
+#import "ios/chrome/browser/discover_feed/model/discover_feed_service_factory.h"
+#import "ios/chrome/browser/discover_feed/model/discover_feed_visibility_provider_configuration.h"
+#import "ios/chrome/browser/regional_capabilities/model/regional_capabilities_service_factory.h"
+#import "ios/chrome/browser/search_engines/model/template_url_service_factory.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/profile/profile_ios.h"
+#import "ios/public/provider/chrome/browser/discover_feed/discover_feed_api.h"
+#import "ios/public/provider/chrome/browser/discover_feed/discover_feed_visibility_provider.h"
+
+DiscoverFeedVisibilityBrowserAgent::DiscoverFeedVisibilityBrowserAgent(
+    Browser* browser)
+    : BrowserUserData(browser) {}
+
+DiscoverFeedVisibilityBrowserAgent::~DiscoverFeedVisibilityBrowserAgent() {
+  [visibility_provider_ shutdown];
+  visibility_provider_ = nil;
+}
+
+#pragma mark - Public
+
+bool DiscoverFeedVisibilityBrowserAgent::IsEnabled() {
+  return GetVisibilityProvider().isEnabled;
+}
+
+void DiscoverFeedVisibilityBrowserAgent::SetEnabled(bool enabled) {
+  GetVisibilityProvider().enabled = enabled;
+}
+
+DiscoverFeedEligibility DiscoverFeedVisibilityBrowserAgent::GetEligibility() {
+  return [GetVisibilityProvider() eligibility];
+}
+
+bool DiscoverFeedVisibilityBrowserAgent::ShouldBeVisible() {
+  return GetEligibility() == DiscoverFeedEligibility::kEligible && IsEnabled();
+}
+
+void DiscoverFeedVisibilityBrowserAgent::AddObserver(
+    id<DiscoverFeedVisibilityObserver> observer) {
+  [GetVisibilityProvider() addObserver:observer];
+}
+
+void DiscoverFeedVisibilityBrowserAgent::RemoveObserver(
+    id<DiscoverFeedVisibilityObserver> observer) {
+  [GetVisibilityProvider() removeObserver:observer];
+}
+
+#pragma mark - Private
+
+id<DiscoverFeedVisibilityProvider>
+DiscoverFeedVisibilityBrowserAgent::GetVisibilityProvider() {
+  if (!visibility_provider_) {
+    DiscoverFeedVisibilityProviderConfiguration* configuration =
+        [[DiscoverFeedVisibilityProviderConfiguration alloc] init];
+    ProfileIOS* profile = browser_->GetProfile();
+    configuration.prefService = profile->GetPrefs();
+    configuration.discoverFeedService =
+        DiscoverFeedServiceFactory::GetForProfile(profile);
+    configuration.templateURLService =
+        ios::TemplateURLServiceFactory::GetForProfile(profile);
+    configuration.regionalCapabilitiesService =
+        ios::RegionalCapabilitiesServiceFactory::GetForProfile(profile);
+    configuration.resumedFromSafeMode =
+        [browser_->GetSceneState().profileState.appState resumingFromSafeMode];
+    // TODO(crbug.com/412691611): Uncomment.
+    /*visibility_provider_ =
+        ios::provider::CreateDiscoverFeedVisibilityProvider(configuration);*/
+  }
+  return visibility_provider_;
+}
diff --git a/ios/chrome/browser/discover_feed/model/discover_feed_visibility_observer.h b/ios/chrome/browser/discover_feed/model/discover_feed_visibility_observer.h
new file mode 100644
index 0000000..5009765
--- /dev/null
+++ b/ios/chrome/browser/discover_feed/model/discover_feed_visibility_observer.h
@@ -0,0 +1,25 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_DISCOVER_FEED_MODEL_DISCOVER_FEED_VISIBILITY_OBSERVER_H_
+#define IOS_CHROME_BROWSER_DISCOVER_FEED_MODEL_DISCOVER_FEED_VISIBILITY_OBSERVER_H_
+
+@class DiscoverFeedVisibilityProvider;
+
+/// Protocol declaration for objects that need to know about Discover feed
+/// visibility changes.
+@protocol DiscoverFeedVisibilityObserver
+
+@optional
+
+/// Notifies observer that the feed eligibility has changed. Observer should
+/// display or hide the feed preference toggles accordingly.
+- (void)didChangeDiscoverFeedEligibility;
+
+/// Notifies observer that the feed visibility should be updated.
+- (void)didChangeDiscoverFeedVisibility;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_DISCOVER_FEED_MODEL_DISCOVER_FEED_VISIBILITY_OBSERVER_H_
diff --git a/ios/chrome/browser/discover_feed/model/discover_feed_visibility_provider_configuration.h b/ios/chrome/browser/discover_feed/model/discover_feed_visibility_provider_configuration.h
new file mode 100644
index 0000000..4093f02
--- /dev/null
+++ b/ios/chrome/browser/discover_feed/model/discover_feed_visibility_provider_configuration.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_DISCOVER_FEED_MODEL_DISCOVER_FEED_VISIBILITY_PROVIDER_CONFIGURATION_H_
+#define IOS_CHROME_BROWSER_DISCOVER_FEED_MODEL_DISCOVER_FEED_VISIBILITY_PROVIDER_CONFIGURATION_H_
+
+#import <Foundation/Foundation.h>
+
+class DiscoverFeedService;
+class PrefService;
+class TemplateURLService;
+
+namespace regional_capabilities {
+class RegionalCapabilitiesService;
+}
+
+/// Configuration object for the Discover visibility provider.
+@interface DiscoverFeedVisibilityProviderConfiguration : NSObject
+
+/// The service to get the discover feed.
+@property(nonatomic, assign) DiscoverFeedService* discoverFeedService;
+/// The per Profile PrefService.
+@property(nonatomic, assign) PrefService* prefService;
+/// The service to get the default search engine.
+@property(nonatomic, assign) TemplateURLService* templateURLService;
+/// The service to check location.
+@property(nonatomic, assign) regional_capabilities::RegionalCapabilitiesService*
+    regionalCapabilitiesService;
+/// `YES` if the app is resumed from safe mode.
+@property(nonatomic, assign) BOOL resumedFromSafeMode;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_DISCOVER_FEED_MODEL_DISCOVER_FEED_VISIBILITY_PROVIDER_CONFIGURATION_H_
diff --git a/ios/chrome/browser/discover_feed/model/discover_feed_visibility_provider_configuration.mm b/ios/chrome/browser/discover_feed/model/discover_feed_visibility_provider_configuration.mm
new file mode 100644
index 0000000..0b320fa
--- /dev/null
+++ b/ios/chrome/browser/discover_feed/model/discover_feed_visibility_provider_configuration.mm
@@ -0,0 +1,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.
+
+#import "ios/chrome/browser/discover_feed/model/discover_feed_visibility_provider_configuration.h"
+
+@implementation DiscoverFeedVisibilityProviderConfiguration
+@end
diff --git a/ios/chrome/browser/discover_feed/model/feed_constants.h b/ios/chrome/browser/discover_feed/model/feed_constants.h
index 289378f7..c790e81 100644
--- a/ios/chrome/browser/discover_feed/model/feed_constants.h
+++ b/ios/chrome/browser/discover_feed/model/feed_constants.h
@@ -55,6 +55,18 @@
   FollowingFeedSortTypeByLatest
 };
 
+/// Enums representing whether the user is eligible to view the feed, and if
+/// not, why.
+enum class DiscoverFeedEligibility {
+  /// User is eligible for the Discover feed.
+  kEligible,
+  /// Feed is ineligible for an unknown reason.
+  kIneligibleReasonUnknown,
+  /// Feed is ineligible because a managed account is being used, and feed is
+  /// not allowed by enterprise settings.
+  kDisabledByEnterprisePolicy,
+};
+
 // The user defaults key indicating if the user has ever engaged with a feed.
 extern NSString* const kEngagedWithFeedKey;
 
diff --git a/ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent.mm b/ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent.mm
index 233979e..8f89e7d2 100644
--- a/ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent.mm
+++ b/ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent.mm
@@ -238,6 +238,7 @@
     }
   } else if (level >= SceneActivationLevelForegroundInactive) {
     [self updateWindowHasIncognitoContent:sceneState];
+    [self handleExternalStartupIntents:sceneState];
     [self updateBackgroundedForEnoughTimeOnForeground];
     // Close media presentations when the app is foregrounded rather than
     // backgrounded to avoid freezes.
@@ -254,8 +255,8 @@
   [self logEnabledHistogramOnce];
   if (IsIOSSoftLockEnabled()) {
     [self setUpPrefObservers];
+    [self notifyObservers];
     [self maybeEnterTabGridWithSceneState:sceneState];
-
     [self logIncognitoLockStateHistogramOnce];
     [self recordIncognitoLockImpressionForSceneState:sceneState];
   }
@@ -282,6 +283,19 @@
 
 #pragma mark - private
 
+// Marks the lock screen as authenticated, if reauth is not enabled and Chrome
+// was started via external intents.
+- (void)handleExternalStartupIntents:(SceneState*)sceneState {
+  if (!IsIOSSoftLockEnabled()) {
+    return;
+  }
+
+  if (self.incognitoLockState != IncognitoLockState::kReauth &&
+      sceneState.startupHadExternalIntent) {
+    self.authenticatedSinceLastForeground = YES;
+  }
+}
+
 // Switch from tab to tab switcher, if the current surface is a locked Incognito
 // tab.
 - (void)maybeEnterTabGridWithSceneState:(SceneState*)sceneState {
diff --git a/ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent_unittest.mm b/ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent_unittest.mm
index 4739ef40..70449be 100644
--- a/ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent_unittest.mm
+++ b/ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent_unittest.mm
@@ -593,4 +593,32 @@
   EXPECT_OCMOCK_VERIFY(application_commands_handler_);
 }
 
+// Test that soft lock is not required when Chrome was launched via an external
+// intent.
+TEST_F(IncognitoReauthSceneAgentTest, NoSoftLockOnExternalIntents) {
+  SetUpTestObjects(/*tab_count=*/1, /*reauth_enabled=*/false,
+                   /*soft_lock_feature_enabled=*/true,
+                   /*soft_lock_pref_enabled=*/true);
+  scene_state_.startupHadExternalIntent = YES;
+
+  // Advance the clock and foreground the app.
+  AdvanceClock(kIOSSoftLockBackgroundThreshold.Get());
+  scene_state_.activationLevel = SceneActivationLevelForegroundActive;
+
+  EXPECT_EQ(agent_.incognitoLockState, IncognitoLockState::kNone);
+}
+
+// Test that reauth is required when Chrome was launched via an external intent.
+TEST_F(IncognitoReauthSceneAgentTest, ReauthOnExternalIntents) {
+  SetUpTestObjects(/*tab_count=*/1, /*reauth_enabled=*/true,
+                   /*soft_lock_feature_enabled=*/true,
+                   /*soft_lock_pref_enabled=*/false);
+  scene_state_.startupHadExternalIntent = YES;
+
+  // Go foreground.
+  scene_state_.activationLevel = SceneActivationLevelForegroundActive;
+
+  EXPECT_EQ(agent_.incognitoLockState, IncognitoLockState::kReauth);
+}
+
 }  // namespace
diff --git a/ios/chrome/browser/intents/model/user_activity_browser_agent.mm b/ios/chrome/browser/intents/model/user_activity_browser_agent.mm
index 7b62b27..e672acc 100644
--- a/ios/chrome/browser/intents/model/user_activity_browser_agent.mm
+++ b/ios/chrome/browser/intents/model/user_activity_browser_agent.mm
@@ -843,13 +843,15 @@
             LensEntrypoint::ContextMenu,
             search_engines::SupportsSearchImageWithLens(template_url_service));
     if (!useLens) {
-      NSData* image_data =
-          connection_information_.startupParameters.imageSearchData;
-      web::NavigationManager::WebLoadParams web_load_params =
-          ImageSearchParamGenerator::LoadParamsForImageData(
-              image_data, GURL(), template_url_service);
+      if (search_engines::SupportsSearchByImage(template_url_service)) {
+        NSData* image_data =
+            connection_information_.startupParameters.imageSearchData;
+        web::NavigationManager::WebLoadParams web_load_params =
+            ImageSearchParamGenerator::LoadParamsForImageData(
+                image_data, GURL(), template_url_service);
 
-      params.web_params = web_load_params;
+        params.web_params = web_load_params;
+      }
     } else {
       connection_information_.startupParameters.postOpeningAction =
           START_LENS_FROM_SHARE_EXTENSION;
diff --git a/ios/chrome/browser/lens_overlay/model/lens_overlay_detents_manager.mm b/ios/chrome/browser/lens_overlay/model/lens_overlay_detents_manager.mm
index d7df9019..995a4c4 100644
--- a/ios/chrome/browser/lens_overlay/model/lens_overlay_detents_manager.mm
+++ b/ios/chrome/browser/lens_overlay/model/lens_overlay_detents_manager.mm
@@ -240,7 +240,6 @@
       break;
     case SheetDetentStateConsentDialog:
       _sheet.detents = @[ [self consentDetent] ];
-      _sheet.largestUndimmedDetentIdentifier = kConsentSheetDetentIdentifier;
       _sheet.selectedDetentIdentifier = kConsentSheetDetentIdentifier;
       break;
     case SheetDetentStateInfoMessage:
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_consent_presenter.mm b/ios/chrome/browser/lens_overlay/ui/lens_overlay_consent_presenter.mm
index 9bcdbe5..adc94040 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_consent_presenter.mm
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_consent_presenter.mm
@@ -81,7 +81,7 @@
 - (BOOL)lensOverlayDetentsManagerShouldDismissBottomSheet:
     (LensOverlayDetentsManager*)detentsManager {
   DCHECK(detentsManager.sheetDimension == SheetDimensionState::kConsent);
-  return YES;
+  return NO;
 }
 
 @end
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_consent_view_controller.mm b/ios/chrome/browser/lens_overlay/ui/lens_overlay_consent_view_controller.mm
index 0537a2f..53da509 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_consent_view_controller.mm
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_consent_view_controller.mm
@@ -43,6 +43,8 @@
 // The height of the invariant items of the dialog
 // (e.g. bottom action buttons, the padding).
 const CGFloat kDialogFixedItemsHeight = 160;
+// The width of the dialog in regular display size.
+const CGFloat kDialogWidthInRegularDisplaySize = 540;
 
 // Whether to use the updated onboarding string.
 bool UseUpdatedStrings() {
@@ -157,9 +159,11 @@
 
 - (CGSize)preferredContentSize {
   [_contentStack layoutIfNeeded];
-  CGFloat fittingWidth = self.view.safeAreaLayoutGuide.layoutFrame.size.width;
   CGFloat presentedContentHeight = _contentStack.frame.size.height;
-  return CGSizeMake(fittingWidth,
+
+  // Only regular width is relevant, as the bottom sheet prresentation is
+  // edge-attached in compact width.
+  return CGSizeMake(kDialogWidthInRegularDisplaySize,
                     presentedContentHeight + kDialogFixedItemsHeight);
 }
 
@@ -200,11 +204,9 @@
   UIImageView* imageView = [[UIImageView alloc]
       initWithImage:[UIImage imageNamed:kLensOverlayOnboardingImageName]];
   imageView.translatesAutoresizingMaskIntoConstraints = NO;
-  [NSLayoutConstraint activateConstraints:@[
-    [imageView.widthAnchor
-        constraintEqualToConstant:kLensOverlayOnboardingIllustrationSize],
-    [imageView.heightAnchor constraintEqualToAnchor:imageView.widthAnchor],
-  ]];
+  AddSizeConstraints(imageView,
+                     CGSizeMake(kLensOverlayOnboardingIllustrationSize,
+                                kLensOverlayOnboardingIllustrationSize));
 
   return imageView;
 }
diff --git a/ios/chrome/browser/lens_overlay/ui/lens_overlay_entrypoint_view.mm b/ios/chrome/browser/lens_overlay/ui/lens_overlay_entrypoint_view.mm
index e9824b23..e4fb25c 100644
--- a/ios/chrome/browser/lens_overlay/ui/lens_overlay_entrypoint_view.mm
+++ b/ios/chrome/browser/lens_overlay/ui/lens_overlay_entrypoint_view.mm
@@ -94,6 +94,7 @@
         [UIColor colorNamed:kGrey300Color];
     [self insertSubview:_visibilityIndicatorView belowSubview:self.imageView];
     _visibilityIndicatorView.layer.cornerRadius = kVisibilityIndicatorSize / 2;
+    _visibilityIndicatorView.userInteractionEnabled = NO;
     AddSameCenterConstraints(self, _visibilityIndicatorView);
     AddSizeConstraints(
         _visibilityIndicatorView,
diff --git a/ios/chrome/browser/main/model/BUILD.gn b/ios/chrome/browser/main/model/BUILD.gn
index a966c223..ddd08dad 100644
--- a/ios/chrome/browser/main/model/BUILD.gn
+++ b/ios/chrome/browser/main/model/BUILD.gn
@@ -25,6 +25,7 @@
     "//ios/chrome/browser/crash_report/model/breadcrumbs",
     "//ios/chrome/browser/credential_provider/model:buildflags",
     "//ios/chrome/browser/device_sharing/model",
+    "//ios/chrome/browser/discover_feed/model:discover_feed_visibility_browser_agent",
     "//ios/chrome/browser/favicon/model",
     "//ios/chrome/browser/follow/model:browser_agent",
     "//ios/chrome/browser/fullscreen/ui_bundled",
@@ -36,6 +37,8 @@
     "//ios/chrome/browser/omnibox/model/omnibox_position",
     "//ios/chrome/browser/policy/model",
     "//ios/chrome/browser/policy/model:browser_agent",
+    "//ios/chrome/browser/reader_mode/model",
+    "//ios/chrome/browser/reader_mode/model:features",
     "//ios/chrome/browser/reading_list/model",
     "//ios/chrome/browser/saved_tab_groups/model:tab_group_service",
     "//ios/chrome/browser/send_tab_to_self/model",
diff --git a/ios/chrome/browser/main/model/DEPS b/ios/chrome/browser/main/model/DEPS
index b067119..063a3fd 100644
--- a/ios/chrome/browser/main/model/DEPS
+++ b/ios/chrome/browser/main/model/DEPS
@@ -15,8 +15,10 @@
     "+ios/chrome/browser/crash_report/model/breadcrumbs/breadcrumb_manager_browser_agent.h",
     "+ios/chrome/browser/credential_provider/model/credential_provider_browser_agent.h",
     "+ios/chrome/browser/credential_provider/model/credential_provider_buildflags.h",
+    "+ios/chrome/browser/discover_feed/model/discover_feed_visibility_browser_agent.h",
     "+ios/chrome/browser/device_sharing/model/device_sharing_browser_agent.h",
     "+ios/chrome/browser/follow/model/follow_browser_agent.h",
+    "+ios/chrome/browser/reader_mode/model",
     "+ios/chrome/browser/fullscreen/ui_bundled/fullscreen_controller.h",
     "+ios/chrome/browser/infobars/model/overlays/browser_agent/infobar_overlay_browser_agent_util.h",
     "+ios/chrome/browser/intents/model/user_activity_browser_agent.h",
diff --git a/ios/chrome/browser/main/model/browser_agent_util.mm b/ios/chrome/browser/main/model/browser_agent_util.mm
index b5520e0..40ff0cc 100644
--- a/ios/chrome/browser/main/model/browser_agent_util.mm
+++ b/ios/chrome/browser/main/model/browser_agent_util.mm
@@ -12,6 +12,7 @@
 #import "ios/chrome/browser/crash_report/model/breadcrumbs/breadcrumb_manager_browser_agent.h"
 #import "ios/chrome/browser/credential_provider/model/credential_provider_buildflags.h"
 #import "ios/chrome/browser/device_sharing/model/device_sharing_browser_agent.h"
+#import "ios/chrome/browser/discover_feed/model/discover_feed_visibility_browser_agent.h"
 #import "ios/chrome/browser/favicon/model/favicon_browser_agent.h"
 #import "ios/chrome/browser/follow/model/follow_browser_agent.h"
 #import "ios/chrome/browser/fullscreen/ui_bundled/fullscreen_controller.h"
@@ -22,11 +23,15 @@
 #import "ios/chrome/browser/metrics/model/web_state_list_metrics_browser_agent.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_position/omnibox_position_browser_agent.h"
 #import "ios/chrome/browser/policy/model/policy_watcher_browser_agent.h"
+#import "ios/chrome/browser/reader_mode/model/features.h"
+#import "ios/chrome/browser/reader_mode/model/reader_mode_browser_agent.h"
 #import "ios/chrome/browser/reading_list/model/reading_list_browser_agent.h"
 #import "ios/chrome/browser/send_tab_to_self/model/send_tab_to_self_browser_agent.h"
 #import "ios/chrome/browser/sessions/model/live_tab_context_browser_agent.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/shared/public/commands/command_dispatcher.h"
+#import "ios/chrome/browser/shared/public/commands/reader_mode_commands.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/snapshots/model/snapshot_browser_agent.h"
 #import "ios/chrome/browser/start_surface/ui_bundled/start_surface_recent_tab_browser_agent.h"
@@ -113,6 +118,11 @@
   WebStateListMetricsBrowserAgent::CreateForBrowser(
       browser, SessionMetrics::FromProfile(browser->GetProfile()));
 
+  if (IsReaderModeAvailable()) {
+    ReaderModeBrowserAgent::CreateForBrowser(browser,
+                                             browser->GetWebStateList());
+  }
+
   // Normal profiles are the only ones to get tab usage recorder.
   if (!browser_is_off_record) {
     TabUsageRecorderBrowserAgent::CreateForBrowser(browser);
@@ -139,6 +149,10 @@
     TabBasedIPHBrowserAgent::CreateForBrowser(browser);
   }
 
+  if (!browser_is_off_record && !browser_is_inactive) {
+    DiscoverFeedVisibilityBrowserAgent::CreateForBrowser(browser);
+  }
+
 #if BUILDFLAG(IOS_CREDENTIAL_PROVIDER_ENABLED)
   CredentialProviderBrowserAgent::CreateForBrowser(browser);
 #endif
diff --git a/ios/chrome/browser/metrics/model/demographics_egtest.mm b/ios/chrome/browser/metrics/model/demographics_egtest.mm
index 2498569..0249d476 100644
--- a/ios/chrome/browser/metrics/model/demographics_egtest.mm
+++ b/ios/chrome/browser/metrics/model/demographics_egtest.mm
@@ -237,17 +237,11 @@
 
   // Expect 2 counts because in the iOS First Run, the MetricsService is started
   // quicker, which causes two metrics log uploads to happen by this point.
-  ConditionBlock condition = ^{
-    NSError* error = [MetricsAppInterface
-        expectUniqueSampleWithCount:2
-                          forBucket:success
-                       forHistogram:@"UMA.UserDemographics.Status"];
-    return error == nil;
-  };
-
-  GREYAssert(base::test::ios::WaitUntilConditionOrTimeout(
-                 base::test::ios::kWaitForActionTimeout, condition),
-             @"Unexpected histogram contents");
+  NSError* error = [MetricsAppInterface
+      expectUniqueSampleWithCount:2
+                        forBucket:success
+                     forHistogram:@"UMA.UserDemographics.Status"];
+  chrome_test_util::GREYAssertErrorNil(error);
 }
 // LINT.ThenChange(/chrome/browser/metrics/metrics_service_user_demographics_browsertest.cc:AddSyncedUserBirthYearAndGenderToProtoData)
 
diff --git a/ios/chrome/browser/omnibox/model/BUILD.gn b/ios/chrome/browser/omnibox/model/BUILD.gn
index 31c10be..fe3348e 100644
--- a/ios/chrome/browser/omnibox/model/BUILD.gn
+++ b/ios/chrome/browser/omnibox/model/BUILD.gn
@@ -48,8 +48,6 @@
     "omnibox_image_fetcher.mm",
     "omnibox_pedal_annotator.h",
     "omnibox_pedal_annotator.mm",
-    "omnibox_popup_view_base.h",
-    "omnibox_popup_view_base.mm",
     "omnibox_popup_view_ios.h",
     "omnibox_popup_view_ios.mm",
     "omnibox_text_controller.h",
@@ -161,8 +159,8 @@
   sources = [
     "test_omnibox_edit_model_ios.h",
     "test_omnibox_edit_model_ios.mm",
-    "test_omnibox_popup_view_base.h",
-    "test_omnibox_popup_view_base.mm",
+    "test_omnibox_popup_view_ios.h",
+    "test_omnibox_popup_view_ios.mm",
     "test_omnibox_view_base.h",
     "test_omnibox_view_base.mm",
   ]
diff --git a/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.h b/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.h
index c45e2a50..66038430 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.h
+++ b/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.h
@@ -17,7 +17,6 @@
 #import "base/memory/raw_ptr.h"
 #import "base/memory/weak_ptr.h"
 #import "base/time/time.h"
-#import "build/build_config.h"
 #import "components/omnibox/browser/autocomplete_controller.h"
 #import "components/omnibox/browser/autocomplete_input.h"
 #import "components/omnibox/browser/autocomplete_match.h"
@@ -26,16 +25,11 @@
 #import "components/omnibox/common/omnibox_focus_state.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_view_base.h"
 #import "third_party/metrics_proto/omnibox_event.pb.h"
-#import "third_party/skia/include/core/SkColor.h"
 #import "ui/base/window_open_disposition.h"
-#import "ui/gfx/native_widget_types.h"
 #import "url/gurl.h"
 
 class OmniboxControllerIOS;
-class OmniboxPopupViewBase;
-namespace gfx {
-class Image;
-}
+class OmniboxPopupViewIOS;
 
 class OmniboxEditModelIOS {
  public:
@@ -44,9 +38,9 @@
   OmniboxEditModelIOS(const OmniboxEditModelIOS&) = delete;
   OmniboxEditModelIOS& operator=(const OmniboxEditModelIOS&) = delete;
 
-  void set_popup_view(OmniboxPopupViewBase* popup_view);
-  OmniboxPopupViewBase* get_popup_view() { return popup_view_; }
-  const OmniboxPopupViewBase* get_popup_view() const { return popup_view_; }
+  void set_popup_view(OmniboxPopupViewIOS* popup_view);
+  OmniboxPopupViewIOS* get_popup_view() { return popup_view_; }
+  const OmniboxPopupViewIOS* get_popup_view() const { return popup_view_; }
 
   metrics::OmniboxEventProto::PageClassification GetPageClassification() const;
 
@@ -56,12 +50,6 @@
   // URL. Virtual for testing.
   virtual AutocompleteMatch CurrentMatch(GURL* alternate_nav_url) const;
 
-  // Called when the user wants to export the entire current text as a URL.
-  // Sets the url, and if known, the title and favicon.
-  void GetDataForURLExport(GURL* url,
-                           std::u16string* title,
-                           gfx::Image* favicon);
-
   // Returns true if the current edit contents will be treated as a
   // URL/navigation, as opposed to a search.
   bool CurrentTextIsURL() const;
@@ -129,20 +117,6 @@
   void StartAutocomplete(bool has_selected_text,
                          bool prevent_inline_autocomplete);
 
-  // Determines whether the user can "paste and go", given the specified text.
-  bool CanPasteAndGo(const std::u16string& text) const;
-
-  // Navigates to the destination last supplied to CanPasteAndGo.
-  void PasteAndGo(
-      const std::u16string& text,
-      base::TimeTicks match_selection_timestamp = base::TimeTicks());
-
-  // Sets `match` and `alternate_nav_url` based on classifying `text`.
-  // `alternate_nav_url` may be nullptr.
-  void ClassifyString(const std::u16string& text,
-                      AutocompleteMatch* match,
-                      GURL* alternate_nav_url) const;
-
   // Opens given selection. Most kinds of selection invoke an action or
   // otherwise call `OpenMatch`, but some may `AcceptInput` which is not
   // guaranteed to open a match or commit the omnibox.
@@ -162,11 +136,6 @@
 
   base::TimeTicks last_omnibox_focus() const { return last_omnibox_focus_; }
 
-  // This is the same as when the Omnibox is visibly focused.
-  bool is_caret_visible() const {
-    return focus_state_ == OMNIBOX_FOCUS_VISIBLE;
-  }
-
   // Clears additional text.
   void ClearAdditionalText();
 
@@ -179,18 +148,6 @@
   // done.
   void StartZeroSuggestRequest(bool user_clobbered_permanent_text = false);
 
-  // Sets the visibility of the caret in the omnibox, if it has focus. The
-  // visibility of the caret is reset to visible if either
-  //   - The user starts typing, or
-  //   - We explicitly focus the omnibox again.
-  // The latter case must be handled in three separate places--OnSetFocus(),
-  // OmniboxViewBase::SetFocus(), and the mouse handlers in OmniboxViewBase. See
-  // accompanying comments for why each of these is necessary.
-  //
-  // Caret visibility is tracked per-tab and updates automatically upon
-  // switching tabs.
-  void SetCaretVisibility(bool visible);
-
   // Sent before `OnKillFocus` and before the popup is closed.
   void OnWillKillFocus();
 
@@ -235,22 +192,6 @@
   // Name of the histogram tracking cut or copy omnibox commands.
   static const char kCutOrCopyAllTextHistogram[];
 
-  // Just forwards the call to the OmniboxViewBase referred within.
-  void SetAccessibilityLabel(const AutocompleteMatch& match);
-
-  // Returns true if the destination URL of the match is bookmarked.
-  bool IsStarredMatch(const AutocompleteMatch& match) const;
-
-  // Gets the suggestion group header text associated with the given suggestion
-  // group ID.
-  // In addition to calling `AutocompleteResult::GetHeaderForSuggestionGroup()`,
-  // this function takes into account certain header visibility criteria (e.g.
-  // experiment flags) to determine the proper header text, which will then be
-  // used by the relevant code to conditionally show suggestion group headers
-  // in the Omnibox/Realbox popup.
-  std::u16string GetSuggestionGroupHeaderText(
-      const std::optional<omnibox::GroupId>& suggestion_group_id) const;
-
   // Returns true if the popup exists and is open. Virtual for testing.
   virtual bool PopupIsOpen() const;
 
@@ -298,10 +239,6 @@
 
   AutocompleteController* autocomplete_controller() const;
 
-  // If no query is in progress, starts working on an autocomplete query.
-  // Returns true if started; false otherwise.
-  bool MaybeStartQueryForPopup();
-
   // Asks the browser to load the popup's currently selected item, using the
   // supplied disposition.  This may close the popup.
   void AcceptInput(
@@ -352,17 +289,13 @@
 
   // If focus_state_ does not match `state`, we update it and notify the
   // InstantController about the change (passing along the `reason` for the
-  // change). If the caret visibility changes, we call ApplyCaretVisibility() on
-  // the view.
+  // change).
   void SetFocusState(OmniboxFocusState state, OmniboxFocusChangeReason reason);
 
   // Returns view text if there is a view. Until the model is made the
   // primary data source, this should not be called when there's no view.
   std::u16string GetText() const;
 
-  // Closes lens if still needed.
-  void MaybeCloseLens();
-
   // Owns this.
   raw_ptr<OmniboxControllerIOS> controller_;
 
@@ -406,23 +339,6 @@
   // This should be valid whenever user_input_in_progress_ is true.
   base::TimeTicks time_user_first_modified_omnibox_;
 
-  // When the user closes the popup, we need to remember the URL for their
-  // desired choice, so that if they hit enter without reopening the popup we
-  // know where to go.  We could simply rerun autocomplete in this case, but
-  // we'd need to either wait for all results to come in (unacceptably slow) or
-  // do the wrong thing when the user had chosen some provider whose results
-  // were not returned instantaneously.
-  //
-  // This variable is only valid when user_input_in_progress_ is true, since
-  // when it is false the user has either never input anything (so there won't
-  // be a value here anyway) or has canceled their input, which should be
-  // treated the same way.  Also, since this is for preserving a desired URL
-  // after the popup has been closed, we ignore this if the popup is open, and
-  // simply ask the popup for the desired URL directly.  As a result, the
-  // contents of this variable only need to be updated when the popup is closed
-  // but user_input_in_progress_ is not being cleared.
-  std::u16string url_for_remembered_user_selection_;
-
   // Inline autocomplete is allowed if the user has not just deleted text. In
   // this case, inline_autocompletion_ is appended to the user_text_ and
   // displayed selected (at least initially).
@@ -442,12 +358,6 @@
   // presses escape.
   bool in_revert_;
 
-  // When exiting the scope or committing the omnibox, it should
-  // be closed again, unless lens is invoked by taking a contextual search
-  // match. In that case, the omnibox relinquishes the obligation to close so
-  // as to not interfere with lens match fulfillment and continued use.
-  bool close_lens_;
-
   // The input that was sent to the AutocompleteController. Since no
   // autocomplete query is started after a tab switch, it is possible for this
   // `input_` to differ from the one currently stored in AutocompleteController.
@@ -455,17 +365,7 @@
 
   // The popup view is nullptr when there's no popup, and is non-null when
   // a popup view exists (i.e. between calls to `set_popup_view`).
-  raw_ptr<OmniboxPopupViewBase> popup_view_ = nullptr;
-
-  // The current popup selection; set to normal kNoMatch when there's no popup.
-  OmniboxPopupSelection popup_selection_ =
-      OmniboxPopupSelection(OmniboxPopupSelection::kNoMatch,
-                            OmniboxPopupSelection::NORMAL);
-
-  // When a result changes, this informs of the URL in the previously selected
-  // suggestion whose tab switch button was focused, so that we may compare
-  // if equal.
-  GURL old_focused_url_;
+  raw_ptr<OmniboxPopupViewIOS> popup_view_ = nullptr;
 
   base::WeakPtrFactory<OmniboxEditModelIOS> weak_factory_{this};
 };
diff --git a/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.mm b/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.mm
index 4912dffb6..f54db03 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.mm
@@ -65,22 +65,17 @@
 #import "components/search_engines/template_url_starter_pack_data.h"
 #import "components/strings/grit/components_strings.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_controller_ios.h"
-#import "ios/chrome/browser/omnibox/model/omnibox_popup_view_base.h"
+#import "ios/chrome/browser/omnibox/model/omnibox_popup_view_ios.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_view_base.h"
 #import "net/cookies/cookie_util.h"
 #import "third_party/icu/source/common/unicode/ubidi.h"
 #import "third_party/metrics_proto/omnibox_event.pb.h"
 #import "third_party/metrics_proto/omnibox_focus_type.pb.h"
-#import "ui/base/l10n/l10n_util.h"
-#import "ui/gfx/color_palette.h"
-#import "ui/gfx/geometry/rect.h"
-#import "ui/gfx/image/image.h"
 #import "url/third_party/mozilla/url_parse.h"
 #import "url/url_util.h"
 
 using bookmarks::BookmarkModel;
 using metrics::OmniboxEventProto;
-using omnibox::mojom::NavigationPredictor;
 
 // Helpers --------------------------------------------------------------------
 
@@ -245,18 +240,12 @@
       focus_resulted_in_navigation_(false),
       just_deleted_text_(false),
       paste_state_(NONE),
-      in_revert_(false),
-      close_lens_(false) {}
+      in_revert_(false) {}
 
 OmniboxEditModelIOS::~OmniboxEditModelIOS() = default;
 
-void OmniboxEditModelIOS::set_popup_view(OmniboxPopupViewBase* popup_view) {
+void OmniboxEditModelIOS::set_popup_view(OmniboxPopupViewIOS* popup_view) {
   popup_view_ = popup_view;
-
-  // Clear/reset popup-related state.
-  old_focused_url_ = GURL();
-  popup_selection_ = OmniboxPopupSelection(OmniboxPopupSelection::kNoMatch,
-                                           OmniboxPopupSelection::NORMAL);
 }
 
 metrics::OmniboxEventProto::PageClassification
@@ -319,16 +308,6 @@
       autocomplete_controller()->result(), has_focus());
 }
 
-void OmniboxEditModelIOS::GetDataForURLExport(GURL* url,
-                                              std::u16string* title,
-                                              gfx::Image* favicon) {
-  *url = CurrentMatch(nullptr).destination_url;
-  if (*url == controller_->client()->GetURL()) {
-    *title = controller_->client()->GetTitle();
-    *favicon = controller_->client()->GetFavicon();
-  }
-}
-
 bool OmniboxEditModelIOS::CurrentTextIsURL() const {
   // If !user_input_in_progress_, we can determine if the text is a URL without
   // starting the autocomplete system. This speeds browser startup.
@@ -372,10 +351,6 @@
   // This code early exits if the copied text looks like a search query. It's
   // not at the very top of this method, as it would interpret the intranet URL
   // "printer/path" as a search query instead of a URL.
-  //
-  // We can't use CurrentTextIsURL() or GetDataForURLExport() because right now
-  // the user is probably holding down control to cause the copy, which will
-  // screw up our calculation of the desired_tld.
   AutocompleteMatch match_from_text;
   controller_->client()->GetAutocompleteClassifier()->Classify(
       *text, /*is_keyword_selected=*/false, true, GetPageClassification(),
@@ -524,48 +499,6 @@
   controller_->StartAutocomplete(input_);
 }
 
-bool OmniboxEditModelIOS::CanPasteAndGo(const std::u16string& text) const {
-  if (!controller_->client()->IsPasteAndGoEnabled()) {
-    return false;
-  }
-
-  AutocompleteMatch match;
-  ClassifyString(text, &match, nullptr);
-  return match.destination_url.is_valid();
-}
-
-void OmniboxEditModelIOS::PasteAndGo(
-    const std::u16string& text,
-    base::TimeTicks match_selection_timestamp) {
-  DCHECK(CanPasteAndGo(text));
-
-  if (view_) {
-    view_->RevertAll();
-  }
-  AutocompleteMatch match;
-  GURL alternate_nav_url;
-  ClassifyString(text, &match, &alternate_nav_url);
-
-  GURL upgraded_url;
-  if (match.type == AutocompleteMatchType::URL_WHAT_YOU_TYPED &&
-      controller_->client()->ShouldDefaultTypedNavigationsToHttps() &&
-      AutocompleteInput::ShouldUpgradeToHttps(
-          text, match.destination_url,
-          controller_->client()->GetHttpsPortForTesting(),
-          controller_->client()->IsUsingFakeHttpsForHttpsUpgradeTesting(),
-          &upgraded_url)) {
-    input_.set_added_default_scheme_to_typed_url(true);
-    DCHECK(upgraded_url.is_valid());
-    match.destination_url = upgraded_url;
-  } else {
-    input_.set_added_default_scheme_to_typed_url(false);
-  }
-
-  OpenMatch(OmniboxPopupSelection(OmniboxPopupSelection::kNoMatch), match,
-            WindowOpenDisposition::CURRENT_TAB, alternate_nav_url, text,
-            match_selection_timestamp);
-}
-
 void OmniboxEditModelIOS::OpenSelection(OmniboxPopupSelection selection,
                                         base::TimeTicks timestamp,
                                         WindowOpenDisposition disposition) {
@@ -588,7 +521,9 @@
 
 void OmniboxEditModelIOS::OpenSelection(base::TimeTicks timestamp,
                                         WindowOpenDisposition disposition) {
-  OpenSelection(popup_selection_, timestamp, disposition);
+  OpenSelection(OmniboxPopupSelection(OmniboxPopupSelection::kNoMatch,
+                                      OmniboxPopupSelection::NORMAL),
+                timestamp, disposition);
 }
 
 void OmniboxEditModelIOS::ClearAdditionalText() {
@@ -662,14 +597,6 @@
   controller_->StartAutocomplete(input_);
 }
 
-void OmniboxEditModelIOS::SetCaretVisibility(bool visible) {
-  // Caret visibility only matters if the omnibox has focus.
-  if (focus_state_ != OMNIBOX_FOCUS_NONE) {
-    SetFocusState(visible ? OMNIBOX_FOCUS_VISIBLE : OMNIBOX_FOCUS_INVISIBLE,
-                  OMNIBOX_FOCUS_CHANGE_EXPLICIT);
-  }
-}
-
 void OmniboxEditModelIOS::OnWillKillFocus() {
   if (user_input_in_progress_ || !in_revert_) {
     controller_->client()->OnInputStateChanged();
@@ -707,9 +634,6 @@
   current_match_ = new_match;
 
   inline_autocompletion_ = inline_autocompletion;
-  if (inline_autocompletion_.empty() && view_) {
-    view_->OnInlineAutocompleteTextCleared();
-  }
 
   const std::u16string& user_text =
       user_input_in_progress_ ? user_text_ : input_.text();
@@ -787,20 +711,10 @@
 const char OmniboxEditModelIOS::kCutOrCopyAllTextHistogram[] =
     "Omnibox.CutOrCopyAllText";
 
-void OmniboxEditModelIOS::SetAccessibilityLabel(
-    const AutocompleteMatch& match) {
-  if (view_) {
-    view_->SetAccessibilityLabel(view_->GetText(), match, true);
-  }
-}
-
 void OmniboxEditModelIOS::InternalSetUserText(const std::u16string& text) {
   user_text_ = text;
   just_deleted_text_ = false;
   inline_autocompletion_.clear();
-  if (view_) {
-    view_->OnInlineAutocompleteTextCleared();
-  }
 }
 
 void OmniboxEditModelIOS::GetInfoForCurrentText(AutocompleteMatch* match,
@@ -817,13 +731,6 @@
       // stopped. So the default match must be the desired selection.
       *match = *autocomplete_controller()->result().default_match();
       found_match_for_text = true;
-    } else if (PopupIsOpen() &&
-               GetPopupSelection().line != OmniboxPopupSelection::kNoMatch) {
-      const OmniboxPopupSelection selection = GetPopupSelection();
-      const AutocompleteMatch& selected_match =
-          autocomplete_controller()->result().match_at(selection.line);
-      *match = selected_match;
-      found_match_for_text = true;
     }
     if (found_match_for_text && alternate_nav_url) {
       AutocompleteProviderClient* provider_client =
@@ -845,47 +752,14 @@
   }
 }
 
-bool OmniboxEditModelIOS::IsStarredMatch(const AutocompleteMatch& match) const {
-  auto* bookmark_model = controller_->client()->GetBookmarkModel();
-  return bookmark_model && bookmark_model->IsBookmarked(match.destination_url);
-}
-
-std::u16string OmniboxEditModelIOS::GetSuggestionGroupHeaderText(
-    const std::optional<omnibox::GroupId>& suggestion_group_id) const {
-  bool force_hide_row_header =
-      OmniboxFieldTrial::IsHideSuggestionGroupHeadersEnabledInContext(
-          autocomplete_controller()->input().current_page_classification());
-
-  return suggestion_group_id.has_value() && !force_hide_row_header
-             ? autocomplete_controller()->result().GetHeaderForSuggestionGroup(
-                   suggestion_group_id.value())
-             : u"";
-}
-
 bool OmniboxEditModelIOS::PopupIsOpen() const {
   return popup_view_ && popup_view_->IsOpen();
 }
 
-OmniboxPopupSelection OmniboxEditModelIOS::GetPopupSelection() const {
-  DCHECK(popup_view_);
-  return popup_selection_;
-}
-
 void OmniboxEditModelIOS::OnPopupResultChanged() {
   if (!popup_view_) {
     return;
   }
-  const AutocompleteResult& result = autocomplete_controller()->result();
-
-  if (result.default_match()) {
-    OmniboxPopupSelection selection = GetPopupSelection();
-    selection.line = 0;
-    selection.state = OmniboxPopupSelection::NORMAL;
-    popup_selection_ = selection;
-  } else {
-    popup_selection_ = OmniboxPopupSelection(OmniboxPopupSelection::kNoMatch,
-                                             OmniboxPopupSelection::NORMAL);
-  }
   popup_view_->UpdatePopupAppearance();
 }
 
@@ -1211,14 +1085,6 @@
   }
 }
 
-void OmniboxEditModelIOS::ClassifyString(const std::u16string& text,
-                                         AutocompleteMatch* match,
-                                         GURL* alternate_nav_url) const {
-  DCHECK(match);
-  controller_->client()->GetAutocompleteClassifier()->Classify(
-      text, false, false, GetPageClassification(), match, alternate_nav_url);
-}
-
 bool OmniboxEditModelIOS::SetInputInProgressNoNotify(bool in_progress) {
   if (user_input_in_progress_ == in_progress) {
     return false;
@@ -1247,15 +1113,7 @@
     return;
   }
 
-  // Update state and notify view if the omnibox has focus and the caret
-  // visibility changed.
-  const bool was_caret_visible = is_caret_visible();
   focus_state_ = state;
-  if (focus_state_ != OMNIBOX_FOCUS_NONE &&
-      is_caret_visible() != was_caret_visible && view_) {
-    view_->ApplyCaretVisibility();
-  }
-
   controller_->client()->OnFocusChanged(focus_state_, reason);
 }
 
diff --git a/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios_unittest.mm b/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios_unittest.mm
index 82de241..5d5403d 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios_unittest.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_edit_model_ios_unittest.mm
@@ -35,7 +35,7 @@
 #import "ios/chrome/browser/omnibox/model/omnibox_controller_ios.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_view_base.h"
 #import "ios/chrome/browser/omnibox/model/test_omnibox_edit_model_ios.h"
-#import "ios/chrome/browser/omnibox/model/test_omnibox_popup_view_base.h"
+#import "ios/chrome/browser/omnibox/model/test_omnibox_popup_view_ios.h"
 #import "ios/chrome/browser/omnibox/model/test_omnibox_view_base.h"
 #import "testing/gmock/include/gmock/gmock.h"
 #import "testing/gtest/include/gtest/gtest.h"
@@ -366,7 +366,6 @@
   EXPECT_EQ(u"https://www.example.com/", model()->GetPermanentDisplayText());
   EXPECT_EQ(u"https://www.example.com/", view()->GetText());
   EXPECT_FALSE(model()->user_input_in_progress());
-  EXPECT_FALSE(view()->IsSelectAll());
 
   EXPECT_EQ(u"https://www.example.com/", view()->GetText());
   EXPECT_TRUE(model()->CurrentTextIsURL());
@@ -429,7 +428,7 @@
   base::test::TaskEnvironment task_environment_;
   TestingPrefServiceSimple pref_service_;
   std::unique_ptr<TestOmniboxViewBase> view_;
-  TestOmniboxPopupViewBase popup_view_;
+  TestOmniboxPopupViewIOS popup_view_;
   OmniboxTriggeredFeatureService triggered_feature_service_;
 };
 
diff --git a/ios/chrome/browser/omnibox/model/omnibox_popup_view_base.h b/ios/chrome/browser/omnibox/model/omnibox_popup_view_base.h
deleted file mode 100644
index b6cc83d..0000000
--- a/ios/chrome/browser/omnibox/model/omnibox_popup_view_base.h
+++ /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.
-
-// This file defines the interface class OmniboxPopupViewBase.  Each toolkit
-// will implement the popup view differently, so that code is inherently
-// platform specific.  However, the OmniboxPopupModel needs to do some
-// communication with the view.  Since the model is shared between platforms,
-// we need to define an interface that all view implementations will share.
-
-#ifndef IOS_CHROME_BROWSER_OMNIBOX_MODEL_OMNIBOX_POPUP_VIEW_BASE_H_
-#define IOS_CHROME_BROWSER_OMNIBOX_MODEL_OMNIBOX_POPUP_VIEW_BASE_H_
-
-#import <stddef.h>
-
-#import <string_view>
-
-#import "base/callback_list.h"
-#import "base/functional/callback_forward.h"
-#import "base/memory/raw_ptr.h"
-#import "build/build_config.h"
-#import "components/omnibox/browser/omnibox_popup_selection.h"
-
-class OmniboxControllerIOS;
-class OmniboxEditModelIOS;
-namespace ui {
-struct AXNodeData;
-}
-
-class OmniboxPopupViewBase {
- public:
-  explicit OmniboxPopupViewBase(OmniboxControllerIOS* controller);
-  virtual ~OmniboxPopupViewBase();
-
-  virtual OmniboxEditModelIOS* model();
-  virtual const OmniboxEditModelIOS* model() const;
-
-  virtual OmniboxControllerIOS* controller();
-  virtual const OmniboxControllerIOS* controller() const;
-
-  // Returns true if the popup is currently open.
-  virtual bool IsOpen() const = 0;
-
-  // Redraws the popup window to match any changes in the result set; this may
-  // mean opening or closing the window.
-  virtual void UpdatePopupAppearance() = 0;
-
-  // Adds a callback that will be called when the popup window becomes visible.
-  base::CallbackListSubscription AddOpenListener(
-      base::RepeatingClosure callback);
-
- protected:
-  // Call when the popup will appear to notify listeners.
-  void NotifyOpenListeners();
-
- private:
-  base::RepeatingClosureList on_popup_callbacks_;
-
-  // Owned by OmniboxView which owns this.
-  const raw_ptr<OmniboxControllerIOS> controller_;
-};
-
-#endif  // IOS_CHROME_BROWSER_OMNIBOX_MODEL_OMNIBOX_POPUP_VIEW_BASE_H_
diff --git a/ios/chrome/browser/omnibox/model/omnibox_popup_view_base.mm b/ios/chrome/browser/omnibox/model/omnibox_popup_view_base.mm
deleted file mode 100644
index b0219bd7..0000000
--- a/ios/chrome/browser/omnibox/model/omnibox_popup_view_base.mm
+++ /dev/null
@@ -1,43 +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/omnibox/model/omnibox_popup_view_base.h"
-
-#import <string_view>
-
-#import "base/callback_list.h"
-#import "base/functional/callback_forward.h"
-#import "ios/chrome/browser/omnibox/model/omnibox_controller_ios.h"
-
-OmniboxPopupViewBase::OmniboxPopupViewBase(OmniboxControllerIOS* controller)
-    : controller_(controller) {}
-
-OmniboxPopupViewBase::~OmniboxPopupViewBase() = default;
-
-OmniboxEditModelIOS* OmniboxPopupViewBase::model() {
-  return const_cast<OmniboxEditModelIOS*>(
-      const_cast<const OmniboxPopupViewBase*>(this)->model());
-}
-
-const OmniboxEditModelIOS* OmniboxPopupViewBase::model() const {
-  return controller_->edit_model();
-}
-
-OmniboxControllerIOS* OmniboxPopupViewBase::controller() {
-  return const_cast<OmniboxControllerIOS*>(
-      const_cast<const OmniboxPopupViewBase*>(this)->controller());
-}
-
-const OmniboxControllerIOS* OmniboxPopupViewBase::controller() const {
-  return controller_;
-}
-
-base::CallbackListSubscription OmniboxPopupViewBase::AddOpenListener(
-    base::RepeatingClosure callback) {
-  return on_popup_callbacks_.Add(std::move(callback));
-}
-
-void OmniboxPopupViewBase::NotifyOpenListeners() {
-  on_popup_callbacks_.Notify();
-}
diff --git a/ios/chrome/browser/omnibox/model/omnibox_popup_view_ios.h b/ios/chrome/browser/omnibox/model/omnibox_popup_view_ios.h
index a2cfc86..0c551f0d 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_popup_view_ios.h
+++ b/ios/chrome/browser/omnibox/model/omnibox_popup_view_ios.h
@@ -8,24 +8,35 @@
 #import <UIKit/UIKit.h>
 
 #import "base/memory/raw_ptr.h"
-#import "ios/chrome/browser/omnibox/model/omnibox_popup_view_base.h"
 
 @class OmniboxAutocompleteController;
 class OmniboxControllerIOS;
+class OmniboxEditModelIOS;
 
-// iOS implementation of OmniboxPopupView.
-class OmniboxPopupViewIOS : public OmniboxPopupViewBase {
+class OmniboxPopupViewIOS {
  public:
   OmniboxPopupViewIOS(
       OmniboxControllerIOS* controller,
-      OmniboxAutocompleteController* omniboxAutocompleteController);
-  ~OmniboxPopupViewIOS() override;
+      OmniboxAutocompleteController* omnibox_autocomplete_controller);
+  virtual ~OmniboxPopupViewIOS();
 
-  // OmniboxPopupView implementation.
-  bool IsOpen() const override;
-  void UpdatePopupAppearance() override;
+  OmniboxEditModelIOS* model();
+  const OmniboxEditModelIOS* model() const;
+
+  OmniboxControllerIOS* controller();
+  const OmniboxControllerIOS* controller() const;
+
+  // Returns true if the popup is currently open.
+  virtual bool IsOpen() const;
+
+  // Redraws the popup window to match any changes in the result set; this may
+  // mean opening or closing the window.
+  virtual void UpdatePopupAppearance();
 
  private:
+  // Owned by OmniboxViewIOS which owns this.
+  const raw_ptr<OmniboxControllerIOS> controller_;
+
   __weak OmniboxAutocompleteController* omnibox_autocomplete_controller_;
 };
 
diff --git a/ios/chrome/browser/omnibox/model/omnibox_popup_view_ios.mm b/ios/chrome/browser/omnibox/model/omnibox_popup_view_ios.mm
index ce89287d..27807594 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_popup_view_ios.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_popup_view_ios.mm
@@ -4,38 +4,14 @@
 
 #import "ios/chrome/browser/omnibox/model/omnibox_popup_view_ios.h"
 
-#import <QuartzCore/QuartzCore.h>
-
-#import <memory>
-#import <string>
-
-#import "base/check.h"
-#import "base/memory/ptr_util.h"
-#import "base/metrics/histogram_macros.h"
-#import "base/metrics/user_metrics.h"
-#import "base/metrics/user_metrics_action.h"
-#import "components/omnibox/browser/autocomplete_match.h"
-#import "components/omnibox/browser/omnibox_popup_selection.h"
-#import "components/open_from_clipboard/clipboard_recent_content.h"
-#import "ios/chrome/browser/ntp/model/new_tab_page_tab_helper.h"
-#import "ios/chrome/browser/ntp/shared/metrics/home_metrics.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_autocomplete_controller.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_controller_ios.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_edit_model_ios.h"
-#import "ios/chrome/browser/omnibox/public/omnibox_util.h"
-#import "ios/chrome/browser/shared/model/profile/profile_ios.h"
-#import "ios/chrome/browser/shared/public/features/system_flags.h"
-#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
-#import "ios/chrome/grit/ios_theme_resources.h"
-#import "ios/web/public/thread/web_thread.h"
-#import "net/url_request/url_request_context_getter.h"
-
-using base::UserMetricsAction;
 
 OmniboxPopupViewIOS::OmniboxPopupViewIOS(
     OmniboxControllerIOS* controller,
     OmniboxAutocompleteController* omnibox_autocomplete_controller)
-    : OmniboxPopupViewBase(controller),
+    : controller_(controller),
       omnibox_autocomplete_controller_(omnibox_autocomplete_controller) {
   DCHECK(controller);
   model()->set_popup_view(this);
@@ -45,6 +21,24 @@
   model()->set_popup_view(nullptr);
 }
 
+OmniboxEditModelIOS* OmniboxPopupViewIOS::model() {
+  return const_cast<OmniboxEditModelIOS*>(
+      const_cast<const OmniboxPopupViewIOS*>(this)->model());
+}
+
+const OmniboxEditModelIOS* OmniboxPopupViewIOS::model() const {
+  return controller_->edit_model();
+}
+
+OmniboxControllerIOS* OmniboxPopupViewIOS::controller() {
+  return const_cast<OmniboxControllerIOS*>(
+      const_cast<const OmniboxPopupViewIOS*>(this)->controller());
+}
+
+const OmniboxControllerIOS* OmniboxPopupViewIOS::controller() const {
+  return controller_;
+}
+
 void OmniboxPopupViewIOS::UpdatePopupAppearance() {
   [omnibox_autocomplete_controller_ updatePopupSuggestions];
 }
diff --git a/ios/chrome/browser/omnibox/model/omnibox_view_base.h b/ios/chrome/browser/omnibox/model/omnibox_view_base.h
index f0b5919d..87e3a60 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_view_base.h
+++ b/ios/chrome/browser/omnibox/model/omnibox_view_base.h
@@ -52,18 +52,11 @@
   OmniboxControllerIOS* controller();
   const OmniboxControllerIOS* controller() const;
 
-  // Called when any relevant state changes other than changing tabs.
-  virtual void Update() = 0;
-
   // Returns the current text of the edit control, which could be the
   // "temporary" text set by the popup, the "permanent" text set by the
   // browser, or just whatever the user has currently typed.
   virtual std::u16string GetText() const = 0;
 
-  // `true` if the user is in the process of editing the field, or if
-  // the field is empty.
-  bool IsEditingOrEmpty() const;
-
   // The user text is the text the user has manually keyed in.  When present,
   // this is shown in preference to the permanent text; hitting escape will
   // revert to the permanent text.
@@ -84,19 +77,12 @@
   // Sets the omnibox adjacent additional text label in the location bar view.
   virtual void SetAdditionalText(const std::u16string& text) = 0;
 
-  // Returns true if all text is selected. Returns false if there is no text.
-  virtual bool IsSelectAll() const = 0;
-
   // Fills `start` and `end` with the indexes of the current selection's bounds.
   // It is not guaranteed that `*start < *end`, as the selection can be
   // directed.  If there is no selection, `start` and `end` will both be equal
   // to the current cursor position.
   virtual void GetSelectionBounds(size_t* start, size_t* end) const = 0;
 
-  // Selects all the text in the edit.  Use this in place of SetSelAll() to
-  // avoid selecting the "phantom newline" at the end of the edit.
-  virtual void SelectAll(bool reversed) = 0;
-
   // Reverts the edit and popup back to their unedited state (permanent text
   // showing, popup closed, no user input in progress).
   virtual void RevertAll();
@@ -110,20 +96,6 @@
   // defines a method with that name.
   virtual void CloseOmniboxPopup();
 
-  // Sets the focus to the omnibox. `is_user_initiated` is true when the user
-  // explicitly focused the omnibox, and false when the omnibox was
-  // automatically focused (like for browser startup or NTP load).
-  virtual void SetFocus(bool is_user_initiated) = 0;
-
-  // Shows or hides the caret based on whether the model's is_caret_visible() is
-  // true.
-  virtual void ApplyCaretVisibility() = 0;
-
-  // Updates the accessibility state by enunciating any on-focus text.
-  virtual void SetAccessibilityLabel(const std::u16string& display_text,
-                                     const AutocompleteMatch& match,
-                                     bool notify_text_changed) {}
-
   // Called when the inline autocomplete text in the model may have changed.
   // `user_text` is the portion of omnibox text the user typed.
   // `inline`_autocompletion` is the autocompleted part.
@@ -131,9 +103,6 @@
       const std::u16string& user_text,
       const std::u16string& inline_autocompletion) = 0;
 
-  // Called when the inline autocomplete text in the model has been cleared.
-  virtual void OnInlineAutocompleteTextCleared() = 0;
-
   // Checkpoints the current edit state before an operation that might trigger
   // a new autocomplete run to open or modify the popup. Call this before
   // user-initiated edit actions that trigger autocomplete, but *not* for
@@ -144,35 +113,6 @@
   // to call UpdatePopup().
   virtual bool OnAfterPossibleChange() = 0;
 
-  // Returns the gfx::NativeView of the edit view.
-  virtual gfx::NativeView GetNativeView() const = 0;
-
-  // Gets the relative window for the pop up window of OmniboxPopupView. The pop
-  // up window will be shown under the relative window. When an IME is attached
-  // to the rich edit control, the IME window is the relative window. Otherwise,
-  // the top-most window is the relative window.
-  virtual gfx::NativeView GetRelativeWindowForPopup() const = 0;
-
-  // Returns true if the user is composing something in an IME.
-  virtual bool IsImeComposing() const = 0;
-
-  // Returns true if we know for sure that an IME is showing a popup window,
-  // which may overlap the omnibox's popup window.
-  virtual bool IsImeShowingPopup() const;
-
-  // Display a virtual keyboard or alternate input view if enabled.
-  virtual void ShowVirtualKeyboardIfEnabled();
-
-  // Hides a virtual keyboard or alternate input view if enabled.
-  virtual void HideImeIfNeeded();
-
-  // Returns true if the view is displaying UI that indicates that query
-  // refinement will take place when the user selects the current match.  For
-  // search matches, this will cause the omnibox to search over the existing
-  // corpus (e.g. Images) rather than start a new Web search.  This method will
-  // only ever return true on mobile ports.
-  virtual bool IsIndicatingQueryRefinement() const;
-
   // Returns `text` with any leading javascript schemas stripped.
   static std::u16string StripJavascriptSchemas(const std::u16string& text);
 
@@ -214,33 +154,6 @@
   // Internally invoked whenever the text changes in some way.
   virtual void TextChanged();
 
-  // Return the number of characters in the current buffer. The name
-  // `GetTextLength` can't be used as the Windows override of this class
-  // inherits from a class that defines a method with that name.
-  virtual int GetOmniboxTextLength() const = 0;
-
-  // Try to parse the current text as a URL and colorize the components.
-  virtual void EmphasizeURLComponents() = 0;
-
-  // Marks part (or, if `range` is invalid, all) of the current text as
-  // emphasized or de-emphasized, by changing its color.
-  virtual void SetEmphasis(bool emphasize, const gfx::Range& range) = 0;
-
-  // Sets the color and strikethrough state for `range`, which represents the
-  // current scheme, based on the current security state.  Schemes are displayed
-  // in different ways for different security levels.
-  virtual void UpdateSchemeStyle(const gfx::Range& range) = 0;
-
-  // Parses `display_text`, then invokes SetEmphasis() and UpdateSchemeStyle()
-  // appropriately. If the text is a query string, there is no scheme, and
-  // everything is emphasized equally, whereas for URLs the scheme may be styled
-  // based on the current security state, with parts of the URL de-emphasized to
-  // draw attention to whatever best represents the "identity" of the current
-  // URL.
-  void UpdateTextStyle(const std::u16string& display_text,
-                       const bool text_is_url,
-                       const AutocompleteSchemeClassifier& classifier);
-
  private:
   friend class TestOmniboxViewBase;
 
diff --git a/ios/chrome/browser/omnibox/model/omnibox_view_base.mm b/ios/chrome/browser/omnibox/model/omnibox_view_base.mm
index 42741a99..56e9ba13 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_view_base.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_view_base.mm
@@ -152,13 +152,6 @@
 
 OmniboxViewBase::~OmniboxViewBase() = default;
 
-bool OmniboxViewBase::IsEditingOrEmpty() const {
-  return model()->user_input_in_progress() || GetOmniboxTextLength() == 0 ||
-         (OmniboxFieldTrial::IsOnFocusZeroSuggestEnabledInContext(
-              model()->GetPageClassification()) &&
-          model()->PopupIsOpen());
-}
-
 void OmniboxViewBase::SetUserText(const std::u16string& text) {
   SetUserText(text, true);
 }
@@ -187,23 +180,6 @@
   controller()->StopAutocomplete(/*clear_result=*/true);
 }
 
-bool OmniboxViewBase::IsImeShowingPopup() const {
-  // Default to claiming that the IME is not showing a popup, since hiding the
-  // omnibox dropdown is a bad user experience when we don't know for sure that
-  // we have to.
-  return false;
-}
-
-void OmniboxViewBase::ShowVirtualKeyboardIfEnabled() {}
-
-void OmniboxViewBase::HideImeIfNeeded() {}
-
-bool OmniboxViewBase::IsIndicatingQueryRefinement() const {
-  // The default implementation always returns false.  Mobile ports can override
-  // this method and implement as needed.
-  return false;
-}
-
 void OmniboxViewBase::GetState(State* state) {
   state->text = GetText();
   GetSelectionBounds(&state->sel_start, &state->sel_end);
@@ -264,76 +240,5 @@
 }
 
 void OmniboxViewBase::TextChanged() {
-  EmphasizeURLComponents();
   model()->OnChanged();
 }
-
-void OmniboxViewBase::UpdateTextStyle(
-    const std::u16string& display_text,
-    const bool text_is_url,
-    const AutocompleteSchemeClassifier& classifier) {
-  if (!text_is_url) {
-    SetEmphasis(true, gfx::Range::InvalidRange());
-    return;
-  }
-
-  enum DemphasizeComponents {
-    EVERYTHING,
-    ALL_BUT_SCHEME,
-    ALL_BUT_HOST,
-    NOTHING,
-  } deemphasize = NOTHING;
-
-  url::Component scheme, host;
-  AutocompleteInput::ParseForEmphasizeComponents(display_text, classifier,
-                                                 &scheme, &host);
-
-  const std::u16string url_scheme =
-      display_text.substr(scheme.begin, scheme.len);
-
-  const bool is_extension_url =
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-      base::EqualsASCII(url_scheme, extensions::kExtensionScheme);
-#else
-      false;
-#endif
-
-  // Extension IDs are not human-readable, so deemphasize everything to draw
-  // attention to the human-readable name in the location icon text.
-  // Data URLs are rarely human-readable and can be used for spoofing, so draw
-  // attention to the scheme to emphasize "this is just a bunch of data".
-  // For normal URLs, the host is the best proxy for "identity".
-  if (is_extension_url) {
-    deemphasize = EVERYTHING;
-  } else if (url_scheme == url::kDataScheme16) {
-    deemphasize = ALL_BUT_SCHEME;
-  } else if (host.is_nonempty()) {
-    deemphasize = ALL_BUT_HOST;
-  }
-
-  gfx::Range scheme_range = scheme.is_nonempty()
-                                ? gfx::Range(scheme.begin, scheme.end())
-                                : gfx::Range::InvalidRange();
-  switch (deemphasize) {
-    case EVERYTHING:
-      SetEmphasis(false, gfx::Range::InvalidRange());
-      break;
-    case NOTHING:
-      SetEmphasis(true, gfx::Range::InvalidRange());
-      break;
-    case ALL_BUT_SCHEME:
-      DCHECK(scheme_range.IsValid());
-      SetEmphasis(false, gfx::Range::InvalidRange());
-      SetEmphasis(true, scheme_range);
-      break;
-    case ALL_BUT_HOST:
-      SetEmphasis(false, gfx::Range::InvalidRange());
-      SetEmphasis(true, gfx::Range(host.begin, host.end()));
-      break;
-  }
-
-  // Emphasize the scheme for security UI display purposes (if necessary).
-  if (!model()->user_input_in_progress() && scheme_range.IsValid()) {
-    UpdateSchemeStyle(scheme_range);
-  }
-}
diff --git a/ios/chrome/browser/omnibox/model/omnibox_view_base_unittest.mm b/ios/chrome/browser/omnibox/model/omnibox_view_base_unittest.mm
index 96df963a..e898223 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_view_base_unittest.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_view_base_unittest.mm
@@ -25,7 +25,7 @@
 #import "components/search_engines/template_url_data.h"
 #import "ios/chrome/browser/omnibox/model/omnibox_controller_ios.h"
 #import "ios/chrome/browser/omnibox/model/test_omnibox_edit_model_ios.h"
-#import "ios/chrome/browser/omnibox/model/test_omnibox_popup_view_base.h"
+#import "ios/chrome/browser/omnibox/model/test_omnibox_popup_view_ios.h"
 #import "ios/chrome/browser/omnibox/model/test_omnibox_view_base.h"
 #import "testing/gmock/include/gmock/gmock.h"
 #import "testing/gtest/include/gtest/gtest.h"
@@ -106,7 +106,7 @@
   base::test::TaskEnvironment task_environment_;
   std::unique_ptr<TestOmniboxViewBase> view_;
   raw_ptr<TestOmniboxClient> omnibox_client_;
-  TestOmniboxPopupViewBase popup_view_;
+  TestOmniboxPopupViewIOS popup_view_;
 };
 }  // namespace
 
diff --git a/ios/chrome/browser/omnibox/model/omnibox_view_ios.h b/ios/chrome/browser/omnibox/model/omnibox_view_ios.h
index d3abe8be..94be1e7 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_view_ios.h
+++ b/ios/chrome/browser/omnibox/model/omnibox_view_ios.h
@@ -53,21 +53,9 @@
       const std::u16string& inline_autocompletion) override;
   void OnBeforePossibleChange() override;
   bool OnAfterPossibleChange() override;
-  bool IsImeComposing() const override;
-  bool IsIndicatingQueryRefinement() const override;
   void SetAdditionalText(const std::u16string& text) override;
-
-  // OmniboxView stubs.
-  void Update() override {}
-  bool IsSelectAll() const override;
   void GetSelectionBounds(std::u16string::size_type* start,
                           std::u16string::size_type* end) const override;
-  void SelectAll(bool reversed) override {}
-  void SetFocus(bool is_user_initiated) override {}
-  void ApplyCaretVisibility() override {}
-  void OnInlineAutocompleteTextCleared() override {}
-  gfx::NativeView GetNativeView() const override;
-  gfx::NativeView GetRelativeWindowForPopup() const override;
 
   // OmniboxTextChange methods.
 
@@ -87,14 +75,7 @@
   // Returns the current selection.
   NSRange GetCurrentSelection() const { return current_selection_; }
 
- protected:
-  int GetOmniboxTextLength() const override;
-  void EmphasizeURLComponents() override {}
-
  private:
-  void SetEmphasis(bool emphasize, const gfx::Range& range) override {}
-  void UpdateSchemeStyle(const gfx::Range& scheme_range) override {}
-
   OmniboxTextFieldIOS* field_;
 
   State state_before_change_;
diff --git a/ios/chrome/browser/omnibox/model/omnibox_view_ios.mm b/ios/chrome/browser/omnibox/model/omnibox_view_ios.mm
index d13e555..aa42d573 100644
--- a/ios/chrome/browser/omnibox/model/omnibox_view_ios.mm
+++ b/ios/chrome/browser/omnibox/model/omnibox_view_ios.mm
@@ -131,18 +131,6 @@
   return something_changed;
 }
 
-bool OmniboxViewIOS::IsImeComposing() const {
-  return [field_ markedTextRange] != nil;
-}
-
-bool OmniboxViewIOS::IsIndicatingQueryRefinement() const {
-  return false;
-}
-
-bool OmniboxViewIOS::IsSelectAll() const {
-  return false;
-}
-
 void OmniboxViewIOS::GetSelectionBounds(std::u16string::size_type* start,
                                         std::u16string::size_type* end) const {
   if ([field_ isFirstResponder]) {
@@ -154,14 +142,6 @@
   }
 }
 
-gfx::NativeView OmniboxViewIOS::GetNativeView() const {
-  return gfx::NativeView();
-}
-
-gfx::NativeView OmniboxViewIOS::GetRelativeWindowForPopup() const {
-  return gfx::NativeView();
-}
-
 bool OmniboxViewIOS::OnWillChange(NSRange range, NSString* new_text) {
   bool ok_to_change = true;
 
@@ -292,7 +272,3 @@
   current_selection_ = [field_ selectedNSRange];
   OnDidChange(/*processing_user_event=*/true);
 }
-
-int OmniboxViewIOS::GetOmniboxTextLength() const {
-  return [field_ displayedText].length;
-}
diff --git a/ios/chrome/browser/omnibox/model/test_omnibox_popup_view_base.h b/ios/chrome/browser/omnibox/model/test_omnibox_popup_view_base.h
deleted file mode 100644
index 3c6ca99..0000000
--- a/ios/chrome/browser/omnibox/model/test_omnibox_popup_view_base.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_OMNIBOX_MODEL_TEST_OMNIBOX_POPUP_VIEW_BASE_H_
-#define IOS_CHROME_BROWSER_OMNIBOX_MODEL_TEST_OMNIBOX_POPUP_VIEW_BASE_H_
-
-#import "ios/chrome/browser/omnibox/model/omnibox_popup_view_base.h"
-
-// Fake implementation of OmniboxPopupViewBase for use in tests.
-class TestOmniboxPopupViewBase : public OmniboxPopupViewBase {
- public:
-  TestOmniboxPopupViewBase() : OmniboxPopupViewBase(/*controller=*/nullptr) {}
-  ~TestOmniboxPopupViewBase() override = default;
-  bool IsOpen() const override;
-  void UpdatePopupAppearance() override {}
-};
-
-#endif  // IOS_CHROME_BROWSER_OMNIBOX_MODEL_TEST_OMNIBOX_POPUP_VIEW_BASE_H_
diff --git a/ios/chrome/browser/omnibox/model/test_omnibox_popup_view_ios.h b/ios/chrome/browser/omnibox/model/test_omnibox_popup_view_ios.h
new file mode 100644
index 0000000..04cabb0
--- /dev/null
+++ b/ios/chrome/browser/omnibox/model/test_omnibox_popup_view_ios.h
@@ -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.
+
+#ifndef IOS_CHROME_BROWSER_OMNIBOX_MODEL_TEST_OMNIBOX_POPUP_VIEW_IOS_H_
+#define IOS_CHROME_BROWSER_OMNIBOX_MODEL_TEST_OMNIBOX_POPUP_VIEW_IOS_H_
+
+#import "ios/chrome/browser/omnibox/model/omnibox_popup_view_ios.h"
+
+// Fake implementation of OmniboxPopupViewIOS for use in tests.
+class TestOmniboxPopupViewIOS : public OmniboxPopupViewIOS {
+ public:
+  TestOmniboxPopupViewIOS()
+      : OmniboxPopupViewIOS(/*controller=*/nullptr,
+                            /*omnibox_autocomplete_controller=*/nil) {}
+  ~TestOmniboxPopupViewIOS() override = default;
+  bool IsOpen() const override;
+  void UpdatePopupAppearance() override {}
+};
+
+#endif  // IOS_CHROME_BROWSER_OMNIBOX_MODEL_TEST_OMNIBOX_POPUP_VIEW_IOS_H_
diff --git a/ios/chrome/browser/omnibox/model/test_omnibox_popup_view_base.mm b/ios/chrome/browser/omnibox/model/test_omnibox_popup_view_ios.mm
similarity index 79%
rename from ios/chrome/browser/omnibox/model/test_omnibox_popup_view_base.mm
rename to ios/chrome/browser/omnibox/model/test_omnibox_popup_view_ios.mm
index 8055824d..d8a9c82 100644
--- a/ios/chrome/browser/omnibox/model/test_omnibox_popup_view_base.mm
+++ b/ios/chrome/browser/omnibox/model/test_omnibox_popup_view_ios.mm
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/omnibox/model/test_omnibox_popup_view_base.h"
+#import "ios/chrome/browser/omnibox/model/test_omnibox_popup_view_ios.h"
 
-bool TestOmniboxPopupViewBase::IsOpen() const {
+bool TestOmniboxPopupViewIOS::IsOpen() const {
   return false;
 }
diff --git a/ios/chrome/browser/omnibox/model/test_omnibox_view_base.h b/ios/chrome/browser/omnibox/model/test_omnibox_view_base.h
index a16f83a..580e2bb7 100644
--- a/ios/chrome/browser/omnibox/model/test_omnibox_view_base.h
+++ b/ios/chrome/browser/omnibox/model/test_omnibox_view_base.h
@@ -28,7 +28,6 @@
   static State CreateState(std::string text, size_t sel_start, size_t sel_end);
 
   // OmniboxViewBase:
-  void Update() override {}
   std::u16string GetText() const override;
   void SetWindowTextAndCaretPos(const std::u16string& text,
                                 size_t caret_pos,
@@ -36,25 +35,13 @@
                                 bool notify_text_changed) override;
   void SetCaretPos(size_t caret_pos) override {}
   void SetAdditionalText(const std::u16string& text) override {}
-  bool IsSelectAll() const override;
   void GetSelectionBounds(size_t* start, size_t* end) const override;
-  void SelectAll(bool reversed) override;
   void UpdatePopup() override {}
-  void SetFocus(bool is_user_initiated) override {}
-  void ApplyCaretVisibility() override {}
   void OnInlineAutocompleteTextMaybeChanged(
       const std::u16string& user_text,
       const std::u16string& inline_autocompletion) override;
-  void OnInlineAutocompleteTextCleared() override;
   void OnBeforePossibleChange() override {}
   bool OnAfterPossibleChange() override;
-  gfx::NativeView GetNativeView() const override;
-  gfx::NativeView GetRelativeWindowForPopup() const override;
-  bool IsImeComposing() const override;
-  int GetOmniboxTextLength() const override;
-  void EmphasizeURLComponents() override {}
-  void SetEmphasis(bool emphasize, const gfx::Range& range) override {}
-  void UpdateSchemeStyle(const gfx::Range& range) override {}
   using OmniboxViewBase::GetStateChanges;
 
  private:
diff --git a/ios/chrome/browser/omnibox/model/test_omnibox_view_base.mm b/ios/chrome/browser/omnibox/model/test_omnibox_view_base.mm
index 940efb8..bd17d88 100644
--- a/ios/chrome/browser/omnibox/model/test_omnibox_view_base.mm
+++ b/ios/chrome/browser/omnibox/model/test_omnibox_view_base.mm
@@ -33,23 +33,11 @@
   selection_ = gfx::Range(caret_pos);
 }
 
-bool TestOmniboxViewBase::IsSelectAll() const {
-  return selection_.EqualsIgnoringDirection(gfx::Range(0, text_.size()));
-}
-
 void TestOmniboxViewBase::GetSelectionBounds(size_t* start, size_t* end) const {
   *start = selection_.start();
   *end = selection_.end();
 }
 
-void TestOmniboxViewBase::SelectAll(bool reversed) {
-  if (reversed) {
-    selection_ = gfx::Range(text_.size(), 0);
-  } else {
-    selection_ = gfx::Range(0, text_.size());
-  }
-}
-
 void TestOmniboxViewBase::OnInlineAutocompleteTextMaybeChanged(
     const std::u16string& user_text,
     const std::u16string& inline_autocompletion) {
@@ -65,26 +53,6 @@
   }
 }
 
-void TestOmniboxViewBase::OnInlineAutocompleteTextCleared() {
-  inline_autocompletion_.clear();
-}
-
 bool TestOmniboxViewBase::OnAfterPossibleChange() {
   return false;
 }
-
-gfx::NativeView TestOmniboxViewBase::GetNativeView() const {
-  return gfx::NativeView();
-}
-
-gfx::NativeView TestOmniboxViewBase::GetRelativeWindowForPopup() const {
-  return gfx::NativeView();
-}
-
-bool TestOmniboxViewBase::IsImeComposing() const {
-  return false;
-}
-
-int TestOmniboxViewBase::GetOmniboxTextLength() const {
-  return 0;
-}
diff --git a/ios/chrome/browser/popup_menu/ui_bundled/overflow_menu/overflow_menu_mediator.mm b/ios/chrome/browser/popup_menu/ui_bundled/overflow_menu/overflow_menu_mediator.mm
index 14bc835..c43e651 100644
--- a/ios/chrome/browser/popup_menu/ui_bundled/overflow_menu/overflow_menu_mediator.mm
+++ b/ios/chrome/browser/popup_menu/ui_bundled/overflow_menu/overflow_menu_mediator.mm
@@ -2420,12 +2420,8 @@
 
 // Sets the Reader mode UI visibility.
 - (void)setReaderModeVisibility:(BOOL)visible {
+  ReaderModeTabHelper::FromWebState(self.webState)->SetActive(visible);
   [self dismissMenu];
-  if (visible) {
-    [self.readerModeHandler showReaderMode];
-  } else {
-    [self.readerModeHandler hideReaderMode];
-  }
 }
 
 #pragma mark - Destinations Handlers
diff --git a/ios/chrome/browser/profile/model/profile_manager_ios_impl.h b/ios/chrome/browser/profile/model/profile_manager_ios_impl.h
index 103fe40..9b743bd8 100644
--- a/ios/chrome/browser/profile/model/profile_manager_ios_impl.h
+++ b/ios/chrome/browser/profile/model/profile_manager_ios_impl.h
@@ -122,6 +122,10 @@
   // Holds the Profile instances that this instance has created.
   std::map<std::string, ProfileInfo, std::less<>> profiles_map_;
 
+  // Holds the ScopedProfileKeepAliveIOS for the profiles that are loading.
+  std::map<std::string, ScopedProfileKeepAliveIOS, std::less<>>
+      loading_profiles_map_;
+
   // The owned ProfileAttributesStorageIOS instance.
   MutableProfileAttributesStorageIOS profile_attributes_storage_;
 
diff --git a/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm b/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm
index 64b8e48..0d59410 100644
--- a/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm
+++ b/ios/chrome/browser/profile/model/profile_manager_ios_impl.mm
@@ -105,7 +105,7 @@
  public:
   explicit ProfileInfo(std::unique_ptr<ProfileIOS> profile)
       : profile_(std::move(profile)) {
-    DCHECK(profile_);
+    CHECK(profile_);
   }
 
   ProfileInfo(ProfileInfo&&) = default;
@@ -128,14 +128,14 @@
   // Increment the keep alive counter and return its value.
   uint32_t IncrementKeepAliveCounter() {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    DCHECK_LT(keep_alive_counter_, std::numeric_limits<uint32_t>::max());
+    CHECK_LT(keep_alive_counter_, std::numeric_limits<uint32_t>::max());
     return ++keep_alive_counter_;
   }
 
   // Decrement the keep alive counter and return its value.
   uint32_t DecrementKeepAliveCounter() {
     DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-    DCHECK_GT(keep_alive_counter_, 0u);
+    CHECK_GT(keep_alive_counter_, 0u);
     return --keep_alive_counter_;
   }
 
@@ -143,20 +143,20 @@
   SEQUENCE_CHECKER(sequence_checker_);
   std::unique_ptr<ProfileIOS> profile_;
   std::vector<ProfileLoadedCallback> callbacks_;
-  uint32_t keep_alive_counter_ = 1;
+  uint32_t keep_alive_counter_ = 0;
   bool is_loaded_ = false;
 };
 
 void ProfileManagerIOSImpl::ProfileInfo::SetIsLoaded() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!is_loaded_);
+  CHECK(!is_loaded_);
   is_loaded_ = true;
 }
 
 void ProfileManagerIOSImpl::ProfileInfo::AddCallback(
     ProfileLoadedCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!is_loaded_);
+  CHECK(!is_loaded_);
   if (!callback.is_null()) {
     callbacks_.push_back(std::move(callback));
   }
@@ -184,20 +184,16 @@
 void ProfileManagerIOSImpl::PrepareForDestruction() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   will_be_destroyed_ = true;
+
+  // Drops the ScopedProfileKeepAliveIOS for all profiles that are still
+  // loading before notifying the observers. Then check that there are
+  // no profiles still kept alive.
+  loading_profiles_map_.clear();
+
   for (auto& observer : observers_) {
     observer.OnProfileManagerWillBeDestroyed(this);
   }
 
-  // If a profile has not been fully loaded, then the ProfileManagerIOSImpl
-  // will have kept an implicit reference (via the keep alive counter being
-  // initialized to 1). This should only be the case for profiles still in
-  // the loading stage. Unload them now.
-  while (!profiles_map_.empty()) {
-    auto iter = profiles_map_.begin();
-    CHECK(!iter->second.is_loaded());
-    MaybeUnloadProfile(iter->first);
-  }
-
   CHECK(profiles_map_.empty());
 }
 
@@ -212,7 +208,7 @@
     }
 
     ProfileIOS* profile = profile_info.profile();
-    DCHECK(profile);
+    CHECK(profile);
 
     observer->OnProfileCreated(this, profile);
     if (profile_info.is_loaded()) {
@@ -238,7 +234,7 @@
   if (iter != profiles_map_.end()) {
     ProfileInfo& profile_info = iter->second;
     if (profile_info.is_loaded()) {
-      DCHECK(profile_info.profile());
+      CHECK(profile_info.profile());
       return profile_info.profile();
     }
   }
@@ -255,7 +251,7 @@
     }
 
     if (profile_info.is_loaded()) {
-      DCHECK(profile_info.profile());
+      CHECK(profile_info.profile());
       loaded_profiles.push_back(profile_info.profile());
     }
   }
@@ -306,7 +302,7 @@
 
 void ProfileManagerIOSImpl::MarkProfileForDeletion(std::string_view name) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(CanDeleteProfileWithName(name));
+  CHECK(CanDeleteProfileWithName(name));
 
   // Remove the profile from the ProfileAttributesStorageIOS to prevent
   // people iterating over all profiles from seeing it anymore.
@@ -380,8 +376,8 @@
     ProfileIOS* profile,
     CreationMode creation_mode) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_EQ(creation_mode, CreationMode::kAsynchronous);
-  DCHECK(profile);
+  CHECK_EQ(creation_mode, CreationMode::kAsynchronous);
+  CHECK(profile);
 
   for (auto& observer : observers_) {
     observer.OnProfileCreated(this, profile);
@@ -394,13 +390,13 @@
     bool is_new_profile,
     bool success) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK_EQ(creation_mode, CreationMode::kAsynchronous);
-  DCHECK(profile);
-  DCHECK(!profile->IsOffTheRecord());
+  CHECK_EQ(creation_mode, CreationMode::kAsynchronous);
+  CHECK(profile);
+  CHECK(!profile->IsOffTheRecord());
 
   const std::string& name = profile->GetProfileName();
   auto iter = profiles_map_.find(name);
-  DCHECK(iter != profiles_map_.end());
+  CHECK(iter != profiles_map_.end());
   ProfileInfo& profile_info = iter->second;
   auto callbacks = profile_info.TakeCallbacks();
 
@@ -433,17 +429,17 @@
 
   // Notify the observers after invoking the callbacks in case of success.
   if (success) {
-    DCHECK(profile);
+    CHECK(profile);
     for (auto& observer : observers_) {
       observer.OnProfileLoaded(this, profile);
     }
   }
 
-  // The ProfileInfo is initialized with a keep alive counter of 1.
-  // Decrement the counter now that the load has completed (this will
-  // unload the profile unless one of the invoked callback stored the
-  // ScopedProfileKeepAliveIOS object).
-  MaybeUnloadProfile(name);
+  // The profile is fully loaded, so drop the ScopedProfileKeepAliveIOS
+  // owned by this instance. If no other code keeps the profile alive,
+  // it will be unloaded at this point.
+  CHECK(base::Contains(loading_profiles_map_, name));
+  loading_profiles_map_.erase(name);
 }
 
 bool ProfileManagerIOSImpl::CreateOrLoadProfile(
@@ -499,20 +495,27 @@
 
     if (!existing) {
       profile_attributes_storage_.AddProfile(name);
-      DCHECK(HasProfileWithName(name));
+      CHECK(HasProfileWithName(name));
     }
 
-    std::tie(iter, inserted) = profiles_map_.insert(std::make_pair(
-        std::string(name), ProfileInfo(ProfileIOS::CreateProfile(
-                               profile_data_dir_.Append(name), name,
-                               CreationMode::kAsynchronous, this))));
+    std::tie(iter, inserted) = profiles_map_.emplace(
+        name, ProfileIOS::CreateProfile(profile_data_dir_.Append(name), name,
+                                        CreationMode::kAsynchronous, this));
 
-    DCHECK(inserted);
+    CHECK(inserted);
   }
 
-  DCHECK(iter != profiles_map_.end());
+  CHECK(iter != profiles_map_.end());
   ProfileInfo& profile_info = iter->second;
-  DCHECK(profile_info.profile());
+  CHECK(profile_info.profile());
+
+  // Ensure the profile is kept alive until it is fully loaded or
+  // the current instance is destroyed.
+  if (inserted) {
+    CHECK(!base::Contains(loading_profiles_map_, name));
+    loading_profiles_map_.emplace(name,
+                                  CreateScopedProfileKeepAlive(&profile_info));
+  }
 
   if (!created_callback.is_null()) {
     std::move(created_callback)
@@ -536,7 +539,7 @@
   DoFinalInitForServices(profile);
 
   // Log the profile size after a reasonable startup delay.
-  DCHECK(!profile->IsOffTheRecord());
+  CHECK(!profile->IsOffTheRecord());
   base::ThreadPool::PostDelayedTask(
       FROM_HERE,
       {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
diff --git a/ios/chrome/browser/providers/discover_feed/chromium_discover_feed.mm b/ios/chrome/browser/providers/discover_feed/chromium_discover_feed.mm
index 94786a9f..7158f993 100644
--- a/ios/chrome/browser/providers/discover_feed/chromium_discover_feed.mm
+++ b/ios/chrome/browser/providers/discover_feed/chromium_discover_feed.mm
@@ -48,5 +48,10 @@
   return std::make_unique<ChromiumDiscoverFeedService>();
 }
 
+id<DiscoverFeedVisibilityProvider> CreateDiscoverFeedVisibilityProvider(
+    DiscoverFeedVisibilityProviderConfiguration* configuration) {
+  return nil;
+}
+
 }  // namespace provider
 }  // namespace ios
diff --git a/ios/chrome/browser/reader_mode/coordinator/reader_mode_coordinator.mm b/ios/chrome/browser/reader_mode/coordinator/reader_mode_coordinator.mm
index a038584..a939dd4 100644
--- a/ios/chrome/browser/reader_mode/coordinator/reader_mode_coordinator.mm
+++ b/ios/chrome/browser/reader_mode/coordinator/reader_mode_coordinator.mm
@@ -26,7 +26,7 @@
 - (void)start {
   _viewController = [[ReaderModeViewController alloc] init];
   _mediator = [[ReaderModeMediator alloc]
-      initWithWebState:self.browser->GetWebStateList()->GetActiveWebState()];
+      initWithWebStateList:self.browser->GetWebStateList()];
   _mediator.consumer = _viewController;
   [self.baseViewController addChildViewController:_viewController];
   [_viewController didMoveToParentViewController:self.baseViewController];
diff --git a/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.h b/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.h
index 7ac752e0..cfceceae 100644
--- a/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.h
+++ b/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.h
@@ -8,20 +8,17 @@
 #import <Foundation/Foundation.h>
 
 #import "base/memory/raw_ptr.h"
-#import "ios/chrome/browser/reader_mode/ui/reader_mode_consumer.h"
+#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
 
-namespace web {
-class WebState;
-}
+@protocol ReaderModeConsumer;
 
 // Mediator for the Reader mode UI.
 @interface ReaderModeMediator : NSObject
 
 @property(nonatomic, weak) id<ReaderModeConsumer> consumer;
 
-// Initializes the mediator using `webState` as the source WebState from which
-// Reader mode content needs to be extracted.
-- (instancetype)initWithWebState:(raw_ptr<web::WebState>)webState;
+// Initializes the mediator for the given `webStateList`.
+- (instancetype)initWithWebStateList:(raw_ptr<WebStateList>)webStateList;
 
 // Disconnects the mediator from the model layer.
 - (void)disconnect;
diff --git a/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.mm b/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.mm
index 07b06b2..d2595dc5 100644
--- a/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.mm
+++ b/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.mm
@@ -5,21 +5,28 @@
 #import "ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.h"
 
 #import "ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.h"
+#import "ios/chrome/browser/reader_mode/ui/reader_mode_consumer.h"
+#import "ios/chrome/browser/shared/model/web_state_list/web_state_list_observer_bridge.h"
 #import "ios/web/public/web_state.h"
 
+@interface ReaderModeMediator () <WebStateListObserving>
+@end
+
 @implementation ReaderModeMediator {
-  base::WeakPtr<web::WebState> _sourceWebState;
+  raw_ptr<WebStateList> _webStateList;
+  std::unique_ptr<WebStateListObserverBridge> _webStateListObserverBridge;
 }
 
 #pragma mark - Initialization
 
-- (instancetype)initWithWebState:(raw_ptr<web::WebState>)webState {
+- (instancetype)initWithWebStateList:(raw_ptr<WebStateList>)webStateList {
   self = [super init];
   if (self) {
-    CHECK(webState);
-    // TODO(crbug.com/409940117): Ensure the UI reacts to a change in active
-    // WebState e.g. new active WebState or no active WebState.
-    _sourceWebState = webState->GetWeakPtr();
+    CHECK(webStateList);
+    _webStateList = webStateList;
+    _webStateListObserverBridge =
+        std::make_unique<WebStateListObserverBridge>(self);
+    _webStateList->AddObserver(_webStateListObserverBridge.get());
   }
   return self;
 }
@@ -29,16 +36,54 @@
 - (void)setConsumer:(id<ReaderModeConsumer>)consumer {
   CHECK(consumer);
   _consumer = consumer;
-  ReaderModeTabHelper* tabHelper =
-      ReaderModeTabHelper::FromWebState(_sourceWebState.get());
-  CHECK(tabHelper);
-  [self.consumer setContentView:tabHelper->GetReaderModeContentView()];
+  [self updateContentWithNewActiveWebState:_webStateList->GetActiveWebState()];
+}
+
+#pragma mark - WebStateListObserving
+
+- (void)didChangeWebStateList:(WebStateList*)webStateList
+                       change:(const WebStateListChange&)change
+                       status:(const WebStateListStatus&)status {
+  if (status.active_web_state_change()) {
+    [self updateContentWithNewActiveWebState:status.new_active_web_state];
+  }
+}
+
+- (void)webStateListDestroyed:(WebStateList*)webStateList {
+  _webStateList->RemoveObserver(_webStateListObserverBridge.get());
+  _webStateListObserverBridge.reset();
+  _webStateList = nullptr;
 }
 
 #pragma mark - Public
 
 - (void)disconnect {
-  _sourceWebState = nil;
+  if (_webStateList) {
+    _webStateList->RemoveObserver(_webStateListObserverBridge.get());
+    _webStateListObserverBridge.reset();
+    _webStateList = nullptr;
+  }
+}
+
+#pragma mark - Private
+
+// If `activeWebState` is not null, feed the Reader mode content view of
+// `activeWebState` to `consumer`. Otherwise, give `nil` content view to
+// consumer.
+- (void)updateContentWithNewActiveWebState:(web::WebState*)activeWebState {
+  // Remove the old content view.
+  [self.consumer setContentView:nil];
+  // If there is a new content view, feed it to consumer.
+  if (!activeWebState) {
+    return;
+  }
+  ReaderModeTabHelper* readerModeTabHelper =
+      ReaderModeTabHelper::FromWebState(activeWebState);
+  if (!readerModeTabHelper->IsActive()) {
+    return;
+  }
+  [self.consumer
+      setContentView:readerModeTabHelper->GetReaderModeContentView()];
 }
 
 @end
diff --git a/ios/chrome/browser/reader_mode/model/BUILD.gn b/ios/chrome/browser/reader_mode/model/BUILD.gn
index e3dae31..fb707f51 100644
--- a/ios/chrome/browser/reader_mode/model/BUILD.gn
+++ b/ios/chrome/browser/reader_mode/model/BUILD.gn
@@ -6,6 +6,8 @@
 
 source_set("model") {
   sources = [
+    "reader_mode_browser_agent.h",
+    "reader_mode_browser_agent.mm",
     "reader_mode_content_delegate.h",
     "reader_mode_content_tab_helper.h",
     "reader_mode_content_tab_helper.mm",
@@ -19,6 +21,7 @@
     "reader_mode_model_factory.mm",
     "reader_mode_tab_helper.h",
     "reader_mode_tab_helper.mm",
+    "reader_mode_tab_helper_delegate.h",
   ]
   deps = [
     ":constants",
@@ -33,9 +36,11 @@
     "//ios/chrome/app/strings:ios_strings_grit",
     "//ios/chrome/browser/contextual_panel/model:public",
     "//ios/chrome/browser/dom_distiller/model",
+    "//ios/chrome/browser/shared/model/browser",
     "//ios/chrome/browser/shared/model/profile",
     "//ios/chrome/browser/shared/model/profile:profile_keyed_service_factory",
     "//ios/chrome/browser/shared/model/url",
+    "//ios/chrome/browser/shared/model/web_state_list",
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/shared/public/features:system_flags",
     "//ios/chrome/browser/shared/ui/symbols",
@@ -80,6 +85,7 @@
 source_set("unit_tests") {
   testonly = true
   sources = [
+    "reader_mode_browser_agent_unittest.mm",
     "reader_mode_content_tab_helper_unittest.mm",
     "reader_mode_distiller_page_unittest.mm",
     "reader_mode_java_script_feature_unittest.mm",
@@ -99,10 +105,13 @@
     "//components/ukm/ios:ukm_url_recorder",
     "//ios/chrome/browser/contextual_panel/model:public",
     "//ios/chrome/browser/dom_distiller/model",
+    "//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/model/url:constants",
+    "//ios/chrome/browser/shared/model/web_state_list",
+    "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/web/model:feature_flags",
     "//ios/chrome/browser/web/model:web_internal",
     "//ios/chrome/test/fakes",
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_browser_agent.h b/ios/chrome/browser/reader_mode/model/reader_mode_browser_agent.h
new file mode 100644
index 0000000..9ed6255f
--- /dev/null
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_browser_agent.h
@@ -0,0 +1,57 @@
+// 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_READER_MODE_MODEL_READER_MODE_BROWSER_AGENT_H_
+#define IOS_CHROME_BROWSER_READER_MODE_MODEL_READER_MODE_BROWSER_AGENT_H_
+
+#import "base/scoped_observation.h"
+#import "ios/chrome/browser/reader_mode/model/reader_mode_tab_helper_delegate.h"
+#import "ios/chrome/browser/shared/model/browser/browser_user_data.h"
+#import "ios/chrome/browser/shared/model/web_state_list/web_state_list_observer.h"
+#import "ios/web/public/web_state_observer.h"
+
+@protocol ReaderModeCommands;
+class ReaderModeTabHelper;
+class WebStateList;
+
+// Observes the WebStateList of the associated browser and ensures the Reader
+// mode UI is presented and dismissed accordingly when there is a new active
+// WebState or when Reader mode is activated/deactivated in the currently active
+// WebState. Acts as the delegate of the ReaderModeTabHelper in the current
+// active WebState.
+class ReaderModeBrowserAgent : public BrowserUserData<ReaderModeBrowserAgent>,
+                               public WebStateListObserver,
+                               public ReaderModeTabHelperDelegate {
+ public:
+  ReaderModeBrowserAgent(const ReaderModeBrowserAgent&) = delete;
+  ReaderModeBrowserAgent& operator=(const ReaderModeBrowserAgent&) = delete;
+
+  ~ReaderModeBrowserAgent() override;
+
+  // Sets the Reader mode UI handler.
+  void SetReaderModeHandler(id<ReaderModeCommands> reader_mode_handler);
+
+ private:
+  friend class BrowserUserData<ReaderModeBrowserAgent>;
+
+  explicit ReaderModeBrowserAgent(Browser* browser,
+                                  WebStateList* web_state_list);
+
+  // WebStateListObserver methods.
+  void WebStateListDidChange(WebStateList* web_state_list,
+                             const WebStateListChange& change,
+                             const WebStateListStatus& status) override;
+  void WebStateListDestroyed(WebStateList* web_state_list) override;
+
+  // ReaderModeTabHelperDelegate methods.
+  void ReaderModeDidBecomeActive(ReaderModeTabHelper* tab_helper) override;
+  void ReaderModeDidBecomeInactive(ReaderModeTabHelper* tab_helper) override;
+
+  base::ScopedObservation<WebStateList, WebStateListObserver>
+      web_state_list_scoped_observation_{this};
+
+  __weak id<ReaderModeCommands> reader_mode_handler_ = nil;
+};
+
+#endif  // IOS_CHROME_BROWSER_READER_MODE_MODEL_READER_MODE_BROWSER_AGENT_H_
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_browser_agent.mm b/ios/chrome/browser/reader_mode/model/reader_mode_browser_agent.mm
new file mode 100644
index 0000000..ca97a26e
--- /dev/null
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_browser_agent.mm
@@ -0,0 +1,90 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/reader_mode/model/reader_mode_browser_agent.h"
+
+#import "ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.h"
+#import "ios/chrome/browser/shared/public/commands/reader_mode_commands.h"
+
+#pragma mark - Public
+
+ReaderModeBrowserAgent::~ReaderModeBrowserAgent() = default;
+
+void ReaderModeBrowserAgent::SetReaderModeHandler(
+    id<ReaderModeCommands> reader_mode_handler) {
+  reader_mode_handler_ = reader_mode_handler;
+}
+
+#pragma mark - Private
+
+ReaderModeBrowserAgent::ReaderModeBrowserAgent(Browser* browser,
+                                               WebStateList* web_state_list)
+    : BrowserUserData(browser) {
+  web_state_list_scoped_observation_.Observe(web_state_list);
+}
+
+#pragma mark - WebStateListObserver
+
+void ReaderModeBrowserAgent::WebStateListDidChange(
+    WebStateList* web_state_list,
+    const WebStateListChange& change,
+    const WebStateListStatus& status) {
+  if (!status.active_web_state_change()) {
+    // If there is no change in active WebState, do nothing.
+    return;
+  }
+
+  ReaderModeTabHelper* const old_tab_helper =
+      status.old_active_web_state
+          ? ReaderModeTabHelper::FromWebState(status.old_active_web_state)
+          : nullptr;
+  ReaderModeTabHelper* const new_tab_helper =
+      status.new_active_web_state
+          ? ReaderModeTabHelper::FromWebState(status.new_active_web_state)
+          : nullptr;
+
+  if (old_tab_helper) {
+    // If there was an active WebState, remove ourself as delegate of the Reader
+    // mode tab helper and stop observing the WebState.
+    old_tab_helper->SetDelegate(nullptr);
+  }
+  if (new_tab_helper) {
+    // If there is a new active WebState, start observing it and the associated
+    // Reader mode tab helper.
+    new_tab_helper->SetDelegate(this);
+  }
+
+  // Show or hide the Reader mode UI if necessary.
+  const bool old_reader_mode_active =
+      old_tab_helper && old_tab_helper->IsActive();
+  const bool new_reader_mode_active =
+      new_tab_helper && new_tab_helper->IsActive();
+  if (!old_reader_mode_active && new_reader_mode_active) {
+    [reader_mode_handler_ showReaderMode];
+  } else if (old_reader_mode_active && !new_reader_mode_active) {
+    [reader_mode_handler_ hideReaderMode];
+  }
+}
+
+void ReaderModeBrowserAgent::WebStateListDestroyed(
+    WebStateList* web_state_list) {
+  // If the WebStateList is destroyed, stop observing it.
+  web_state_list_scoped_observation_.Reset();
+}
+
+#pragma mark - ReaderModeTabHelperDelegate
+
+void ReaderModeBrowserAgent::ReaderModeDidBecomeActive(
+    ReaderModeTabHelper* tab_helper) {
+  // If Reader mode becomes active in the active WebState, show the Reader mode
+  // UI.
+  [reader_mode_handler_ showReaderMode];
+}
+
+void ReaderModeBrowserAgent::ReaderModeDidBecomeInactive(
+    ReaderModeTabHelper* tab_helper) {
+  // If Reader mode becomes inactive in the active WebState, hide the Reader
+  // mode UI.
+  [reader_mode_handler_ hideReaderMode];
+}
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_browser_agent_unittest.mm b/ios/chrome/browser/reader_mode/model/reader_mode_browser_agent_unittest.mm
new file mode 100644
index 0000000..708c28ef
--- /dev/null
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_browser_agent_unittest.mm
@@ -0,0 +1,118 @@
+// 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/reader_mode/model/reader_mode_browser_agent.h"
+
+#import "ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.h"
+#import "ios/chrome/browser/shared/model/browser/test/test_browser.h"
+#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h"
+#import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h"
+#import "ios/chrome/browser/shared/public/commands/reader_mode_commands.h"
+#import "ios/web/public/test/fakes/fake_web_state.h"
+#import "ios/web/public/test/web_task_environment.h"
+#import "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#import "third_party/ocmock/gtest_support.h"
+
+// Test suite for ReaderModeBrowserAgent.
+class ReaderModeBrowserAgentTest : public PlatformTest {
+ public:
+  void SetUp() override {
+    test_profile_ = TestProfileIOS::Builder().Build();
+    test_browser_ = std::make_unique<TestBrowser>(test_profile_.get());
+    ReaderModeBrowserAgent::CreateForBrowser(test_browser_.get(),
+                                             test_browser_->GetWebStateList());
+    fake_reader_mode_handler_ =
+        OCMStrictProtocolMock(@protocol(ReaderModeCommands));
+    GetReaderModeBrowserAgent()->SetReaderModeHandler(
+        fake_reader_mode_handler_);
+
+    // Initialize the WebStateList.
+    InsertWebState();
+    InsertWebState();
+    InsertWebState();
+    InsertWebState();
+    GetWebStateList()->ActivateWebStateAt(0);
+    ReaderModeTabHelper::FromWebState(GetWebStateList()->GetWebStateAt(1))
+        ->SetActive(true);
+    ReaderModeTabHelper::FromWebState(GetWebStateList()->GetWebStateAt(3))
+        ->SetActive(true);
+  }
+
+  void TearDown() override {
+    GetReaderModeBrowserAgent()->SetReaderModeHandler(nil);
+  }
+
+  // Inserts a FakeWebState with a ReaderModeTabHelper in the WebStateList.
+  void InsertWebState() {
+    std::unique_ptr<web::FakeWebState> web_state =
+        std::make_unique<web::FakeWebState>();
+    ReaderModeTabHelper::CreateForWebState(web_state.get(), nullptr);
+    web_state->SetBrowserState(test_profile_.get());
+    GetWebStateList()->InsertWebState(std::move(web_state));
+  }
+
+  // Returns the browser's WebStateList.
+  WebStateList* GetWebStateList() { return test_browser_->GetWebStateList(); }
+
+  // Returns the active WebState.
+  web::WebState* GetActiveWebState() {
+    return GetWebStateList()->GetActiveWebState();
+  }
+
+  // Returns the ReaderModeBrowserAgent.
+  ReaderModeBrowserAgent* GetReaderModeBrowserAgent() {
+    return ReaderModeBrowserAgent::FromBrowser(test_browser_.get());
+  }
+
+ protected:
+  web::WebTaskEnvironment task_environment_;
+  std::unique_ptr<TestProfileIOS> test_profile_;
+  std::unique_ptr<TestBrowser> test_browser_;
+  id fake_reader_mode_handler_;
+};
+
+// Tests that the Reader mode UI is shown/dismissed when changing the current
+// active WebState in the WebStateList.
+TEST_F(ReaderModeBrowserAgentTest, ChangingActiveWebState) {
+  OCMExpect([fake_reader_mode_handler_ showReaderMode]);
+  GetWebStateList()->ActivateWebStateAt(1);
+  EXPECT_OCMOCK_VERIFY(fake_reader_mode_handler_);
+
+  OCMExpect([fake_reader_mode_handler_ hideReaderMode]);
+  GetWebStateList()->ActivateWebStateAt(0);
+  EXPECT_OCMOCK_VERIFY(fake_reader_mode_handler_);
+
+  OCMExpect([fake_reader_mode_handler_ showReaderMode]);
+  GetWebStateList()->ActivateWebStateAt(3);
+  EXPECT_OCMOCK_VERIFY(fake_reader_mode_handler_);
+
+  OCMExpect([fake_reader_mode_handler_ hideReaderMode]);
+  GetWebStateList()->ActivateWebStateAt(WebStateList::kInvalidIndex);
+  EXPECT_OCMOCK_VERIFY(fake_reader_mode_handler_);
+}
+
+// Tests that the Reader mode UI is not dismissed when moving the active
+// WebState while the Reader mode UI is presented.
+TEST_F(ReaderModeBrowserAgentTest, MovingActiveWebState) {
+  OCMExpect([fake_reader_mode_handler_ showReaderMode]);
+  GetWebStateList()->ActivateWebStateAt(1);
+  EXPECT_OCMOCK_VERIFY(fake_reader_mode_handler_);
+
+  // No call to `hideReaderMode` is expected.
+  GetWebStateList()->MoveWebStateAt(1, 0);
+  EXPECT_OCMOCK_VERIFY(fake_reader_mode_handler_);
+}
+
+// Tests that the Reader mode UI is shown/dismissed when Reader mode is
+// activated/deactivated in the currently active WebState.
+TEST_F(ReaderModeBrowserAgentTest, ChangingReaderModeStatus) {
+  OCMExpect([fake_reader_mode_handler_ showReaderMode]);
+  ReaderModeTabHelper::FromWebState(GetActiveWebState())->SetActive(true);
+  EXPECT_OCMOCK_VERIFY(fake_reader_mode_handler_);
+
+  OCMExpect([fake_reader_mode_handler_ hideReaderMode]);
+  ReaderModeTabHelper::FromWebState(GetActiveWebState())->SetActive(false);
+  EXPECT_OCMOCK_VERIFY(fake_reader_mode_handler_);
+}
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_model.mm b/ios/chrome/browser/reader_mode/model/reader_mode_model.mm
index e823275f..e4dd74c 100644
--- a/ios/chrome/browser/reader_mode/model/reader_mode_model.mm
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_model.mm
@@ -22,7 +22,8 @@
     FetchConfigurationForWebStateCallback callback) {
   ReaderModeTabHelper* reader_mode_tab_helper =
       ReaderModeTabHelper::FromWebState(web_state);
-  if (!reader_mode_tab_helper->CurrentPageSupportsReaderMode()) {
+  if (!reader_mode_tab_helper ||
+      !reader_mode_tab_helper->CurrentPageSupportsReaderMode()) {
     base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
         FROM_HERE, base::BindOnce(std::move(callback), std::move(nullptr)));
     return;
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_model_unittest.mm b/ios/chrome/browser/reader_mode/model/reader_mode_model_unittest.mm
index 1ce9c58..12d2b6c 100644
--- a/ios/chrome/browser/reader_mode/model/reader_mode_model_unittest.mm
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_model_unittest.mm
@@ -20,6 +20,10 @@
     PlatformTest::SetUp();
     profile_ = TestProfileIOS::Builder().Build();
     web_state_ = std::make_unique<web::FakeWebState>();
+  }
+
+  // Attaches a ReaderModeTabHelper to `web_state_`.
+  void AttachReaderModeTabHelper() {
     ReaderModeTabHelper::CreateForWebState(
         web_state_.get(),
         DistillerServiceFactory::GetForProfile(profile_.get()));
@@ -33,6 +37,7 @@
 
 // NTP should return a null configuration.
 TEST_F(ReaderModeModelTest, FetchConfigurationForNTP) {
+  AttachReaderModeTabHelper();
   ReaderModeModel model;
   __block std::unique_ptr<ContextualPanelItemConfiguration> configuration;
 
@@ -53,6 +58,7 @@
 
 // Non-HTML content should return a null configuration.
 TEST_F(ReaderModeModelTest, FetchConfigurationForNonHTMLContent) {
+  AttachReaderModeTabHelper();
   ReaderModeModel model;
   __block std::unique_ptr<ContextualPanelItemConfiguration> configuration;
 
@@ -73,6 +79,7 @@
 
 // HTML content should return the expected non-null configuration.
 TEST_F(ReaderModeModelTest, FetchConfigurationForHTMLContent) {
+  AttachReaderModeTabHelper();
   ReaderModeModel model;
   __block std::unique_ptr<ContextualPanelItemConfiguration> configuration;
 
@@ -96,3 +103,21 @@
   EXPECT_EQ(configuration->relevance,
             ContextualPanelItemConfiguration::high_relevance);
 }
+
+// WebState without a ReaderModeTabHelper should return a null configuration.
+TEST_F(ReaderModeModelTest, FetchConfigurationWithoutReaderModeTabHelper) {
+  ReaderModeModel model;
+  __block std::unique_ptr<ContextualPanelItemConfiguration> configuration;
+
+  model.FetchConfigurationForWebState(
+      web_state_.get(),
+      base::BindOnce(
+          ^(base::OnceClosure quit_closure,
+            std::unique_ptr<ContextualPanelItemConfiguration> config) {
+            configuration = std::move(config);
+            std::move(quit_closure).Run();
+          },
+          task_environment_.QuitClosure()));
+  task_environment_.RunUntilQuit();
+  EXPECT_EQ(configuration, nullptr);
+}
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.h b/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.h
index c590974..b6c806c 100644
--- a/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.h
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.h
@@ -14,8 +14,8 @@
 #import "ios/web/public/web_state_observer.h"
 #import "ios/web/public/web_state_user_data.h"
 
-@protocol ReaderModeCommands;
 @protocol SnackbarCommands;
+class ReaderModeTabHelperDelegate;
 
 // Observes changes to the web state to perform reader mode operations.
 class ReaderModeTabHelper : public web::WebStateObserver,
@@ -29,6 +29,9 @@
 
   ~ReaderModeTabHelper() override;
 
+  // Sets `delegate_`.
+  void SetDelegate(ReaderModeTabHelperDelegate* delegate);
+
   // Returns whether Reader mode is active in the current tab. If so, the Reader
   // mode UI should be presented.
   bool IsActive() const;
@@ -42,8 +45,6 @@
 
   // Sets the snackbar handler.
   void SetSnackbarHandler(id<SnackbarCommands> snackbar_handler);
-  // Sets the reader mode handler.
-  void SetReaderModeHandler(id<ReaderModeCommands> reader_mode_handler);
 
   // Processes the result of the Reader Mode heuristic trigger that was run on
   // the `url` content.
@@ -62,7 +63,6 @@
       web::WebState* web_state,
       web::PageLoadCompletionStatus load_completion_status) override;
   void WebStateDestroyed(web::WebState* web_state) override;
-  void WasHidden(web::WebState* web_state) override;
 
   // ReaderModeContentDelegate overrides:
   void ReaderModeContentDidCancelRequest(
@@ -76,9 +76,6 @@
  private:
   friend class web::WebStateUserData<ReaderModeTabHelper>;
 
-  // Hides the Reader Mode UI and stops any ongoing distillation tasks.
-  void HideReaderMode();
-
   // Determine if the page load is eligible for triggering the reader mode
   // heuristic.
   bool CanTriggerReaderModeHeuristic();
@@ -93,12 +90,16 @@
       const std::string& title,
       const std::string& csp_nonce);
 
+  // Creates `reader_mode_web_state_` and starts distillation.
+  void CreateReaderModeWebState();
+  // Destroys `reader_mode_web_state_` and stops any ongoing distillation.
+  void DestroyReaderModeWebState();
+
   // Whether Reader mode is active in this tab.
   bool active_ = false;
   // WebState used to render the Reader mode content.
   std::unique_ptr<web::WebState> reader_mode_web_state_;
   id<SnackbarCommands> snackbar_handler_;
-  id<ReaderModeCommands> reader_mode_handler_;
   base::TimeDelta heuristic_latency_;
   base::OneShotTimer trigger_reader_mode_timer_;
 
@@ -107,6 +108,8 @@
 
   std::unique_ptr<OfflinePageDistillerViewer> distiller_viewer_;
 
+  raw_ptr<ReaderModeTabHelperDelegate> delegate_ = nullptr;
+
   base::WeakPtrFactory<ReaderModeTabHelper> weak_ptr_factory_{this};
 };
 
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.mm b/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.mm
index 34b828b..8e019b0 100644
--- a/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.mm
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.mm
@@ -17,6 +17,7 @@
 #import "ios/chrome/browser/reader_mode/model/reader_mode_content_tab_helper.h"
 #import "ios/chrome/browser/reader_mode/model/reader_mode_distiller_page.h"
 #import "ios/chrome/browser/reader_mode/model/reader_mode_java_script_feature.h"
+#import "ios/chrome/browser/reader_mode/model/reader_mode_tab_helper_delegate.h"
 #import "ios/chrome/browser/shared/model/profile/profile_ios.h"
 #import "ios/chrome/browser/shared/model/url/url_util.h"
 #import "ios/chrome/browser/shared/public/commands/reader_mode_commands.h"
@@ -167,6 +168,10 @@
 
 ReaderModeTabHelper::~ReaderModeTabHelper() = default;
 
+void ReaderModeTabHelper::SetDelegate(ReaderModeTabHelperDelegate* delegate) {
+  delegate_ = delegate;
+}
+
 bool ReaderModeTabHelper::IsActive() const {
   return active_;
 }
@@ -179,26 +184,17 @@
   if (active_) {
     // If Reader mode is being activated, create the secondary WebState where
     // the content will be rendered and start distillation.
-    web::WebState::CreateParams create_params = web::WebState::CreateParams(
-        ProfileIOS::FromBrowserState(web_state_->GetBrowserState())
-            ->GetOffTheRecordProfile());
-    reader_mode_web_state_ = web::WebState::Create(create_params);
-    ReaderModeContentTabHelper::CreateForWebState(reader_mode_web_state_.get());
-    ReaderModeContentTabHelper::FromWebState(reader_mode_web_state_.get())
-        ->SetDelegate(this);
-    reader_mode_web_state_->SetWebUsageEnabled(true);
-    // TODO(crbug.com/409940117): Decouple heuristic and distillation.
-    TriggerReaderModeHeuristic();
+    CreateReaderModeWebState();
+    if (delegate_) {
+      delegate_->ReaderModeDidBecomeActive(this);
+    }
   } else {
     // If Reader mode is being deactivated, destroy the secondary WebState and
     // ensure the Reader mode UI is dismissed.
-    // TODO(crbug.com/409940117): Ensure the UI gracefully handles the
-    // destruction of the Reader mode WebState while its view is inside the view
-    // hierarchy.
-    reader_mode_web_state_.reset();
-    // Cancel any ongoing distillation task.
-    distiller_viewer_.reset();
-    HideReaderMode();
+    DestroyReaderModeWebState();
+    if (delegate_) {
+      delegate_->ReaderModeDidBecomeInactive(this);
+    }
   }
 }
 
@@ -221,11 +217,6 @@
   snackbar_handler_ = snackbar_handler;
 }
 
-void ReaderModeTabHelper::SetReaderModeHandler(
-    id<ReaderModeCommands> reader_mode_handler) {
-  reader_mode_handler_ = reader_mode_handler;
-}
-
 void ReaderModeTabHelper::PageLoaded(
     web::WebState* web_state,
     web::PageLoadCompletionStatus load_completion_status) {
@@ -265,22 +256,17 @@
     web::NavigationContext* navigation_context) {
   if (!navigation_context->IsSameDocument() ||
       navigation_context->HasUserGesture()) {
-    HideReaderMode();
+    SetActive(false);
   }
 }
 
 void ReaderModeTabHelper::WebStateDestroyed(web::WebState* web_state) {
   CHECK_EQ(web_state_, web_state);
-  HideReaderMode();
+  SetActive(false);
   web_state_->RemoveObserver(this);
   web_state_ = nullptr;
 }
 
-void ReaderModeTabHelper::WasHidden(web::WebState* web_state) {
-  // Ensure the Reader mode UI is hidden when the tab is hidden.
-  HideReaderMode();
-}
-
 void ReaderModeTabHelper::ReaderModeContentDidCancelRequest(
     ReaderModeContentTabHelper* reader_mode_content_tab_helper,
     NSURLRequest* request,
@@ -340,12 +326,6 @@
   }
 }
 
-void ReaderModeTabHelper::HideReaderMode() {
-  if (IsReaderModeAvailable()) {
-    [reader_mode_handler_ hideReaderMode];
-  }
-}
-
 bool ReaderModeTabHelper::CanTriggerReaderModeHeuristic() {
   if (IsReaderModeAvailable()) {
     return true;
@@ -427,11 +407,31 @@
                                             length:html.length()];
       ReaderModeContentTabHelper::FromWebState(reader_mode_web_state_.get())
           ->LoadContent(content_url, content_data);
-      // Once the Reader mode content is ready, show the Reader mode UI.
-      [reader_mode_handler_ showReaderMode];
     } else {
-      // If the page could not be distilled, ensure Reader mode UI is hidden.
-      HideReaderMode();
+      // If the page could not be distilled, deactivate Reader mode in this tab.
+      SetActive(false);
     }
   }
 }
+
+void ReaderModeTabHelper::CreateReaderModeWebState() {
+  web::WebState::CreateParams create_params = web::WebState::CreateParams(
+      ProfileIOS::FromBrowserState(web_state_->GetBrowserState())
+          ->GetOffTheRecordProfile());
+  reader_mode_web_state_ = web::WebState::Create(create_params);
+  ReaderModeContentTabHelper::CreateForWebState(reader_mode_web_state_.get());
+  ReaderModeContentTabHelper::FromWebState(reader_mode_web_state_.get())
+      ->SetDelegate(this);
+  reader_mode_web_state_->SetWebUsageEnabled(true);
+  // TODO(crbug.com/409940117): Decouple heuristic and distillation.
+  TriggerReaderModeHeuristic();
+}
+
+void ReaderModeTabHelper::DestroyReaderModeWebState() {
+  // TODO(crbug.com/409940117): Ensure the UI gracefully handles the
+  // destruction of the Reader mode WebState while its view is inside the view
+  // hierarchy.
+  reader_mode_web_state_.reset();
+  // Cancel any ongoing distillation task.
+  distiller_viewer_.reset();
+}
diff --git a/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper_delegate.h b/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper_delegate.h
new file mode 100644
index 0000000..f03545a
--- /dev/null
+++ b/ios/chrome/browser/reader_mode/model/reader_mode_tab_helper_delegate.h
@@ -0,0 +1,22 @@
+// 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_READER_MODE_MODEL_READER_MODE_TAB_HELPER_DELEGATE_H_
+#define IOS_CHROME_BROWSER_READER_MODE_MODEL_READER_MODE_TAB_HELPER_DELEGATE_H_
+
+class ReaderModeTabHelper;
+
+// Delegate for ReaderModeTabHelper.
+class ReaderModeTabHelperDelegate {
+ public:
+  // Called when Reader mode became active in this tab.
+  virtual void ReaderModeDidBecomeActive(ReaderModeTabHelper* tab_helper) = 0;
+  // Called when Reader mode became inactive in this tab.
+  virtual void ReaderModeDidBecomeInactive(ReaderModeTabHelper* tab_helper) = 0;
+
+ protected:
+  virtual ~ReaderModeTabHelperDelegate() = default;
+};
+
+#endif  // IOS_CHROME_BROWSER_READER_MODE_MODEL_READER_MODE_TAB_HELPER_DELEGATE_H_
diff --git a/ios/chrome/browser/shared/public/commands/reader_mode_commands.h b/ios/chrome/browser/shared/public/commands/reader_mode_commands.h
index 42ff9af150..5164a6e7 100644
--- a/ios/chrome/browser/shared/public/commands/reader_mode_commands.h
+++ b/ios/chrome/browser/shared/public/commands/reader_mode_commands.h
@@ -5,6 +5,8 @@
 #ifndef IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_READER_MODE_COMMANDS_H_
 #define IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_READER_MODE_COMMANDS_H_
 
+#import <Foundation/Foundation.h>
+
 // Commands protocol to show/hide the Reader mode UI.
 @protocol ReaderModeCommands <NSObject>
 
@@ -14,9 +16,6 @@
 // Hides the Reader mode UI.
 - (void)hideReaderMode;
 
-// Shows the Reader mode UI if currently inactive and vice-versa.
-- (void)toggleReaderMode;
-
 @end
 
 #endif  // IOS_CHROME_BROWSER_SHARED_PUBLIC_COMMANDS_READER_MODE_COMMANDS_H_
diff --git a/ios/chrome/browser/shared/ui/symbols/symbol_helpers.mm b/ios/chrome/browser/shared/ui/symbols/symbol_helpers.mm
index f6fe448..763b25eb 100644
--- a/ios/chrome/browser/shared/ui/symbols/symbol_helpers.mm
+++ b/ios/chrome/browser/shared/ui/symbols/symbol_helpers.mm
@@ -5,6 +5,7 @@
 #import "ios/chrome/browser/shared/ui/symbols/symbol_helpers.h"
 
 #import "base/check.h"
+#import "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/shared/public/features/features.h"
 #import "ios/chrome/browser/shared/ui/symbols/symbol_configurations.h"
 
@@ -34,7 +35,8 @@
                         inBundle:nil
                withConfiguration:configuration];
   }
-  DCHECK(symbol);
+  DCHECK(symbol) << " symbol_name: " << base::SysNSStringToUTF8(symbol_name)
+                 << " is_system_symbol: " << system_symbol;
   return symbol;
 }
 
diff --git a/ios/chrome/browser/supervised_user/model/supervised_user_service_factory.mm b/ios/chrome/browser/supervised_user/model/supervised_user_service_factory.mm
index 94539da8..9daa2102 100644
--- a/ios/chrome/browser/supervised_user/model/supervised_user_service_factory.mm
+++ b/ios/chrome/browser/supervised_user/model/supervised_user_service_factory.mm
@@ -62,6 +62,8 @@
       profile->GetSharedURLLoaderFactory(), CHECK_DEREF(profile->GetPrefs()),
       CHECK_DEREF(SupervisedUserSettingsServiceFactory::GetForProfile(profile)),
       &CHECK_DEREF(SyncServiceFactory::GetForProfile(profile)),
-      std::make_unique<FilterDelegateImpl>(),
+      std::make_unique<supervised_user::SupervisedUserURLFilter>(
+          CHECK_DEREF(profile->GetPrefs()),
+          std::make_unique<FilterDelegateImpl>()),
       std::make_unique<SupervisedUserServicePlatformDelegate>(profile));
 }
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/grid_toolbars_mutator.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/grid_toolbars_mutator.h
index 181aa1c4..ddd1791 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/grid_toolbars_mutator.h
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/grid_toolbars_mutator.h
@@ -26,6 +26,11 @@
 // enable the saved configuration.
 - (void)setButtonsEnabled:(BOOL)enabled;
 
+// Sends YES to hide toolbars on the incognito page on the tab switcher when
+// authentication is required to give the full screen blur effect, NO to reshow
+// toolbars.
+- (void)setIncognitoToolbarsBackgroundHidden:(BOOL)hidden;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_TAB_SWITCHER_UI_BUNDLED_TAB_GRID_GRID_GRID_TOOLBARS_MUTATOR_H_
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/incognito/BUILD.gn b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/incognito/BUILD.gn
index b827b18..4eaf0da5 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/incognito/BUILD.gn
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/incognito/BUILD.gn
@@ -22,6 +22,7 @@
     "//components/supervised_user/core/common",
     "//components/supervised_user/core/common:features",
     "//ios/chrome/browser/feature_engagement/model",
+    "//ios/chrome/browser/incognito_reauth/ui_bundled:features",
     "//ios/chrome/browser/incognito_reauth/ui_bundled:incognito_reauth_scene_agent",
     "//ios/chrome/browser/incognito_reauth/ui_bundled:incognito_reauth_util",
     "//ios/chrome/browser/policy/model:policy_util",
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/incognito/incognito_grid_mediator.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/incognito/incognito_grid_mediator.mm
index 0f5e83c..7176903 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/incognito/incognito_grid_mediator.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/incognito/incognito_grid_mediator.mm
@@ -14,6 +14,7 @@
 #import "components/signin/public/identity_manager/identity_manager.h"
 #import "components/signin/public/identity_manager/tribool.h"
 #import "components/supervised_user/core/browser/family_link_user_capabilities.h"
+#import "ios/chrome/browser/incognito_reauth/ui_bundled/features.h"
 #import "ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_constants.h"
 #import "ios/chrome/browser/incognito_reauth/ui_bundled/incognito_reauth_scene_agent.h"
 #import "ios/chrome/browser/policy/model/policy_util.h"
@@ -158,6 +159,10 @@
 
   BOOL authenticationRequired = self.reauthSceneAgent.authenticationRequired;
   if (_incognitoDisabled || authenticationRequired) {
+    if (IsIOSSoftLockEnabled()) {
+      [self.toolbarsMutator
+          setIncognitoToolbarsBackgroundHidden:authenticationRequired];
+    }
     [self.toolbarsMutator
         setToolbarConfiguration:
             [TabGridToolbarsConfiguration
@@ -236,9 +241,9 @@
 
 - (void)reauthAgent:(IncognitoReauthSceneAgent*)agent
     didUpdateIncognitoLockState:(IncognitoLockState)incogitoLockState {
-  [self reauthAgent:agent
-      didUpdateAuthenticationRequirement:incogitoLockState !=
-                                         IncognitoLockState::kNone];
+  BOOL reauthRequired = incogitoLockState != IncognitoLockState::kNone;
+  [self reauthAgent:agent didUpdateAuthenticationRequirement:reauthRequired];
+  [self.toolbarsMutator setIncognitoToolbarsBackgroundHidden:reauthRequired];
 }
 
 #pragma mark - FamilyLinkUserCapabilitiesObserving
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/pinned_tabs/tests/pinned_tabs_drag_drop_egtest.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/pinned_tabs/tests/pinned_tabs_drag_drop_egtest.mm
index 0720f78..2a015cd2 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/pinned_tabs/tests/pinned_tabs_drag_drop_egtest.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/pinned_tabs/tests/pinned_tabs_drag_drop_egtest.mm
@@ -95,7 +95,7 @@
   // The pinned view is hidden when there is no pinned tabs. We can't determine
   // its position precisely.
   XCUICoordinate* end_point =
-      [dst_element coordinateWithNormalizedOffset:CGVectorMake(-0.8, -0.5)];
+      [dst_element coordinateWithNormalizedOffset:CGVectorMake(-1.5, -0.5)];
 
   [start_point pressForDuration:1.5
            thenDragToCoordinate:end_point
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_constants.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_constants.h
index bd7c610..6279ae35 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_constants.h
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_constants.h
@@ -70,8 +70,8 @@
 extern const CGFloat kTabGridEmptyStateHorizontalInset;
 
 // The insets from the edges for the floating button.
-extern const CGFloat kTabGridFloatingButtonVerticalInset;
-extern const CGFloat kTabGridFloatingButtonHorizontalInset;
+extern const CGFloat kTabGridFloatingButtonInset;
+extern const CGFloat kTabGridFloatingButtonInsetIPad;
 
 // Intrinsic heights of the tab grid toolbars.
 extern const CGFloat kTabGridTopToolbarHeight;
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_constants.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_constants.mm
index 4d11842..111b108 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_constants.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_constants.mm
@@ -79,8 +79,8 @@
 extern const CGFloat kTabGridEmptyStateHorizontalInset = 80.0f;
 
 // The insets from the edges for the floating button.
-const CGFloat kTabGridFloatingButtonVerticalInset = 28.0f;
-const CGFloat kTabGridFloatingButtonHorizontalInset = 20.0f;
+const CGFloat kTabGridFloatingButtonInset = 10.0f;
+const CGFloat kTabGridFloatingButtonInsetIPad = 20.0f;
 
 // The Search bar original width ratio of the available space from the
 // containing toolbar before any width modifiers.
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_view_controller.mm
index f84a1017..006caf42 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_view_controller.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_grid_view_controller.mm
@@ -794,6 +794,8 @@
   // Otherwise don't.
   if (!self.viewVisible || !animated) {
     [self.scrollView setContentOffset:targetOffset animated:NO];
+    [self.topToolbar setBackgroundContentOffset:targetOffset animated:NO];
+    [self.bottomToolbar setBackgroundContentOffset:targetOffset animated:NO];
     self.currentPage = targetPage;
     // Important updates (e.g., button configurations, incognito visibility) are
     // made at the end of scrolling animations after `self.currentPage` is set.
@@ -806,6 +808,8 @@
     if (scrolled) {
       self.scrollViewAnimatingContentOffset = YES;
       [self.scrollView setContentOffset:targetOffset animated:YES];
+      [self.topToolbar setBackgroundContentOffset:targetOffset animated:YES];
+      [self.bottomToolbar setBackgroundContentOffset:targetOffset animated:YES];
       // `self.currentPage` is set in scrollViewDidEndScrollingAnimation:
     } else {
       self.currentPage = targetPage;
@@ -862,6 +866,7 @@
   UIScrollView* scrollView = [[UIScrollView alloc] init];
   scrollView.translatesAutoresizingMaskIntoConstraints = NO;
   scrollView.pagingEnabled = YES;
+  scrollView.showsHorizontalScrollIndicator = NO;
   scrollView.delegate = self;
   // Ensures that scroll view does not add additional margins based on safe
   // areas.
@@ -1891,6 +1896,8 @@
   // scroll width.
   contentOffset.x = offsetWidth * offset;
   self.scrollView.contentOffset = contentOffset;
+  [self.topToolbar setBackgroundContentOffset:contentOffset animated:NO];
+  [self.bottomToolbar setBackgroundContentOffset:contentOffset animated:NO];
 }
 
 - (void)pageControlChangedPageByDrag:(id)sender {
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_groups_panel_coordinator_unittest.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_groups_panel_coordinator_unittest.mm
index 313851db..de805a7 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_groups_panel_coordinator_unittest.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/tab_groups/tab_groups_panel_coordinator_unittest.mm
@@ -77,6 +77,10 @@
   // No-op.
 }
 
+- (void)setIncognitoToolbarsBackgroundHidden:(BOOL)hidden {
+  // No-op.
+}
+
 @end
 
 class TabGroupsPanelCoordinatorTest : public PlatformTest {
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.h
index c8889a1..986c3efc 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.h
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.h
@@ -93,6 +93,8 @@
 // Sets the toolbar background offset to match the content scroll view offset.
 - (void)setBackgroundContentOffset:(CGPoint)backgroundContentOffset
                           animated:(BOOL)animated;
+// Sets whether the incognito toolbar background should be hidden.
+- (void)setIncognitoBackgroundHidden:(BOOL)hidden;
 
 @end
 
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm
index a44cf72..8683cb1e 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_bottom_toolbar.mm
@@ -23,6 +23,7 @@
 #import "ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbars_utils.h"
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
 #import "ios/chrome/grit/ios_strings.h"
+#import "ui/base/device_form_factor.h"
 #import "ui/base/l10n/l10n_util.h"
 
 @implementation TabGridBottomToolbar {
@@ -31,7 +32,6 @@
   UIBarButtonItem* _spaceItem;
   NSArray<NSLayoutConstraint*>* _compactConstraints;
   NSArray<NSLayoutConstraint*>* _floatingConstraints;
-  NSLayoutConstraint* _largeNewTabButtonBottomAnchor;
   TabGridNewTabButton* _smallNewTabButton;
   TabGridNewTabButton* _largeNewTabButton;
   UIBarButtonItem* _doneButton;
@@ -172,6 +172,11 @@
   _closeAllOrUndoButton.enabled = enabled;
 }
 
+- (void)setIncognitoBackgroundHidden:(BOOL)hidden {
+  [_scrollBackgroundView hideIncognitoToolbarBackground:hidden];
+  [self updateBackgroundVisibility];
+}
+
 - (void)useUndoCloseAll:(BOOL)useUndo {
   _closeAllOrUndoButton.enabled = YES;
   if (useUndo) {
@@ -372,18 +377,37 @@
   _largeNewTabButton.translatesAutoresizingMaskIntoConstraints = NO;
   _largeNewTabButton.page = self.page;
 
-  CGFloat floatingButtonVerticalInset = kTabGridFloatingButtonVerticalInset;
+  // Try to force the button to be aligned with the safe area. Lower priority to
+  // avoid clashing with the constraints with the actual sides when there is no
+  // safe area.
+  NSLayoutConstraint* largeButtonTrailingSafeAreaConstraint =
+      [_largeNewTabButton.trailingAnchor
+          constraintEqualToAnchor:self.safeAreaLayoutGuide.trailingAnchor];
+  largeButtonTrailingSafeAreaConstraint.priority = UILayoutPriorityDefaultHigh;
+  NSLayoutConstraint* largeButtonBottomSafeAreaConstraint =
+      [_largeNewTabButton.bottomAnchor
+          constraintEqualToAnchor:self.safeAreaLayoutGuide.bottomAnchor];
+  largeButtonBottomSafeAreaConstraint.priority = UILayoutPriorityDefaultHigh;
 
-  _largeNewTabButtonBottomAnchor = [_largeNewTabButton.bottomAnchor
-      constraintEqualToAnchor:self.safeAreaLayoutGuide.bottomAnchor
-                     constant:-floatingButtonVerticalInset];
+  CGFloat largeButtonHorizontalInset =
+      ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET
+          ? kTabGridFloatingButtonInsetIPad
+          : kTabGridFloatingButtonInset;
+  CGFloat largeButtonVerticalInset =
+      ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET
+          ? kTabGridFloatingButtonInsetIPad
+          : kTabGridFloatingButtonInset;
 
   _floatingConstraints = @[
     [_largeNewTabButton.topAnchor constraintEqualToAnchor:self.topAnchor],
-    _largeNewTabButtonBottomAnchor,
+    [_largeNewTabButton.bottomAnchor
+        constraintLessThanOrEqualToAnchor:self.bottomAnchor
+                                 constant:-largeButtonVerticalInset],
+    largeButtonBottomSafeAreaConstraint,
     [_largeNewTabButton.trailingAnchor
-        constraintEqualToAnchor:self.trailingAnchor
-                       constant:-kTabGridFloatingButtonHorizontalInset],
+        constraintLessThanOrEqualToAnchor:self.trailingAnchor
+                                 constant:-largeButtonHorizontalInset],
+    largeButtonTrailingSafeAreaConstraint,
   ];
 
   _newTabButtonItem.title = _largeNewTabButton.accessibilityLabel;
@@ -406,8 +430,6 @@
     [self updateBackgroundVisibility];
     return;
   }
-  _largeNewTabButtonBottomAnchor.constant =
-      -kTabGridFloatingButtonVerticalInset;
 
   if (self.mode == TabGridMode::kSelection) {
     [NSLayoutConstraint deactivateConstraints:_floatingConstraints];
@@ -467,8 +489,7 @@
 
 // Returns YES if the `_largeNewTabButton` is showing on the toolbar.
 - (BOOL)isShowingFloatingButton {
-  return _largeNewTabButton.superview &&
-         _largeNewTabButtonBottomAnchor.isActive;
+  return _largeNewTabButton.superview;
 }
 
 // Returns YES if should use compact bottom toolbar layout.
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_new_tab_button.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_new_tab_button.mm
index b4f3536..a1e0fd17 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_new_tab_button.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_new_tab_button.mm
@@ -13,31 +13,63 @@
 #import "ios/chrome/common/ui/util/constraints_ui_util.h"
 #import "ios/chrome/common/ui/util/pointer_interaction_util.h"
 #import "ios/chrome/grit/ios_strings.h"
+#import "ui/base/device_form_factor.h"
 #import "ui/base/l10n/l10n_util.h"
 
 namespace {
 
 // The size of the small symbol image.
 const CGFloat kSmallSymbolSize = 24;
+// Size of the button when using a large symbol.
+const CGFloat kSmallSize = 38;
 // The size of the large symbol image.
-const CGFloat kLargeSymbolSize = 37;
+const CGFloat kLargeSymbolSize = 28;
+// Size of the button when using a large symbol.
+const CGFloat kLargeSize = 44;
+// The size of the large symbol image.
+const CGFloat kLargeSymbolSizeIPad = 34;
+// Size of the button when using a large symbol.
+const CGFloat kLargeSizeIPad = 52;
 
 }  // namespace
 
-@interface TabGridNewTabButton ()
-
-@property(nonatomic, strong) UIImage* symbol;
-
-@end
-
-@implementation TabGridNewTabButton
+@implementation TabGridNewTabButton {
+  // The symbol for this button.
+  UIImage* _symbol;
+  // The image container, centered with the button. Not using the image of the
+  // button to avoid alignment issues.
+  UIImageView* _imageContainer;
+}
 
 - (instancetype)initWithLargeSize:(BOOL)largeSize {
   self = [super initWithFrame:CGRectZero];
   if (self) {
-    CGFloat symbolSize = largeSize ? kLargeSymbolSize : kSmallSymbolSize;
+    CGFloat symbolSize;
+    CGFloat buttonSize;
+    if (largeSize) {
+      if (ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) {
+        symbolSize = kLargeSymbolSizeIPad;
+        buttonSize = kLargeSizeIPad;
+      } else {
+        symbolSize = kLargeSymbolSize;
+        buttonSize = kLargeSize;
+      }
+    } else {
+      symbolSize = kSmallSymbolSize;
+      buttonSize = kSmallSize;
+    }
     _symbol = CustomSymbolWithPointSize(kPlusCircleFillSymbol, symbolSize);
-    [self setImage:_symbol forState:UIControlStateNormal];
+
+    _imageContainer = [[UIImageView alloc] initWithImage:_symbol];
+    _imageContainer.translatesAutoresizingMaskIntoConstraints = NO;
+    [self addSubview:_imageContainer];
+
+    AddSameCenterConstraints(self, _imageContainer);
+
+    [NSLayoutConstraint activateConstraints:@[
+      [self.heightAnchor constraintEqualToConstant:buttonSize],
+      [self.widthAnchor constraintEqualToAnchor:self.heightAnchor],
+    ]];
     self.pointerInteractionEnabled = YES;
     self.pointerStyleProvider = CreateLiftEffectCirclePointerStyleProvider();
   }
@@ -58,21 +90,15 @@
     case TabGridPageIncognitoTabs:
       self.accessibilityLabel =
           l10n_util::GetNSString(IDS_IOS_TAB_GRID_CREATE_NEW_INCOGNITO_TAB);
-      [self
-          setImage:SymbolWithPalette(
-                       self.symbol, @[ UIColor.blackColor, UIColor.whiteColor ])
-          forState:UIControlStateNormal];
+      _imageContainer.image = SymbolWithPalette(
+          _symbol, @[ UIColor.blackColor, UIColor.whiteColor ]);
       break;
     case TabGridPageRegularTabs:
       self.accessibilityLabel =
           l10n_util::GetNSString(IDS_IOS_TAB_GRID_CREATE_NEW_TAB);
-      [self
-          setImage:SymbolWithPalette(self.symbol,
-                                     @[
-                                       UIColor.blackColor,
-                                       [UIColor colorNamed:kStaticBlue400Color]
-                                     ])
-          forState:UIControlStateNormal];
+      _imageContainer.image = SymbolWithPalette(
+          _symbol,
+          @[ UIColor.blackColor, [UIColor colorNamed:kStaticBlue400Color] ]);
       break;
     case TabGridPageRemoteTabs:
     case TabGridPageTabGroups:
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbar_scrolling_background.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbar_scrolling_background.h
index 94745dfb..3daac1e5 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbar_scrolling_background.h
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbar_scrolling_background.h
@@ -18,7 +18,8 @@
 - (void)updateBackgroundsForPage:(TabGridPage)page
             scrolledToEdgeHidden:(BOOL)scrolledToEdge
     scrolledBackgroundViewHidden:(BOOL)scrolledBackgroundViewHidden;
-
+// Hides incognito toolbar according to `hidden` by setting the alpha value.
+- (void)hideIncognitoToolbarBackground:(BOOL)hidden;
 @end
 
 #endif  // IOS_CHROME_BROWSER_TAB_SWITCHER_UI_BUNDLED_TAB_GRID_TOOLBARS_TAB_GRID_TOOLBAR_SCROLLING_BACKGROUND_H_
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbar_scrolling_background.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbar_scrolling_background.mm
index bc2c3da..befda95e 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbar_scrolling_background.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbar_scrolling_background.mm
@@ -89,4 +89,8 @@
   }
 }
 
+- (void)hideIncognitoToolbarBackground:(BOOL)hidden {
+  _incognitoTabsBackground.alpha = hidden ? 0 : 1;
+}
+
 @end
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbars_mediator.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbars_mediator.mm
index dff48e1..768f44f 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbars_mediator.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_toolbars_mediator.mm
@@ -125,6 +125,11 @@
   }
 }
 
+- (void)setIncognitoToolbarsBackgroundHidden:(BOOL)hidden {
+  [self.topToolbarConsumer setIncognitoBackgroundHidden:hidden];
+  [self.bottomToolbarConsumer setIncognitoBackgroundHidden:hidden];
+}
+
 #pragma mark - WebStateListObserving
 
 - (void)didChangeWebStateList:(WebStateList*)webStateList
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.h
index b33c11b..382d5dd 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.h
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.h
@@ -88,6 +88,8 @@
 // Sets the toolbar background offset to match the content scroll view offset.
 - (void)setBackgroundContentOffset:(CGPoint)backgroundContentOffset
                           animated:(BOOL)animated;
+// Sets whether the incognito toolbar background should be hidden.
+- (void)setIncognitoBackgroundHidden:(BOOL)hidden;
 
 @end
 
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm
index 11c7f1e87c..b0952d1 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/tab_grid_top_toolbar.mm
@@ -167,6 +167,10 @@
   _doneButton.enabled = enabled;
 }
 
+- (void)setIncognitoBackgroundHidden:(BOOL)hidden {
+  [_scrollBackgroundView hideIncognitoToolbarBackground:hidden];
+}
+
 - (void)useUndoCloseAll:(BOOL)useUndo {
   _closeAllOrUndoButton.enabled = YES;
   if (useUndo) {
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/test/fake_tab_grid_toolbars_mediator.h b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/test/fake_tab_grid_toolbars_mediator.h
index e3b716a..bdf19e0 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/test/fake_tab_grid_toolbars_mediator.h
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/test/fake_tab_grid_toolbars_mediator.h
@@ -19,6 +19,7 @@
 @property(nonatomic, strong) TabGridToolbarsConfiguration* configuration;
 @property(nonatomic, weak) id<TabGridToolbarsGridDelegate> delegate;
 @property(nonatomic, assign) BOOL enabled;
+@property(nonatomic, assign) BOOL incognitoHidden;
 
 @end
 
diff --git a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/test/fake_tab_grid_toolbars_mediator.mm b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/test/fake_tab_grid_toolbars_mediator.mm
index 72128a2c..39924a4 100644
--- a/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/test/fake_tab_grid_toolbars_mediator.mm
+++ b/ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/toolbars/test/fake_tab_grid_toolbars_mediator.mm
@@ -18,4 +18,8 @@
   self.enabled = enabled;
 }
 
+- (void)setIncognitoToolbarsBackgroundHidden:(BOOL)hidden {
+  self.incognitoHidden = hidden;
+}
+
 @end
diff --git a/ios/chrome/browser/widget_kit/model/BUILD.gn b/ios/chrome/browser/widget_kit/model/BUILD.gn
index ab6243a4..c78adb5 100644
--- a/ios/chrome/browser/widget_kit/model/BUILD.gn
+++ b/ios/chrome/browser/widget_kit/model/BUILD.gn
@@ -23,6 +23,7 @@
     deps = [
       ":model_swift",
       "//base",
+      "//ios/chrome/common/app_group",
     ]
     frameworks = [ "Foundation.framework" ]
   }
diff --git a/ios/chrome/browser/widget_kit/model/widget_metrics_util.h b/ios/chrome/browser/widget_kit/model/widget_metrics_util.h
index 25e990b..8d9144c 100644
--- a/ios/chrome/browser/widget_kit/model/widget_metrics_util.h
+++ b/ios/chrome/browser/widget_kit/model/widget_metrics_util.h
@@ -11,6 +11,8 @@
 
 + (void)logInstalledWidgets;
 
++ (void)logWidgetDeletedUiCount;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_WIDGET_KIT_MODEL_WIDGET_METRICS_UTIL_H_
diff --git a/ios/chrome/browser/widget_kit/model/widget_metrics_util.mm b/ios/chrome/browser/widget_kit/model/widget_metrics_util.mm
index e1fcb1e..97eb4c1 100644
--- a/ios/chrome/browser/widget_kit/model/widget_metrics_util.mm
+++ b/ios/chrome/browser/widget_kit/model/widget_metrics_util.mm
@@ -8,6 +8,7 @@
 #import "base/notreached.h"
 #import "base/strings/sys_string_conversions.h"
 #import "ios/chrome/browser/widget_kit/model/model_swift.h"
+#import "ios/chrome/common/app_group/app_group_constants.h"
 
 using base::UmaHistogramEnumeration;
 
@@ -31,6 +32,15 @@
   kMaxValue = kSearchPasswords,
 };
 
+// Values of the UMA IOS.WidgetsForMIM.DeletedAccountUiDisplayed histogram. Must
+// be kept up to date with IOSWidgetKitExtensionKind in enums.xml. These values
+// are persisted to logs. Entries should not be renumbered and numeric values
+// should never be reused.
+enum class WidgetKitDeletedUiCount {
+  kDeletedUiDisplayed = 0,
+  kMaxValue = kDeletedUiDisplayed,
+};
+
 WidgetKitExtensionKind UMAKindForWidgetKind(NSString* kind) {
   // TODO(crbug.com/40725610): Share this names in a constant file everywhere
   // they are used. Currently names matches the declared names in each widget
@@ -89,4 +99,14 @@
   [WidgetsMetricLogger logInstalledWidgets];
 }
 
++ (void)logWidgetDeletedUiCount {
+  NSUserDefaults* shared_defaults = app_group::GetGroupUserDefaults();
+  bool ui_presented = [shared_defaults boolForKey:@"DeletedAccountUiDisplayed"];
+  if (ui_presented) {
+    base::UmaHistogramEnumeration("IOS.WidgetsForMIM.DeletedAccountUiDisplayed",
+                                  WidgetKitDeletedUiCount::kDeletedUiDisplayed);
+    [shared_defaults setBool:false forKey:@"DeletedAccountUiDisplayed"];
+  }
+}
+
 @end
diff --git a/ios/chrome/test/fakes/BUILD.gn b/ios/chrome/test/fakes/BUILD.gn
index 6168b71..78202cc 100644
--- a/ios/chrome/test/fakes/BUILD.gn
+++ b/ios/chrome/test/fakes/BUILD.gn
@@ -10,6 +10,8 @@
     "fake_ar_quick_look_tab_helper_delegate.mm",
     "fake_contained_presenter.h",
     "fake_contained_presenter.mm",
+    "fake_discover_feed_eligibility_handler.h",
+    "fake_discover_feed_eligibility_handler.mm",
     "fake_download_manager_consumer.h",
     "fake_download_manager_consumer.mm",
     "fake_download_manager_tab_helper_delegate.h",
@@ -32,6 +34,7 @@
     "//base",
     "//components/language/ios/browser",
     "//components/translate/core/common",
+    "//ios/chrome/browser/discover_feed/model",
     "//ios/chrome/browser/download/model",
     "//ios/chrome/browser/download/ui",
     "//ios/chrome/browser/overscroll_actions/ui_bundled",
@@ -41,6 +44,7 @@
     "//ios/chrome/browser/shared/public/commands",
     "//ios/chrome/browser/store_kit/model",
     "//ios/chrome/browser/web/model:web_internal",
+    "//ios/public/provider/chrome/browser/discover_feed:discover_feed_api",
     "//ios/web/public",
     "//ios/web/public/download",
   ]
diff --git a/ios/chrome/test/fakes/fake_discover_feed_eligibility_handler.h b/ios/chrome/test/fakes/fake_discover_feed_eligibility_handler.h
new file mode 100644
index 0000000..63f2180
--- /dev/null
+++ b/ios/chrome/test/fakes/fake_discover_feed_eligibility_handler.h
@@ -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.
+
+#ifndef IOS_CHROME_TEST_FAKES_FAKE_DISCOVER_FEED_ELIGIBILITY_HANDLER_H_
+#define IOS_CHROME_TEST_FAKES_FAKE_DISCOVER_FEED_ELIGIBILITY_HANDLER_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/public/provider/chrome/browser/discover_feed/discover_feed_visibility_provider.h"
+
+enum class DiscoverFeedEligibility;
+
+/// Fake eligibility handler for Discover feed for testing use.
+@interface FakeDiscoverFeedEligibilityHandler
+    : NSObject <DiscoverFeedVisibilityProvider>
+
+/// Sets eligibilty; should be called by the test case to verify eligibility
+/// change behavior.
+- (void)setEligibility:(DiscoverFeedEligibility)eligibility;
+
+/// Number of observers registered.
+- (int)observerCount;
+
+/// Returns `YES` if `-shutdown` is invoked.
+- (BOOL)isShutdown;
+
+@end
+
+#endif  // IOS_CHROME_TEST_FAKES_FAKE_DISCOVER_FEED_ELIGIBILITY_HANDLER_H_
diff --git a/ios/chrome/test/fakes/fake_discover_feed_eligibility_handler.mm b/ios/chrome/test/fakes/fake_discover_feed_eligibility_handler.mm
new file mode 100644
index 0000000..ee1d45b9
--- /dev/null
+++ b/ios/chrome/test/fakes/fake_discover_feed_eligibility_handler.mm
@@ -0,0 +1,68 @@
+// 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/test/fakes/fake_discover_feed_eligibility_handler.h"
+
+#import "ios/chrome/browser/discover_feed/model/discover_feed_visibility_observer.h"
+#import "ios/chrome/browser/discover_feed/model/discover_feed_visibility_provider_configuration.h"
+#import "ios/chrome/browser/discover_feed/model/feed_constants.h"
+
+@implementation FakeDiscoverFeedEligibilityHandler {
+  DiscoverFeedEligibility _eligibility;
+  BOOL _shutdown;
+  int _observerCount;
+}
+
+/// Property is declared in a protocol and not autosynthesized.
+@synthesize enabled = _enabled;
+
+- (instancetype)init {
+  self = [super init];
+  if (self) {
+    _enabled = YES;
+    _eligibility = DiscoverFeedEligibility::kEligible;
+    _observerCount = 0;
+  }
+  return self;
+}
+
+- (void)setEligibility:(DiscoverFeedEligibility)eligibility {
+  _eligibility = eligibility;
+}
+
+- (int)observerCount {
+  return _observerCount;
+}
+
+- (BOOL)isShutdown {
+  return _shutdown;
+}
+
+- (void)setEnabled:(BOOL)enabled {
+  _enabled = enabled;
+}
+
+#pragma mark - DiscoverFeedVisibilityProvider
+
+- (DiscoverFeedEligibility)eligibility {
+  return _eligibility;
+}
+
+- (void)addObserver:(id<DiscoverFeedVisibilityObserver>)observer {
+  if (observer) {
+    _observerCount++;
+  }
+}
+
+- (void)removeObserver:(id<DiscoverFeedVisibilityObserver>)observer {
+  if (observer) {
+    _observerCount--;
+  }
+}
+
+- (void)shutdown {
+  _shutdown = YES;
+}
+
+@end
diff --git a/ios/chrome/test/providers/discover_feed/BUILD.gn b/ios/chrome/test/providers/discover_feed/BUILD.gn
index d540a83..b6a3ff8c 100644
--- a/ios/chrome/test/providers/discover_feed/BUILD.gn
+++ b/ios/chrome/test/providers/discover_feed/BUILD.gn
@@ -9,6 +9,8 @@
     "test_discover_feed_service.h",
     "test_discover_feed_service.mm",
   ]
-  deps =
-      [ "//ios/public/provider/chrome/browser/discover_feed:discover_feed_api" ]
+  deps = [
+    "//ios/chrome/test/fakes",
+    "//ios/public/provider/chrome/browser/discover_feed:discover_feed_api",
+  ]
 }
diff --git a/ios/chrome/test/providers/discover_feed/test_discover_feed.mm b/ios/chrome/test/providers/discover_feed/test_discover_feed.mm
index a163cac..9ef61f4 100644
--- a/ios/chrome/test/providers/discover_feed/test_discover_feed.mm
+++ b/ios/chrome/test/providers/discover_feed/test_discover_feed.mm
@@ -4,7 +4,10 @@
 
 #import "ios/public/provider/chrome/browser/discover_feed/discover_feed_api.h"
 
+#import "ios/chrome/browser/discover_feed/model/discover_feed_visibility_provider_configuration.h"
+#import "ios/chrome/test/fakes/fake_discover_feed_eligibility_handler.h"
 #import "ios/chrome/test/providers/discover_feed/test_discover_feed_service.h"
+#import "ios/public/provider/chrome/browser/discover_feed/discover_feed_visibility_provider.h"
 
 namespace ios {
 namespace provider {
@@ -14,5 +17,20 @@
   return std::make_unique<TestDiscoverFeedService>();
 }
 
+id<DiscoverFeedVisibilityProvider> CreateDiscoverFeedVisibilityProvider(
+    DiscoverFeedVisibilityProviderConfiguration* configuration) {
+  DiscoverFeedService* discover_feed_service =
+      configuration.discoverFeedService;
+  if (!discover_feed_service) {
+    return nil;
+  }
+  TestDiscoverFeedService* test_discover_feed_service =
+      static_cast<TestDiscoverFeedService*>(discover_feed_service);
+  FakeDiscoverFeedEligibilityHandler* handler =
+      [[FakeDiscoverFeedEligibilityHandler alloc] init];
+  test_discover_feed_service->set_eligibility_handler(handler);
+  return handler;
+}
+
 }  // namespace provider
 }  // namespace ios
diff --git a/ios/chrome/test/providers/discover_feed/test_discover_feed_service.h b/ios/chrome/test/providers/discover_feed/test_discover_feed_service.h
index 742c4176..236e9a1 100644
--- a/ios/chrome/test/providers/discover_feed/test_discover_feed_service.h
+++ b/ios/chrome/test/providers/discover_feed/test_discover_feed_service.h
@@ -7,6 +7,8 @@
 
 #import "ios/chrome/browser/discover_feed/model/discover_feed_service.h"
 
+@class FakeDiscoverFeedEligibilityHandler;
+
 // Dummy DiscoverFeedService implementation used for tests.
 class TestDiscoverFeedService final : public DiscoverFeedService {
  public:
@@ -36,13 +38,16 @@
   void HandleBackgroundRefreshTaskExpiration() final;
   NSDate* GetEarliestBackgroundRefreshBeginDate() final;
 
-  // TestDiscoverFeedService methods
+  // TestDiscoverFeedService methods:
+  void set_eligibility_handler(FakeDiscoverFeedEligibilityHandler* handler);
+  FakeDiscoverFeedEligibilityHandler* get_eligibility_handler();
   UICollectionView* collection_view();
   BrowserViewVisibilityState visibility_state();
 
  private:
   UICollectionView* collection_view_;
   BrowserViewVisibilityState visibility_state_;
+  FakeDiscoverFeedEligibilityHandler* eligibility_handler_;
 };
 
 #endif  // IOS_CHROME_TEST_PROVIDERS_DISCOVER_FEED_TEST_DISCOVER_FEED_SERVICE_H_
diff --git a/ios/chrome/test/providers/discover_feed/test_discover_feed_service.mm b/ios/chrome/test/providers/discover_feed/test_discover_feed_service.mm
index bc7ea235..63b4793b 100644
--- a/ios/chrome/test/providers/discover_feed/test_discover_feed_service.mm
+++ b/ios/chrome/test/providers/discover_feed/test_discover_feed_service.mm
@@ -63,6 +63,16 @@
   return nil;
 }
 
+void TestDiscoverFeedService::set_eligibility_handler(
+    FakeDiscoverFeedEligibilityHandler* handler) {
+  eligibility_handler_ = handler;
+}
+
+FakeDiscoverFeedEligibilityHandler*
+TestDiscoverFeedService::get_eligibility_handler() {
+  return eligibility_handler_;
+}
+
 UICollectionView* TestDiscoverFeedService::collection_view() {
   return collection_view_;
 }
diff --git a/ios/chrome/widget_kit_extension/deleted_account_views.swift b/ios/chrome/widget_kit_extension/deleted_account_views.swift
index 9cce714..6a991dd 100644
--- a/ios/chrome/widget_kit_extension/deleted_account_views.swift
+++ b/ios/chrome/widget_kit_extension/deleted_account_views.swift
@@ -14,6 +14,12 @@
   static let padding: CGFloat = 8
 }
 
+// Store in NSUserDefaults that the deleted account view appeared.
+func CollectMetricsInfo() {
+  guard let sharedDefaults = AppGroupHelper.groupUserDefaults() else { return }
+  sharedDefaults.set(true, forKey: "DeletedAccountUiDisplayed")
+}
+
 func SmallWidgetDeletedAccountView() -> some View {
   VStack {
     ZStack {
@@ -33,7 +39,9 @@
     }
   }
   .crContainerBackground(Color("widget_background_color").unredacted())
-
+  .onAppear {
+    CollectMetricsInfo()
+  }
 }
 
 func MediumWidgetDeletedAccountView() -> some View {
@@ -61,5 +69,7 @@
     }
   }
   .crContainerBackground(Color("widget_background_color").unredacted())
-
+  .onAppear {
+    CollectMetricsInfo()
+  }
 }
diff --git a/ios/public/provider/chrome/browser/discover_feed/BUILD.gn b/ios/public/provider/chrome/browser/discover_feed/BUILD.gn
index 9755762..bf4a2032 100644
--- a/ios/public/provider/chrome/browser/discover_feed/BUILD.gn
+++ b/ios/public/provider/chrome/browser/discover_feed/BUILD.gn
@@ -3,6 +3,9 @@
 # found in the LICENSE file.
 
 source_set("discover_feed_api") {
-  sources = [ "discover_feed_api.h" ]
+  sources = [
+    "discover_feed_api.h",
+    "discover_feed_visibility_provider.h",
+  ]
   public_deps = [ "//ios/chrome/browser/discover_feed/model" ]
 }
diff --git a/ios/public/provider/chrome/browser/discover_feed/discover_feed_api.h b/ios/public/provider/chrome/browser/discover_feed/discover_feed_api.h
index 22cd2e2..9c627c2 100644
--- a/ios/public/provider/chrome/browser/discover_feed/discover_feed_api.h
+++ b/ios/public/provider/chrome/browser/discover_feed/discover_feed_api.h
@@ -5,10 +5,13 @@
 #ifndef IOS_PUBLIC_PROVIDER_CHROME_BROWSER_DISCOVER_FEED_DISCOVER_FEED_API_H_
 #define IOS_PUBLIC_PROVIDER_CHROME_BROWSER_DISCOVER_FEED_DISCOVER_FEED_API_H_
 
-#include <memory>
+#import <memory>
 
-#include "ios/chrome/browser/discover_feed/model/discover_feed_configuration.h"
-#include "ios/chrome/browser/discover_feed/model/discover_feed_service.h"
+#import "ios/chrome/browser/discover_feed/model/discover_feed_configuration.h"
+#import "ios/chrome/browser/discover_feed/model/discover_feed_service.h"
+#import "ios/chrome/browser/discover_feed/model/discover_feed_visibility_provider_configuration.h"
+
+@protocol DiscoverFeedVisibilityProvider;
 
 namespace ios {
 namespace provider {
@@ -17,6 +20,10 @@
 std::unique_ptr<DiscoverFeedService> CreateDiscoverFeedService(
     DiscoverFeedConfiguration* configuration);
 
+// Creates a new instance of DiscoverFeedVisibilityProvider.
+id<DiscoverFeedVisibilityProvider> CreateDiscoverFeedVisibilityProvider(
+    DiscoverFeedVisibilityProviderConfiguration* configuration);
+
 }  // namespace provider
 }  // namespace ios
 
diff --git a/ios/public/provider/chrome/browser/discover_feed/discover_feed_visibility_provider.h b/ios/public/provider/chrome/browser/discover_feed/discover_feed_visibility_provider.h
new file mode 100644
index 0000000..925c00b9
--- /dev/null
+++ b/ios/public/provider/chrome/browser/discover_feed/discover_feed_visibility_provider.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 IOS_PUBLIC_PROVIDER_CHROME_BROWSER_DISCOVER_FEED_DISCOVER_FEED_VISIBILITY_PROVIDER_H_
+#define IOS_PUBLIC_PROVIDER_CHROME_BROWSER_DISCOVER_FEED_DISCOVER_FEED_VISIBILITY_PROVIDER_H_
+
+enum class DiscoverFeedEligibility;
+@protocol DiscoverFeedVisibilityObserver;
+
+/// Provider protocol that manages the visibility of the Discover feed.
+@protocol DiscoverFeedVisibilityProvider <NSObject>
+
+/// `YES` if the user has turned the Discover feed on. Usually can be toggled
+/// through the UI.
+@property(nonatomic, assign, getter=isEnabled) BOOL enabled;
+
+/// `YES` if the user is eligible to view the feed. If `NO`, the Discover feed
+/// should not be available to the user, regardless of their preference.
+- (DiscoverFeedEligibility)eligibility;
+
+/// Adding and removing Discover feed visibility observer from the provider.
+- (void)addObserver:(id<DiscoverFeedVisibilityObserver>)observer;
+- (void)removeObserver:(id<DiscoverFeedVisibilityObserver>)observer;
+
+/// Shutdown. Should be called before the instance is deallocated.
+- (void)shutdown;
+
+@end
+
+#endif  // IOS_PUBLIC_PROVIDER_CHROME_BROWSER_DISCOVER_FEED_DISCOVER_FEED_VISIBILITY_PROVIDER_H_
diff --git a/ios/web/public/js_messaging/resources/gcrweb.ts b/ios/web/public/js_messaging/resources/gcrweb.ts
index ce27afc..b19e33ae 100644
--- a/ios/web/public/js_messaging/resources/gcrweb.ts
+++ b/ios/web/public/js_messaging/resources/gcrweb.ts
@@ -48,6 +48,12 @@
    * Registers and frame by sending its frameId to the native application.
    */
   registerFrame() {
+    if (!document.documentElement) {
+      // Prevent registering frames if there is no document element created.
+      // This is true when rendering non-web content, such as PDFs.
+      return;
+    }
+
     sendWebKitMessage(
       'FrameBecameAvailable', {'crwFrameId': this.getFrameId()});
   }
diff --git a/ios_internal b/ios_internal
index 606119a..e53c6ac 160000
--- a/ios_internal
+++ b/ios_internal
@@ -1 +1 @@
-Subproject commit 606119a35c83780604698af972dc4f769e6247dc
+Subproject commit e53c6ac98134184d114ce34da0229ff717e6f4e7
diff --git a/media/capture/capture_switches.cc b/media/capture/capture_switches.cc
index 88e818b3..f2b84189b 100644
--- a/media/capture/capture_switches.cc
+++ b/media/capture/capture_switches.cc
@@ -44,7 +44,7 @@
 #if !BUILDFLAG(IS_ANDROID)
 BASE_FEATURE(kTabCaptureInfobarLinks,
              "TabCaptureInfobarLinks",
-             base::FEATURE_DISABLED_BY_DEFAULT);
+             base::FEATURE_ENABLED_BY_DEFAULT);
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 #if defined(WEBRTC_USE_PIPEWIRE)
diff --git a/net/base/winsock_util.h b/net/base/winsock_util.h
index 6220ba4..77e78ad0 100644
--- a/net/base/winsock_util.h
+++ b/net/base/winsock_util.h
@@ -8,6 +8,12 @@
 #include <winsock2.h>
 
 #include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <type_traits>
+
+#include "base/containers/span.h"
 
 namespace net {
 
@@ -24,6 +30,22 @@
 // optimization.  The code still works if this function simply returns false.
 bool ResetEventIfSignaled(WSAEVENT hEvent);
 
+// OVERLAPPED structs contain unions, and thus should not be initialized by `=
+// {}` lest there still be uninitialized bytes in union members larger than the
+// first. And some platforms, OVERLAPPED structs contain padding, and thus do
+// not meet `std::has_unique_object_representations_v`, so they need to be
+// explicitly allowed for byte spanification. These helpers encapsulate both
+// complexities.
+template <typename T>
+void FillOVERLAPPEDStruct(T& t, uint8_t value) {
+  if constexpr (std::has_unique_object_representations_v<T>) {
+    std::ranges::fill(base::byte_span_from_ref(t), value);
+  } else {
+    std::ranges::fill(base::byte_span_from_ref(base::allow_nonunique_obj, t),
+                      value);
+  }
+}
+
 }  // namespace net
 
 #endif  // NET_BASE_WINSOCK_UTIL_H_
diff --git a/net/filter/gzip_source_stream.cc b/net/filter/gzip_source_stream.cc
index 3e89bd0..b584109 100644
--- a/net/filter/gzip_source_stream.cc
+++ b/net/filter/gzip_source_stream.cc
@@ -64,8 +64,6 @@
 
 bool GzipSourceStream::Init() {
   zlib_stream_ = std::make_unique<z_stream>();
-  if (!zlib_stream_)
-    return false;
   memset(zlib_stream_.get(), 0, sizeof(z_stream));
 
   int ret;
diff --git a/net/http/http_stream_pool_attempt_manager.cc b/net/http/http_stream_pool_attempt_manager.cc
index 5298f65b..41f05d764 100644
--- a/net/http/http_stream_pool_attempt_manager.cc
+++ b/net/http/http_stream_pool_attempt_manager.cc
@@ -505,12 +505,17 @@
   }
 
   const size_t num_cancel_attempts = tcp_based_attempts_.size();
-  for (auto& attempt : tcp_based_attempts_) {
+  const size_t num_total_connecting_before =
+      pool()->TotalConnectingStreamCount();
+  while (!tcp_based_attempts_.empty()) {
+    std::unique_ptr<TcpBasedAttempt> attempt =
+        ExtractTcpBasedAttempt(tcp_based_attempts_.begin()->get());
     attempt->SetCancelReason(reason);
+    attempt.reset();
   }
-  pool()->DecrementTotalConnectingStreamCount(num_cancel_attempts);
-  tcp_based_attempts_.clear();
-  slow_tcp_based_attempt_count_ = 0;
+  CHECK_EQ(pool()->TotalConnectingStreamCount(),
+           num_total_connecting_before - num_cancel_attempts);
+  CHECK_EQ(slow_tcp_based_attempt_count_, 0u);
 
   base::UmaHistogramCounts100(
       base::StrCat({"Net.HttpStreamPool.TcpBasedAttemptCancelCount.",
@@ -1727,12 +1732,8 @@
     while (group_->ActiveStreamSocketCount() >
                pool()->max_stream_sockets_per_group() &&
            !tcp_based_attempts_.empty()) {
-      std::unique_ptr<TcpBasedAttempt> attempt = std::move(
-          tcp_based_attempts_.extract(tcp_based_attempts_.begin()).value());
-      if (attempt->is_slow()) {
-        --slow_tcp_based_attempt_count_;
-      }
-      pool()->DecrementTotalConnectingStreamCount();
+      std::unique_ptr<TcpBasedAttempt> attempt =
+          ExtractTcpBasedAttempt(tcp_based_attempts_.begin()->get());
       attempt.reset();
     }
   }
@@ -1757,25 +1758,34 @@
   MaybeChangeServiceEndpointRequestPriority();
 }
 
+std::unique_ptr<HttpStreamPool::AttemptManager::TcpBasedAttempt>
+HttpStreamPool::AttemptManager::ExtractTcpBasedAttempt(
+    TcpBasedAttempt* raw_attempt) {
+  auto it = tcp_based_attempts_.find(raw_attempt);
+  CHECK(it != tcp_based_attempts_.end());
+  std::unique_ptr<TcpBasedAttempt> attempt =
+      std::move(tcp_based_attempts_.extract(it).value());
+
+  pool()->DecrementTotalConnectingStreamCount();
+  if (attempt->is_slow()) {
+    CHECK_GT(slow_tcp_based_attempt_count_, 0u);
+    --slow_tcp_based_attempt_count_;
+  }
+
+  return attempt;
+}
+
 void HttpStreamPool::AttemptManager::OnTcpBasedAttemptComplete(
     TcpBasedAttempt* raw_attempt,
     int rv) {
-  if (raw_attempt->is_slow()) {
-    CHECK_GT(slow_tcp_based_attempt_count_, 0u);
-    --slow_tcp_based_attempt_count_;
-
-    if (rv == OK) {
-      auto it = ip_endpoint_states_.find(raw_attempt->ip_endpoint());
-      CHECK(it != ip_endpoint_states_.end());
-      it->second = IPEndPointState::kSlowSucceeded;
-    }
+  if (rv == OK && raw_attempt->is_slow()) {
+    auto it = ip_endpoint_states_.find(raw_attempt->ip_endpoint());
+    CHECK(it != ip_endpoint_states_.end());
+    it->second = IPEndPointState::kSlowSucceeded;
   }
 
-  auto it = tcp_based_attempts_.find(raw_attempt);
-  CHECK(it != tcp_based_attempts_.end());
   std::unique_ptr<TcpBasedAttempt> tcp_based_attempt =
-      std::move(tcp_based_attempts_.extract(it).value());
-  pool()->DecrementTotalConnectingStreamCount();
+      ExtractTcpBasedAttempt(raw_attempt);
 
   if (rv != OK) {
     HandleTcpBasedAttemptFailure(std::move(tcp_based_attempt), rv);
diff --git a/net/http/http_stream_pool_attempt_manager.h b/net/http/http_stream_pool_attempt_manager.h
index a6ba6d1..67070b6 100644
--- a/net/http/http_stream_pool_attempt_manager.h
+++ b/net/http/http_stream_pool_attempt_manager.h
@@ -488,6 +488,10 @@
   // limit.
   raw_ptr<Job> RemoveJobFromQueue(JobQueue::Pointer job_pointer);
 
+  // Transfers the ownership of `raw_attempt` to the caller.
+  std::unique_ptr<TcpBasedAttempt> ExtractTcpBasedAttempt(
+      TcpBasedAttempt* raw_attempt);
+
   void OnTcpBasedAttemptComplete(TcpBasedAttempt* raw_attempt, int rv);
   void OnTcpBasedAttemptSlow(TcpBasedAttempt* raw_attempt);
 
diff --git a/net/log/net_log_with_source.cc b/net/log/net_log_with_source.cc
index 349137c..e94c5bb 100644
--- a/net/log/net_log_with_source.cc
+++ b/net/log/net_log_with_source.cc
@@ -8,6 +8,7 @@
 #include <utility>
 
 #include "base/check_op.h"
+#include "base/containers/span.h"
 #include "base/no_destructor.h"
 #include "base/values.h"
 #include "net/base/net_errors.h"
@@ -27,8 +28,9 @@
                                          NetLogCaptureMode capture_mode) {
   base::Value::Dict dict;
   dict.Set("byte_count", byte_count);
-  if (NetLogCaptureIncludesSocketBytes(capture_mode) && byte_count > 0)
+  if (NetLogCaptureIncludesSocketBytes(capture_mode) && byte_count > 0) {
     dict.Set("bytes", NetLogBinaryValue(bytes, byte_count));
+  }
   return dict;
 }
 
@@ -141,6 +143,13 @@
   AddEntry(type, phase, [&] { return NetLogParamsWithBool(name, value); });
 }
 
+void NetLogWithSource::AddByteTransferEvent(
+    NetLogEventType event_type,
+    base::span<const uint8_t> bytes) const {
+  AddByteTransferEvent(event_type, base::checked_cast<int>(bytes.size()),
+                       base::as_chars(bytes).data());
+}
+
 void NetLogWithSource::AddByteTransferEvent(NetLogEventType event_type,
                                             int byte_count,
                                             const char* bytes) const {
@@ -152,8 +161,9 @@
 // static
 NetLogWithSource NetLogWithSource::Make(NetLog* net_log,
                                         NetLogSourceType source_type) {
-  if (!net_log)
+  if (!net_log) {
     return NetLogWithSource();
+  }
 
   NetLogSource source(source_type, net_log->NextID());
   return NetLogWithSource(source, net_log);
@@ -167,8 +177,9 @@
 // static
 NetLogWithSource NetLogWithSource::Make(NetLog* net_log,
                                         const NetLogSource& source) {
-  if (!net_log || !source.IsValid())
+  if (!net_log || !source.IsValid()) {
     return NetLogWithSource();
+  }
   return NetLogWithSource(source, net_log);
 }
 
@@ -178,8 +189,9 @@
 }
 
 NetLog* NetLogWithSource::net_log() const {
-  if (source_.IsValid())
+  if (source_.IsValid()) {
     return non_null_net_log_;
+  }
   return nullptr;
 }
 
diff --git a/net/log/net_log_with_source.h b/net/log/net_log_with_source.h
index e5d4d8f..c6913bfa 100644
--- a/net/log/net_log_with_source.h
+++ b/net/log/net_log_with_source.h
@@ -113,6 +113,12 @@
   // Logs a byte transfer event to the NetLog.  Determines whether to log the
   // received bytes or not based on the current logging level.
   void AddByteTransferEvent(NetLogEventType event_type,
+                            base::span<const uint8_t> bytes) const;
+
+  // DEPRECATED: Use the above `base::span` variant to avoid unsafe buffer
+  // usage.
+  // TODO(https://crbug.com/40284755): Remove this once the callers are gone.
+  void AddByteTransferEvent(NetLogEventType event_type,
                             int byte_count,
                             const char* bytes) const;
 
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc
index 38f03945..43d5cc12 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "net/socket/ssl_client_socket_impl.h"
 
 #include <errno.h>
@@ -232,8 +227,13 @@
                                                          const uint8_t* in,
                                                          size_t in_len) {
     SSLClientSocketImpl* socket = GetInstance()->GetClientSocketFromSSL(ssl);
-    return socket->PrivateKeySignCallback(out, out_len, max_out, algorithm, in,
-                                          in_len);
+    return socket->PrivateKeySignCallback(
+        algorithm,
+        // SAFETY:
+        // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#ssl_private_key_method_st
+        // `ssl_private_key_method_st::sign` implies that the value of `in_len`
+        // is equal to the actual size of `in`.
+        UNSAFE_BUFFERS(base::span(in, in_len)));
   }
 
   static ssl_private_key_result_t PrivateKeyCompleteCallback(SSL* ssl,
@@ -241,7 +241,12 @@
                                                              size_t* out_len,
                                                              size_t max_out) {
     SSLClientSocketImpl* socket = GetInstance()->GetClientSocketFromSSL(ssl);
-    return socket->PrivateKeyCompleteCallback(out, out_len, max_out);
+    return socket->PrivateKeyCompleteCallback(
+        // SAFETY:
+        // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#ssl_private_key_method_st
+        // The comment of `ssl_private_key_method_st::complete` indicates that
+        // `max_out` is the actual size of the buffer.
+        UNSAFE_BUFFERS(base::span(out, max_out)), out_len);
   }
 
   static void MessageCallback(int is_write,
@@ -298,7 +303,12 @@
   const uint8_t* retry_configs;
   size_t retry_configs_len;
   SSL_get0_ech_retry_configs(ssl_.get(), &retry_configs, &retry_configs_len);
-  return std::vector<uint8_t>(retry_configs, retry_configs + retry_configs_len);
+  // SAFETY:
+  // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_get0_ech_retry_configs
+  // says `retry_configs` and `retry_configs_len` define a buffer containing a
+  // serialized ECHConfigList.
+  return UNSAFE_BUFFERS(
+      std::vector<uint8_t>(retry_configs, retry_configs + retry_configs_len));
 }
 
 int SSLClientSocketImpl::ExportKeyingMaterial(
@@ -526,8 +536,11 @@
   const uint16_t* algorithms;
   size_t num_algorithms =
       SSL_get0_peer_verify_algorithms(ssl_.get(), &algorithms);
-  cert_request_info->signature_algorithms.assign(algorithms,
-                                                 algorithms + num_algorithms);
+  // SAFETY: The comment of `SSL_get0_peer_verify_algorithms` says that
+  // `algorithms` is set to an array, and its return value is the length of the
+  // array.
+  UNSAFE_BUFFERS(cert_request_info->signature_algorithms.assign(
+      algorithms, algorithms + num_algorithms));
 }
 
 void SSLClientSocketImpl::ApplySocketTag(const SocketTag& tag) {
@@ -548,7 +561,7 @@
 int SSLClientSocketImpl::ReadIfReady(IOBuffer* buf,
                                      int buf_len,
                                      CompletionOnceCallback callback) {
-  int rv = DoPayloadRead(buf, buf_len);
+  int rv = DoPayloadRead(buf->first(buf_len));
 
   if (rv == ERR_IO_PENDING) {
     user_read_callback_ = std::move(callback);
@@ -1069,12 +1082,23 @@
   const uint8_t* ocsp_response_raw;
   size_t ocsp_response_len;
   SSL_get0_ocsp_response(ssl_.get(), &ocsp_response_raw, &ocsp_response_len);
-  base::span ocsp_response(ocsp_response_raw, ocsp_response_len);
+  // SAFETY:
+  // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_get0_ocsp_response
+  // The comment of `SSL_get0_ocsp_response` says that `ocsp_response_raw` and
+  // `ocsp_response_len` point to `ocsp_response_len` bytes of an OCSP response
+  // from the server.
+  UNSAFE_BUFFERS(
+      base::span ocsp_response(ocsp_response_raw, ocsp_response_len));
 
   const uint8_t* sct_list_raw;
   size_t sct_list_len;
   SSL_get0_signed_cert_timestamp_list(ssl_.get(), &sct_list_raw, &sct_list_len);
-  base::span sct_list(sct_list_raw, sct_list_len);
+  // SAFETY:
+  // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_get0_signed_cert_timestamp_list
+  // The comment of `SSL_get0_signed_cert_timestamp_list` says that
+  // `sct_list_raw` and `sct_list_len` point to `sct_list_len` bytes of SCT
+  // information from the server.
+  UNSAFE_BUFFERS(base::span sct_list(sct_list_raw, sct_list_len));
 
   cert_verification_result_ = context_->cert_verifier()->Verify(
       CertVerifier::RequestParams(
@@ -1215,19 +1239,16 @@
   return rv;
 }
 
-int SSLClientSocketImpl::DoPayloadRead(IOBuffer* buf, int buf_len) {
+int SSLClientSocketImpl::DoPayloadRead(base::span<uint8_t> buf) {
   crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
 
-  DCHECK_LT(0, buf_len);
-  DCHECK(buf);
-
   int rv;
   if (pending_read_error_ != kSSLClientSocketNoPendingResult) {
     rv = pending_read_error_;
     pending_read_error_ = kSSLClientSocketNoPendingResult;
     if (rv == 0) {
       net_log_.AddByteTransferEvent(NetLogEventType::SSL_SOCKET_BYTES_RECEIVED,
-                                    rv, buf->data());
+                                    base::span<const uint8_t>());
     } else {
       NetLogOpenSSLError(net_log_, NetLogEventType::SSL_READ_ERROR, rv,
                          pending_read_ssl_error_, pending_read_error_info_);
@@ -1237,14 +1258,14 @@
     return rv;
   }
 
-  int total_bytes_read = 0;
+  base::span<uint8_t> available_buffer = buf;
   int ssl_ret, ssl_err;
   do {
-    ssl_ret = SSL_read(ssl_.get(), buf->data() + total_bytes_read,
-                       buf_len - total_bytes_read);
+    ssl_ret =
+        SSL_read(ssl_.get(), available_buffer.data(), available_buffer.size());
     ssl_err = SSL_get_error(ssl_.get(), ssl_ret);
     if (ssl_ret > 0) {
-      total_bytes_read += ssl_ret;
+      available_buffer = available_buffer.subspan(static_cast<size_t>(ssl_ret));
     } else if (ssl_err == SSL_ERROR_WANT_RENEGOTIATE) {
       if (!SSL_renegotiate(ssl_.get())) {
         ssl_err = SSL_ERROR_SSL;
@@ -1253,7 +1274,7 @@
     // Continue processing records as long as there is more data available
     // synchronously.
   } while (ssl_err == SSL_ERROR_WANT_RENEGOTIATE ||
-           (total_bytes_read < buf_len && ssl_ret > 0 &&
+           (!available_buffer.empty() && ssl_ret > 0 &&
             transport_adapter_->HasPendingReadData()));
 
   // Although only the final SSL_read call may have failed, the failure needs to
@@ -1284,10 +1305,10 @@
       pending_read_error_ = 0;
   }
 
-  if (total_bytes_read > 0) {
+  if (available_buffer.size() < buf.size()) {
     // Return any bytes read to the caller. The error will be deferred to the
     // next call of DoPayloadRead.
-    rv = total_bytes_read;
+    rv = buf.size() - available_buffer.size();
 
     // Do not treat insufficient data as an error to return in the next call to
     // DoPayloadRead() - instead, let the call fall through to check SSL_read()
@@ -1303,7 +1324,7 @@
 
   if (rv >= 0) {
     net_log_.AddByteTransferEvent(NetLogEventType::SSL_SOCKET_BYTES_RECEIVED,
-                                  rv, buf->data());
+                                  buf.first(static_cast<size_t>(rv)));
   } else if (rv != ERR_IO_PENDING) {
     NetLogOpenSSLError(net_log_, NetLogEventType::SSL_READ_ERROR, rv,
                        pending_read_ssl_error_, pending_read_error_info_);
@@ -1428,7 +1449,7 @@
   int rv_read = ERR_IO_PENDING;
   int rv_write = ERR_IO_PENDING;
   if (user_read_buf_) {
-    rv_read = DoPayloadRead(user_read_buf_.get(), user_read_buf_len_);
+    rv_read = DoPayloadRead(user_read_buf_->first(user_read_buf_len_));
   } else if (!user_read_callback_.is_null()) {
     // ReadIfReady() is called by the user. Skip DoPayloadRead() and just let
     // the user know that read can be retried.
@@ -1565,12 +1586,8 @@
 }
 
 ssl_private_key_result_t SSLClientSocketImpl::PrivateKeySignCallback(
-    uint8_t* out,
-    size_t* out_len,
-    size_t max_out,
     uint16_t algorithm,
-    const uint8_t* in,
-    size_t in_len) {
+    base::span<const uint8_t> input) {
   DCHECK_EQ(kSSLClientSocketNoPendingResult, signature_result_);
   DCHECK(signature_.empty());
   DCHECK(client_private_key_);
@@ -1593,16 +1610,15 @@
 
   signature_result_ = ERR_IO_PENDING;
   client_private_key_->Sign(
-      algorithm, base::span(in, in_len),
+      algorithm, input,
       base::BindOnce(&SSLClientSocketImpl::OnPrivateKeyComplete,
                      weak_factory_.GetWeakPtr()));
   return ssl_private_key_retry;
 }
 
 ssl_private_key_result_t SSLClientSocketImpl::PrivateKeyCompleteCallback(
-    uint8_t* out,
-    size_t* out_len,
-    size_t max_out) {
+    base::span<uint8_t> buf,
+    size_t* out_len) {
   DCHECK_NE(kSSLClientSocketNoPendingResult, signature_result_);
   DCHECK(client_private_key_);
 
@@ -1612,11 +1628,11 @@
     OpenSSLPutNetError(FROM_HERE, signature_result_);
     return ssl_private_key_failure;
   }
-  if (signature_.size() > max_out) {
+  if (signature_.size() > buf.size()) {
     OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
     return ssl_private_key_failure;
   }
-  memcpy(out, signature_.data(), signature_.size());
+  buf.copy_prefix_from(signature_);
   *out_len = signature_.size();
   signature_.clear();
   return ssl_private_key_success;
diff --git a/net/socket/ssl_client_socket_impl.h b/net/socket/ssl_client_socket_impl.h
index 87d2af8..27789ac 100644
--- a/net/socket/ssl_client_socket_impl.h
+++ b/net/socket/ssl_client_socket_impl.h
@@ -135,7 +135,7 @@
   void OnHandshakeIOComplete(int result);
 
   int DoHandshakeLoop(int last_io_result);
-  int DoPayloadRead(IOBuffer* buf, int buf_len);
+  int DoPayloadRead(base::span<uint8_t> buf);
   int DoPayloadWrite();
   void DoPeek();
 
@@ -169,15 +169,11 @@
   bool IsCachingEnabled() const;
 
   // Callbacks for operations with the private key.
-  ssl_private_key_result_t PrivateKeySignCallback(uint8_t* out,
-                                                  size_t* out_len,
-                                                  size_t max_out,
-                                                  uint16_t algorithm,
-                                                  const uint8_t* in,
-                                                  size_t in_len);
-  ssl_private_key_result_t PrivateKeyCompleteCallback(uint8_t* out,
-                                                      size_t* out_len,
-                                                      size_t max_out);
+  ssl_private_key_result_t PrivateKeySignCallback(
+      uint16_t algorithm,
+      base::span<const uint8_t> input);
+  ssl_private_key_result_t PrivateKeyCompleteCallback(base::span<uint8_t> buf,
+                                                      size_t* out_len);
 
   void OnPrivateKeyComplete(Error error, const std::vector<uint8_t>& signature);
 
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index 93e60bfa..431d95e 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -2,17 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "net/socket/ssl_client_socket.h"
 
 #include <errno.h>
 #include <string.h>
 
 #include <algorithm>
+#include <array>
 #include <memory>
 #include <optional>
 #include <ranges>
@@ -20,6 +16,7 @@
 #include <tuple>
 #include <utility>
 
+#include "base/containers/span.h"
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback_helpers.h"
@@ -361,7 +358,7 @@
     CHECK(!should_block_read_);
     CHECK_GE(len, static_cast<int>(read_if_ready_buf_.size()));
     int rv = read_if_ready_buf_.size();
-    memcpy(buf->data(), read_if_ready_buf_.data(), rv);
+    buf->span().copy_prefix_from(base::as_byte_span(read_if_ready_buf_));
     read_if_ready_buf_.clear();
     return rv;
   }
@@ -370,7 +367,7 @@
                 base::BindOnce(&FakeBlockingStreamSocket::CompleteReadIfReady,
                                base::Unretained(this), buf_copy));
   if (rv > 0)
-    memcpy(buf->data(), buf_copy->data(), rv);
+    buf->span().copy_prefix_from(buf_copy->first(rv));
   if (rv == ERR_IO_PENDING)
     read_if_ready_callback_ = std::move(callback);
   return rv;
@@ -431,7 +428,7 @@
   if (static_cast<size_t>(pending_read_buf_len_) < data.size())
     return false;
 
-  memcpy(pending_read_buf_->data(), data.data(), data.size());
+  pending_read_buf_->span().copy_prefix_from(base::as_byte_span(data));
   pending_read_result_ = data.size();
   return true;
 }
@@ -512,7 +509,7 @@
   DCHECK(read_if_ready_buf_.empty());
   DCHECK(!should_block_read_);
   if (rv > 0)
-    read_if_ready_buf_ = std::string(buf->data(), buf->data() + rv);
+    read_if_ready_buf_ = base::as_string_view(buf->first(rv));
   // The callback may be null if CancelReadIfReady() was called.
   if (!read_if_ready_callback_.is_null())
     std::move(read_if_ready_callback_).Run(rv > 0 ? OK : rv);
@@ -1079,18 +1076,15 @@
       EXPECT_THAT(rv, IsOk());
       EXPECT_TRUE(sock->IsConnected());
 
-      const char request_text[] = "GET / HTTP/1.0\r\n\r\n";
-      static const int kRequestTextSize =
-          static_cast<int>(std::size(request_text) - 1);
-      auto request_buffer =
-          base::MakeRefCounted<IOBufferWithSize>(kRequestTextSize);
-      memcpy(request_buffer->data(), request_text, kRequestTextSize);
+      static constexpr std::string_view request_text = "GET / HTTP/1.0\r\n\r\n";
+      auto request_buffer = base::MakeRefCounted<VectorIOBuffer>(
+          base::as_byte_span(request_text));
 
       // Write the request.
-      rv = callback.GetResult(sock->Write(request_buffer.get(),
-                                          kRequestTextSize, callback.callback(),
-                                          TRAFFIC_ANNOTATION_FOR_TESTS));
-      EXPECT_EQ(kRequestTextSize, rv);
+      rv = callback.GetResult(
+          sock->Write(request_buffer.get(), request_text.size(),
+                      callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
+      EXPECT_EQ(request_text.size(), rv);
 
       // The read will hang; it's waiting for the peer to complete the
       // handshake, and the handshake is still blocked.
@@ -1234,8 +1228,8 @@
 
   int WriteAndWait(std::string_view request) {
     auto request_buffer =
-        base::MakeRefCounted<IOBufferWithSize>(request.size());
-    memcpy(request_buffer->data(), request.data(), request.size());
+        base::MakeRefCounted<VectorIOBuffer>(base::as_byte_span(request));
+
     return callback_.GetResult(
         ssl_socket_->Write(request_buffer.get(), request.size(),
                            callback_.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
@@ -1602,15 +1596,14 @@
   // establishment.
   EXPECT_GT(sock->GetTotalReceivedBytes(), 0);
 
-  const char request_text[] = "GET / HTTP/1.0\r\n\r\n";
+  static constexpr std::string_view request_text = "GET / HTTP/1.0\r\n\r\n";
   auto request_buffer =
-      base::MakeRefCounted<IOBufferWithSize>(std::size(request_text) - 1);
-  memcpy(request_buffer->data(), request_text, std::size(request_text) - 1);
+      base::MakeRefCounted<VectorIOBuffer>(base::as_byte_span(request_text));
 
-  rv = callback.GetResult(
-      sock->Write(request_buffer.get(), std::size(request_text) - 1,
-                  callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
-  EXPECT_EQ(static_cast<int>(std::size(request_text) - 1), rv);
+  rv = callback.GetResult(sock->Write(request_buffer.get(), request_text.size(),
+                                      callback.callback(),
+                                      TRAFFIC_ANNOTATION_FOR_TESTS));
+  EXPECT_EQ(request_text.size(), rv);
 
   auto buf = base::MakeRefCounted<IOBufferWithSize>(4096);
   int64_t unencrypted_bytes_read = 0;
@@ -1685,17 +1678,14 @@
   EXPECT_THAT(rv, IsOk());
   EXPECT_TRUE(sock->IsConnected());
 
-  const char request_text[] = "GET / HTTP/1.0\r\n\r\n";
-  static const int kRequestTextSize =
-      static_cast<int>(std::size(request_text) - 1);
+  static constexpr std::string_view request_text = "GET / HTTP/1.0\r\n\r\n";
   auto request_buffer =
-      base::MakeRefCounted<IOBufferWithSize>(kRequestTextSize);
-  memcpy(request_buffer->data(), request_text, kRequestTextSize);
+      base::MakeRefCounted<VectorIOBuffer>(base::as_byte_span(request_text));
 
-  rv = callback.GetResult(sock->Write(request_buffer.get(), kRequestTextSize,
+  rv = callback.GetResult(sock->Write(request_buffer.get(), request_text.size(),
                                       callback.callback(),
                                       TRAFFIC_ANNOTATION_FOR_TESTS));
-  EXPECT_EQ(kRequestTextSize, rv);
+  EXPECT_EQ(request_text.size(), rv);
 
   // Simulate an unclean/forcible shutdown.
   raw_transport->SetNextReadError(ERR_CONNECTION_RESET);
@@ -1738,12 +1728,9 @@
   EXPECT_THAT(rv, IsOk());
   EXPECT_TRUE(sock->IsConnected());
 
-  const char request_text[] = "GET / HTTP/1.0\r\n\r\n";
-  static const int kRequestTextSize =
-      static_cast<int>(std::size(request_text) - 1);
+  static constexpr std::string_view request_text = "GET / HTTP/1.0\r\n\r\n";
   auto request_buffer =
-      base::MakeRefCounted<IOBufferWithSize>(kRequestTextSize);
-  memcpy(request_buffer->data(), request_text, kRequestTextSize);
+      base::MakeRefCounted<VectorIOBuffer>(base::as_byte_span(request_text));
 
   // Simulate an unclean/forcible shutdown on the underlying socket.
   // However, simulate this error asynchronously.
@@ -1753,10 +1740,10 @@
   // This write should complete synchronously, because the TLS ciphertext
   // can be created and placed into the outgoing buffers independent of the
   // underlying transport.
-  rv = callback.GetResult(sock->Write(request_buffer.get(), kRequestTextSize,
+  rv = callback.GetResult(sock->Write(request_buffer.get(), request_text.size(),
                                       callback.callback(),
                                       TRAFFIC_ANNOTATION_FOR_TESTS));
-  EXPECT_EQ(kRequestTextSize, rv);
+  EXPECT_EQ(request_text.size(), rv);
 
   auto buf = base::MakeRefCounted<IOBufferWithSize>(4096);
 
@@ -1806,20 +1793,17 @@
   // Simulate an unclean/forcible shutdown on the underlying socket.
   raw_error_socket->SetNextWriteError(ERR_CONNECTION_RESET);
 
-  const char request_text[] = "GET / HTTP/1.0\r\n\r\n";
-  static const int kRequestTextSize =
-      static_cast<int>(std::size(request_text) - 1);
+  static constexpr std::string_view request_text = "GET / HTTP/1.0\r\n\r\n";
   auto request_buffer =
-      base::MakeRefCounted<IOBufferWithSize>(kRequestTextSize);
-  memcpy(request_buffer->data(), request_text, kRequestTextSize);
+      base::MakeRefCounted<VectorIOBuffer>(base::as_byte_span(request_text));
 
   // This write should complete synchronously, because the TLS ciphertext
   // can be created and placed into the outgoing buffers independent of the
   // underlying transport.
-  rv = callback.GetResult(sock->Write(request_buffer.get(), kRequestTextSize,
+  rv = callback.GetResult(sock->Write(request_buffer.get(), request_text.size(),
                                       callback.callback(),
                                       TRAFFIC_ANNOTATION_FOR_TESTS));
-  ASSERT_EQ(kRequestTextSize, rv);
+  ASSERT_EQ(request_text.size(), rv);
 
   // Let the event loop spin for a little bit of time. Even on platforms where
   // pumping the state machine involve thread hops, there should be no further
@@ -1990,17 +1974,14 @@
   EXPECT_TRUE(sock->IsConnected());
 
   // Send a request so there is something to read from the socket.
-  const char request_text[] = "GET / HTTP/1.0\r\n\r\n";
-  static const int kRequestTextSize =
-      static_cast<int>(std::size(request_text) - 1);
+  static constexpr std::string_view request_text = "GET / HTTP/1.0\r\n\r\n";
   auto request_buffer =
-      base::MakeRefCounted<IOBufferWithSize>(kRequestTextSize);
-  memcpy(request_buffer->data(), request_text, kRequestTextSize);
+      base::MakeRefCounted<VectorIOBuffer>(base::as_byte_span(request_text));
 
-  rv = callback.GetResult(sock->Write(request_buffer.get(), kRequestTextSize,
+  rv = callback.GetResult(sock->Write(request_buffer.get(), request_text.size(),
                                       callback.callback(),
                                       TRAFFIC_ANNOTATION_FOR_TESTS));
-  EXPECT_EQ(kRequestTextSize, rv);
+  EXPECT_EQ(request_text.size(), rv);
 
   // Start a hanging read.
   TestCompletionCallback read_callback;
@@ -2173,16 +2154,15 @@
   ASSERT_TRUE(CreateAndConnectSSLClientSocket(SSLConfig(), &rv));
   EXPECT_THAT(rv, IsOk());
 
-  const char request_text[] = "GET / HTTP/1.0\r\n\r\n";
+  static constexpr std::string_view request_text = "GET / HTTP/1.0\r\n\r\n";
   auto request_buffer =
-      base::MakeRefCounted<IOBufferWithSize>(std::size(request_text) - 1);
-  memcpy(request_buffer->data(), request_text, std::size(request_text) - 1);
+      base::MakeRefCounted<VectorIOBuffer>(base::as_byte_span(request_text));
 
   TestCompletionCallback callback;
-  rv = callback.GetResult(
-      sock_->Write(request_buffer.get(), std::size(request_text) - 1,
-                   callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
-  EXPECT_EQ(static_cast<int>(std::size(request_text) - 1), rv);
+  rv = callback.GetResult(sock_->Write(request_buffer.get(),
+                                       request_text.size(), callback.callback(),
+                                       TRAFFIC_ANNOTATION_FOR_TESTS));
+  EXPECT_EQ(static_cast<int>(request_text.size()), rv);
 
   auto buf = base::MakeRefCounted<IOBufferWithSize>(1);
   do {
@@ -2214,16 +2194,16 @@
   ASSERT_THAT(rv, IsOk());
   ASSERT_TRUE(sock->IsConnected());
 
-  const char request_text[] = "GET /ssl-many-small-records HTTP/1.0\r\n\r\n";
+  static constexpr std::string_view request_text =
+      "GET /ssl-many-small-records HTTP/1.0\r\n\r\n";
   auto request_buffer =
-      base::MakeRefCounted<IOBufferWithSize>(std::size(request_text) - 1);
-  memcpy(request_buffer->data(), request_text, std::size(request_text) - 1);
+      base::MakeRefCounted<VectorIOBuffer>(base::as_byte_span(request_text));
 
-  rv = callback.GetResult(
-      sock->Write(request_buffer.get(), std::size(request_text) - 1,
-                  callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
+  rv = callback.GetResult(sock->Write(request_buffer.get(), request_text.size(),
+                                      callback.callback(),
+                                      TRAFFIC_ANNOTATION_FOR_TESTS));
   ASSERT_GT(rv, 0);
-  ASSERT_EQ(static_cast<int>(std::size(request_text) - 1), rv);
+  ASSERT_EQ(static_cast<int>(request_text.size()), rv);
 
   // Note: This relies on SSLClientSocketNSS attempting to read up to 17K of
   // data (the max SSL record size) at a time. Ensure that at least 15K worth
@@ -2249,16 +2229,15 @@
   ASSERT_TRUE(CreateAndConnectSSLClientSocket(SSLConfig(), &rv));
   EXPECT_THAT(rv, IsOk());
 
-  const char request_text[] = "GET / HTTP/1.0\r\n\r\n";
+  static constexpr std::string_view request_text = "GET / HTTP/1.0\r\n\r\n";
   auto request_buffer =
-      base::MakeRefCounted<IOBufferWithSize>(std::size(request_text) - 1);
-  memcpy(request_buffer->data(), request_text, std::size(request_text) - 1);
+      base::MakeRefCounted<VectorIOBuffer>(base::as_byte_span(request_text));
 
   TestCompletionCallback callback;
-  rv = callback.GetResult(
-      sock_->Write(request_buffer.get(), std::size(request_text) - 1,
-                   callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
-  EXPECT_EQ(static_cast<int>(std::size(request_text) - 1), rv);
+  rv = callback.GetResult(sock_->Write(request_buffer.get(),
+                                       request_text.size(), callback.callback(),
+                                       TRAFFIC_ANNOTATION_FOR_TESTS));
+  EXPECT_EQ(static_cast<int>(request_text.size()), rv);
 
   // Do a partial read and then exit.  This test should not crash!
   auto buf = base::MakeRefCounted<IOBufferWithSize>(512);
@@ -2284,15 +2263,14 @@
   EXPECT_THAT(rv, IsOk());
   EXPECT_TRUE(sock->IsConnected());
 
-  const char request_text[] = "GET / HTTP/1.0\r\n\r\n";
+  static constexpr std::string_view request_text = "GET / HTTP/1.0\r\n\r\n";
   auto request_buffer =
-      base::MakeRefCounted<IOBufferWithSize>(std::size(request_text) - 1);
-  memcpy(request_buffer->data(), request_text, std::size(request_text) - 1);
+      base::MakeRefCounted<VectorIOBuffer>(base::as_byte_span(request_text));
 
-  rv = callback.GetResult(
-      sock->Write(request_buffer.get(), std::size(request_text) - 1,
-                  callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS));
-  EXPECT_EQ(static_cast<int>(std::size(request_text) - 1), rv);
+  rv = callback.GetResult(sock->Write(request_buffer.get(), request_text.size(),
+                                      callback.callback(),
+                                      TRAFFIC_ANNOTATION_FOR_TESTS));
+  EXPECT_EQ(static_cast<int>(request_text.size()), rv);
 
   auto entries = log_observer_.GetEntries();
   size_t last_index = ExpectLogContainsSomewhereAfter(
@@ -2757,8 +2735,18 @@
           // function with a return value.
           return false;
         }
-        EXPECT_EQ(0, memcmp(ssl_config.trust_anchor_ids.data(), data + 2,
-                            ssl_config.trust_anchor_ids.size()));
+        EXPECT_EQ(
+            base::span(ssl_config.trust_anchor_ids),
+            // SAFETY:
+            // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_early_callback_ctx_extension_get
+            // The comment of `SSL_early_callback_ctx_extension_get` says
+            // that `data` is set to extension contents, and `len` is the
+            // length of the extension contents.
+            //
+            // Earlier, we checked that ssl_config.trust_anchor_ids.size() + 2
+            // == len.
+            UNSAFE_BUFFERS(
+                base::span(data + 2, ssl_config.trust_anchor_ids.size())));
         ran_callback = true;
         return true;
       });
@@ -2812,16 +2800,15 @@
   EXPECT_TRUE(sock_->IsConnectedAndIdle());
   EXPECT_FALSE(sock_->WasEverUsed());
 
-  const char kRequestText[] = "GET / HTTP/1.0\r\n\r\n";
-  const size_t kRequestLen = std::size(kRequestText) - 1;
-  auto request_buffer = base::MakeRefCounted<IOBufferWithSize>(kRequestLen);
-  memcpy(request_buffer->data(), kRequestText, kRequestLen);
+  static constexpr std::string_view request_text = "GET / HTTP/1.0\r\n\r\n";
+  auto request_buffer =
+      base::MakeRefCounted<VectorIOBuffer>(base::as_byte_span(request_text));
 
   TestCompletionCallback callback;
-  rv = callback.GetResult(sock_->Write(request_buffer.get(), kRequestLen,
-                                       callback.callback(),
+  rv = callback.GetResult(sock_->Write(request_buffer.get(),
+                                       request_text.size(), callback.callback(),
                                        TRAFFIC_ANNOTATION_FOR_TESTS));
-  EXPECT_EQ(static_cast<int>(kRequestLen), rv);
+  EXPECT_EQ(static_cast<int>(request_text.size()), rv);
 
   // The socket has now been used.
   EXPECT_TRUE(sock_->WasEverUsed());
@@ -2884,17 +2871,16 @@
   raw_transport->BlockWrite();
 
   // Write a partial HTTP request.
-  const char kRequestText[] = "GET / HTTP/1.0";
-  const size_t kRequestLen = std::size(kRequestText) - 1;
-  auto request_buffer = base::MakeRefCounted<IOBufferWithSize>(kRequestLen);
-  memcpy(request_buffer->data(), kRequestText, kRequestLen);
+  static constexpr std::string_view request_text = "GET / HTTP/1.0";
+  auto request_buffer =
+      base::MakeRefCounted<VectorIOBuffer>(base::as_byte_span(request_text));
 
   // Although transport writes are blocked, SSLClientSocketImpl completes the
   // outer Write operation.
-  EXPECT_EQ(static_cast<int>(kRequestLen),
-            callback.GetResult(sock->Write(request_buffer.get(), kRequestLen,
-                                           callback.callback(),
-                                           TRAFFIC_ANNOTATION_FOR_TESTS)));
+  EXPECT_EQ(static_cast<int>(request_text.size()),
+            callback.GetResult(sock->Write(
+                request_buffer.get(), request_text.size(), callback.callback(),
+                TRAFFIC_ANNOTATION_FOR_TESTS)));
 
   // The Write operation is complete, so the socket should be treated as
   // reusable, in case the server returns an HTTP response before completely
@@ -3559,7 +3545,7 @@
   // |sock1|. Before doing so, break the server's second leg.
   int bytes_read = raw_transport1->pending_read_result();
   ASSERT_LT(0, bytes_read);
-  raw_transport1->pending_read_buf()->data()[bytes_read - 1]++;
+  raw_transport1->pending_read_buf()->span()[bytes_read - 1]++;
 
   // Unblock the Finished message. |sock1->Read| should now fail.
   raw_transport1->UnblockReadResult();
@@ -5015,7 +5001,7 @@
   auto buf = base::MakeRefCounted<IOBufferWithSize>(4096);
   int size = ReadAndWait(buf.get(), 4096);
   EXPECT_GT(size, 0);
-  EXPECT_EQ('1', buf->data()[size - 1]);
+  EXPECT_EQ('1', buf->span()[size - 1]);
 
   SSLInfo ssl_info;
   ASSERT_TRUE(GetSSLInfo(&ssl_info));
@@ -5050,7 +5036,7 @@
   auto buf = base::MakeRefCounted<IOBufferWithSize>(4096);
   int size = ReadAndWait(buf.get(), 4096);
   EXPECT_GT(size, 0);
-  EXPECT_EQ('0', buf->data()[size - 1]);
+  EXPECT_EQ('0', buf->span()[size - 1]);
 
   SSLInfo ssl_info;
   ASSERT_TRUE(GetSSLInfo(&ssl_info));
@@ -5074,7 +5060,7 @@
   auto buf = base::MakeRefCounted<IOBufferWithSize>(4096);
   int size = ReadAndWait(buf.get(), 4096);
   EXPECT_GT(size, 0);
-  EXPECT_EQ('1', buf->data()[size - 1]);
+  EXPECT_EQ('1', buf->span()[size - 1]);
 
   // After the handshake is confirmed, ConfirmHandshake should return
   // synchronously.
@@ -5145,7 +5131,7 @@
   EXPECT_THAT(confirm_callback.GetResult(confirm_rv), IsOk());
   int size = read_callback.GetResult(read_rv);
   ASSERT_GT(size, 0);
-  EXPECT_EQ('1', read_buf->data()[size - 1]);
+  EXPECT_EQ('1', read_buf->span()[size - 1]);
 
   SSLInfo ssl_info;
   ASSERT_TRUE(GetSSLInfo(&ssl_info));
@@ -5324,7 +5310,7 @@
   auto buf = base::MakeRefCounted<IOBufferWithSize>(4096);
   int size = ReadAndWait(buf.get(), 4096);
   EXPECT_GT(size, 0);
-  EXPECT_EQ('0', buf->data()[size - 1]);
+  EXPECT_EQ('0', buf->span()[size - 1]);
 
   SSLInfo ssl_info;
   ASSERT_TRUE(GetSSLInfo(&ssl_info));
@@ -5356,7 +5342,7 @@
   socket->UnblockReadResult();
   int size = read_callback.GetResult(ERR_IO_PENDING);
   EXPECT_GT(size, 0);
-  EXPECT_EQ('1', buf->data()[size - 1]);
+  EXPECT_EQ('1', buf->span()[size - 1]);
 
   SSLInfo ssl_info;
   ASSERT_TRUE(GetSSLInfo(&ssl_info));
@@ -5383,7 +5369,7 @@
   auto buf = base::MakeRefCounted<IOBufferWithSize>(4096);
   int size = ReadAndWait(buf.get(), 4096);
   EXPECT_GT(size, 0);
-  EXPECT_EQ('0', buf->data()[size - 1]);
+  EXPECT_EQ('0', buf->span()[size - 1]);
 
   SSLInfo ssl_info;
   ASSERT_TRUE(GetSSLInfo(&ssl_info));
@@ -5422,7 +5408,7 @@
 
   int result = read_callback.WaitForResult();
   EXPECT_GT(result, 0);
-  EXPECT_EQ('1', buf->data()[result - 1]);
+  EXPECT_EQ('1', buf->span()[result - 1]);
 
   SSLInfo ssl_info;
   ASSERT_TRUE(GetSSLInfo(&ssl_info));
@@ -6063,7 +6049,7 @@
   auto buf = base::MakeRefCounted<IOBufferWithSize>(4096);
   int size = ReadAndWait(buf.get(), 4096);
   EXPECT_GT(size, 0);
-  EXPECT_EQ('1', buf->data()[size - 1]);
+  EXPECT_EQ('1', buf->span()[size - 1]);
 
   // 0-RTT metrics are logged on a PostTask, so if Read returns synchronously,
   // it is possible the metrics haven't been picked up yet.
@@ -6370,8 +6356,15 @@
         // are two length prefixes. A two-byte length prefix (0x0003) followed
         // by a one-byte length prefix (0x02). See
         // https://www.ietf.org/archive/id/draft-vvv-tls-alps-01.html#section-4
-        EXPECT_EQ(std::vector<uint8_t>(data, data + len),
-                  std::vector<uint8_t>({0x00, 0x03, 0x02, 'h', '2'}));
+        static constexpr auto expected =
+            std::to_array<uint8_t>({0x00, 0x03, 0x02, 'h', '2'});
+        EXPECT_EQ(
+            // SAFETY:
+            // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_early_callback_ctx_extension_get
+            // The comment of `SSL_early_callback_ctx_extension_get` says that
+            // `data` is set to extension contents, and `len` is the
+            // length of the extension contents.
+            UNSAFE_BUFFERS(base::span(data, data + len)), base::span(expected));
         return true;
       });
   ASSERT_TRUE(
diff --git a/net/socket/ssl_server_socket_impl.cc b/net/socket/ssl_server_socket_impl.cc
index 8f6c1d5..f5aa5db 100644
--- a/net/socket/ssl_server_socket_impl.cc
+++ b/net/socket/ssl_server_socket_impl.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "net/socket/ssl_server_socket_impl.h"
 
 #include <memory>
@@ -137,15 +132,11 @@
                                                              size_t* out_len,
                                                              size_t max_out);
 
-  ssl_private_key_result_t PrivateKeySignCallback(uint8_t* out,
-                                                  size_t* out_len,
-                                                  size_t max_out,
-                                                  uint16_t algorithm,
-                                                  const uint8_t* in,
-                                                  size_t in_len);
-  ssl_private_key_result_t PrivateKeyCompleteCallback(uint8_t* out,
-                                                      size_t* out_len,
-                                                      size_t max_out);
+  ssl_private_key_result_t PrivateKeySignCallback(
+      uint16_t algorithm,
+      base::span<const uint8_t> input);
+  ssl_private_key_result_t PrivateKeyCompleteCallback(base::span<uint8_t> buf,
+                                                      size_t* out_len);
   void OnPrivateKeyComplete(Error error, const std::vector<uint8_t>& signature);
 
   static int ALPNSelectCallback(SSL* ssl,
@@ -256,8 +247,13 @@
                                                          uint16_t algorithm,
                                                          const uint8_t* in,
                                                          size_t in_len) {
-  return FromSSL(ssl)->PrivateKeySignCallback(out, out_len, max_out, algorithm,
-                                              in, in_len);
+  return FromSSL(ssl)->PrivateKeySignCallback(
+      algorithm,
+      // SAFETY:
+      // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#ssl_private_key_method_st
+      // `ssl_private_key_method_st::sign` implies that the value of `in_len`
+      // is equal to the actual size of `in`.
+      UNSAFE_BUFFERS(base::span(in, in_len)));
 }
 
 // static
@@ -278,41 +274,43 @@
                                                              uint8_t* out,
                                                              size_t* out_len,
                                                              size_t max_out) {
-  return FromSSL(ssl)->PrivateKeyCompleteCallback(out, out_len, max_out);
+  return FromSSL(ssl)->PrivateKeyCompleteCallback(
+      // SAFETY:
+      // https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#ssl_private_key_method_st
+      // The comment of `ssl_private_key_method_st::complete` indicates that
+      // `max_out` is the actual size of the buffer.
+      UNSAFE_BUFFERS(base::span(out, max_out)), out_len);
 }
 
 ssl_private_key_result_t
-SSLServerContextImpl::SocketImpl::PrivateKeySignCallback(uint8_t* out,
-                                                         size_t* out_len,
-                                                         size_t max_out,
-                                                         uint16_t algorithm,
-                                                         const uint8_t* in,
-                                                         size_t in_len) {
+SSLServerContextImpl::SocketImpl::PrivateKeySignCallback(
+    uint16_t algorithm,
+    base::span<const uint8_t> input) {
   DCHECK(context_);
   DCHECK(context_->private_key_);
   signature_result_ = ERR_IO_PENDING;
   context_->private_key_->Sign(
-      algorithm, base::span(in, in_len),
+      algorithm, input,
       base::BindOnce(&SSLServerContextImpl::SocketImpl::OnPrivateKeyComplete,
                      weak_factory_.GetWeakPtr()));
   return ssl_private_key_retry;
 }
 
 ssl_private_key_result_t
-SSLServerContextImpl::SocketImpl::PrivateKeyCompleteCallback(uint8_t* out,
-                                                             size_t* out_len,
-                                                             size_t max_out) {
+SSLServerContextImpl::SocketImpl::PrivateKeyCompleteCallback(
+    base::span<uint8_t> buf,
+    size_t* out_len) {
   if (signature_result_ == ERR_IO_PENDING)
     return ssl_private_key_retry;
   if (signature_result_ != OK) {
     OpenSSLPutNetError(FROM_HERE, signature_result_);
     return ssl_private_key_failure;
   }
-  if (signature_.size() > max_out) {
+  if (signature_.size() > buf.size()) {
     OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
     return ssl_private_key_failure;
   }
-  memcpy(out, signature_.data(), signature_.size());
+  buf.copy_prefix_from(signature_);
   *out_len = signature_.size();
   signature_.clear();
   return ssl_private_key_success;
diff --git a/net/socket/ssl_server_socket_unittest.cc b/net/socket/ssl_server_socket_unittest.cc
index 2c75001..d083c46 100644
--- a/net/socket/ssl_server_socket_unittest.cc
+++ b/net/socket/ssl_server_socket_unittest.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include <array>
 
 // This test suite uses SSLClientSocket to test the implementation of
@@ -20,8 +15,6 @@
 //
 // Implementations of these two classes are included in this file.
 
-#include "net/socket/ssl_server_socket.h"
-
 #include <stdint.h>
 #include <stdlib.h>
 
@@ -32,6 +25,7 @@
 #include "base/check.h"
 #include "base/compiler_specific.h"
 #include "base/containers/queue.h"
+#include "base/containers/span.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/functional/bind.h"
@@ -64,6 +58,7 @@
 #include "net/socket/client_socket_factory.h"
 #include "net/socket/socket_test_util.h"
 #include "net/socket/ssl_client_socket.h"
+#include "net/socket/ssl_server_socket.h"
 #include "net/socket/stream_socket.h"
 #include "net/ssl/openssl_private_key.h"
 #include "net/ssl/ssl_cert_request_info.h"
@@ -203,7 +198,7 @@
   int PropagateData(scoped_refptr<IOBuffer> read_buf, int read_buf_len) {
     scoped_refptr<DrainableIOBuffer> buf = data_.front();
     int copied = std::min(buf->BytesRemaining(), read_buf_len);
-    memcpy(read_buf->data(), buf->data(), copied);
+    read_buf->span().copy_prefix_from(buf->first(copied));
     buf->DidConsume(copied);
 
     if (!buf->BytesRemaining())
@@ -319,24 +314,24 @@
   FakeSocket client(&channel_1, &channel_2);
   FakeSocket server(&channel_2, &channel_1);
 
-  const char kTestData[] = "testing123";
-  const int kTestDataSize = strlen(kTestData);
+  static constexpr std::string_view kTestData = "testing123";
   const int kReadBufSize = 1024;
-  auto write_buf = base::MakeRefCounted<StringIOBuffer>(kTestData);
+  auto write_buf = base::MakeRefCounted<StringIOBuffer>(std::string(kTestData));
   auto read_buf = base::MakeRefCounted<IOBufferWithSize>(kReadBufSize);
 
   // Write then read.
   int written =
-      server.Write(write_buf.get(), kTestDataSize, CompletionOnceCallback(),
+      server.Write(write_buf.get(), kTestData.size(), CompletionOnceCallback(),
                    TRAFFIC_ANNOTATION_FOR_TESTS);
   EXPECT_GT(written, 0);
-  EXPECT_LE(written, kTestDataSize);
+  EXPECT_LE(written, kTestData.size());
 
   int read =
       client.Read(read_buf.get(), kReadBufSize, CompletionOnceCallback());
   EXPECT_GT(read, 0);
   EXPECT_LE(read, written);
-  EXPECT_EQ(0, memcmp(kTestData, read_buf->data(), read));
+  EXPECT_EQ(kTestData.substr(0, read),
+            base::as_string_view(read_buf->first(read)));
 
   // Read then write.
   TestCompletionCallback callback;
@@ -344,15 +339,16 @@
             server.Read(read_buf.get(), kReadBufSize, callback.callback()));
 
   written =
-      client.Write(write_buf.get(), kTestDataSize, CompletionOnceCallback(),
+      client.Write(write_buf.get(), kTestData.size(), CompletionOnceCallback(),
                    TRAFFIC_ANNOTATION_FOR_TESTS);
   EXPECT_GT(written, 0);
-  EXPECT_LE(written, kTestDataSize);
+  EXPECT_LE(written, kTestData.size());
 
   read = callback.WaitForResult();
   EXPECT_GT(read, 0);
   EXPECT_LE(read, written);
-  EXPECT_EQ(0, memcmp(kTestData, read_buf->data(), read));
+  EXPECT_EQ(kTestData.substr(0, read),
+            base::as_string_view(read_buf->first(read)));
 }
 
 class SSLServerSocketTest : public PlatformTest, public WithTaskEnvironment {
@@ -478,12 +474,9 @@
     std::string key_string;
     if (!base::ReadFileToString(key_path, &key_string))
       return nullptr;
-    std::vector<uint8_t> key_vector(
-        reinterpret_cast<const uint8_t*>(key_string.data()),
-        reinterpret_cast<const uint8_t*>(key_string.data() +
-                                         key_string.length()));
     std::unique_ptr<crypto::RSAPrivateKey> key(
-        crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_vector));
+        crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(
+            base::as_byte_span(key_string)));
     return key;
   }
 
@@ -1058,7 +1051,7 @@
   }
   EXPECT_EQ(write_buf->size(), read_buf->BytesConsumed());
   read_buf->SetOffset(0);
-  EXPECT_EQ(0, memcmp(write_buf->data(), read_buf->data(), write_buf->size()));
+  EXPECT_EQ(write_buf->span(), read_buf->first(write_buf->size()));
 
   // Read then write.
   write_buf = base::MakeRefCounted<StringIOBuffer>("hello123");
@@ -1092,7 +1085,7 @@
   }
   EXPECT_EQ(write_buf->size(), read_buf->BytesConsumed());
   read_buf->SetOffset(0);
-  EXPECT_EQ(0, memcmp(write_buf->data(), read_buf->data(), write_buf->size()));
+  EXPECT_EQ(write_buf->span(), read_buf->first(write_buf->size()));
 }
 
 // A regression test for bug 127822 (http://crbug.com/127822).
diff --git a/net/socket/tcp_socket_unittest.cc b/net/socket/tcp_socket_unittest.cc
index c7ab0f0..56e4492e 100644
--- a/net/socket/tcp_socket_unittest.cc
+++ b/net/socket/tcp_socket_unittest.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "net/socket/tcp_socket.h"
 
 #include <stddef.h>
@@ -17,6 +12,7 @@
 #include <string>
 #include <vector>
 
+#include "base/containers/span.h"
 #include "base/functional/bind.h"
 #include "base/memory/ref_counted.h"
 #include "base/test/bind.h"
@@ -263,11 +259,10 @@
     for (size_t i = 0; i < num_messages; ++i) {
       // Use a 1 byte message so that the watcher is notified at most once per
       // message.
-      const std::string message("t");
+      static constexpr std::string_view message = "t";
 
-      scoped_refptr<IOBufferWithSize> write_buffer =
-          base::MakeRefCounted<IOBufferWithSize>(message.size());
-      memmove(write_buffer->data(), message.data(), message.size());
+      auto write_buffer =
+          base::MakeRefCounted<VectorIOBuffer>(base::as_byte_span(message));
 
       TestCompletionCallback write_callback;
       int write_result = accepted_socket->Write(
@@ -1208,12 +1203,22 @@
 
   // Wait until |connecting_socket| is signalled as having data to read.
   fd_set read_fds;
-  FD_ZERO(&read_fds);
+  // SAFETY: The implementations are different on different platforms. However,
+  // they are all operations on the read_fds object itself. No out-of-bounds
+  // behavior will occur.
+  UNSAFE_BUFFERS(FD_ZERO(&read_fds));
   SocketDescriptor connecting_fd =
       connecting_socket.SocketDescriptorForTesting();
-  FD_SET(connecting_fd, &read_fds);
+  // SAFETY: There is an fd array in read_fds, which has different sizes on
+  // different platforms, but is always greater than 1. It will record and check
+  // the number of current fds internally. Since the memory layout and structure
+  // of different platforms are inconsistent, we still use the system standard
+  // interface.
+  UNSAFE_BUFFERS(FD_SET(connecting_fd, &read_fds));
   ASSERT_EQ(select(FD_SETSIZE, &read_fds, nullptr, nullptr, nullptr), 1);
-  ASSERT_TRUE(FD_ISSET(connecting_fd, &read_fds));
+  // SAFETY: Check if this fd exists in read_fds. The system has already handled
+  // the edge cases.
+  ASSERT_TRUE(UNSAFE_BUFFERS(FD_ISSET(connecting_fd, &read_fds)));
 
   // It should now be reported as connected, but not as idle.
   EXPECT_TRUE(connecting_socket.IsConnected());
diff --git a/net/socket/transport_client_socket_pool_unittest.cc b/net/socket/transport_client_socket_pool_unittest.cc
index 31092486..037a1fb 100644
--- a/net/socket/transport_client_socket_pool_unittest.cc
+++ b/net/socket/transport_client_socket_pool_unittest.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "net/socket/transport_client_socket_pool.h"
 
 #include <memory>
@@ -83,26 +78,23 @@
 class SOCKS5MockData {
  public:
   explicit SOCKS5MockData(IoMode mode) {
-    writes_ = std::make_unique<MockWrite[]>(2);
     writes_[0] =
         MockWrite(mode, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength);
     writes_[1] = MockWrite(mode, kSOCKS5OkRequest, kSOCKS5OkRequestLength);
 
-    reads_ = std::make_unique<MockRead[]>(2);
     reads_[0] =
         MockRead(mode, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength);
     reads_[1] = MockRead(mode, kSOCKS5OkResponse, kSOCKS5OkResponseLength);
 
-    data_ = std::make_unique<StaticSocketDataProvider>(
-        base::span(reads_.get(), 2u), base::span(writes_.get(), 2u));
+    data_ = std::make_unique<StaticSocketDataProvider>(reads_, writes_);
   }
 
   SocketDataProvider* data_provider() { return data_.get(); }
 
  private:
   std::unique_ptr<StaticSocketDataProvider> data_;
-  std::unique_ptr<MockWrite[]> writes_;
-  std::unique_ptr<MockRead[]> reads_;
+  std::array<MockWrite, 2> writes_;
+  std::array<MockRead, 2> reads_;
 };
 
 class TransportClientSocketPoolTest : public ::testing::Test,
diff --git a/net/socket/udp_socket_posix.cc b/net/socket/udp_socket_posix.cc
index 2f236f2..e4eff3a 100644
--- a/net/socket/udp_socket_posix.cc
+++ b/net/socket/udp_socket_posix.cc
@@ -5,12 +5,6 @@
 #include "net/socket/udp_socket_posix.h"
 
 #include "base/notimplemented.h"
-
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "build/build_config.h"
 
 #if BUILDFLAG(IS_APPLE)
@@ -779,7 +773,9 @@
     last_tos_ = 0;
     if (bytes_transferred > 0 && msg.msg_controllen > 0) {
       for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr;
-           cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+           // SAFETY: Size and null pointer checks are done in system header
+           // files.
+           cmsg = UNSAFE_BUFFERS(CMSG_NXTHDR(&msg, cmsg))) {
 #if BUILDFLAG(IS_APPLE)
         if ((cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVTOS) ||
             (cmsg->cmsg_level == IPPROTO_IPV6 &&
@@ -789,7 +785,11 @@
             (cmsg->cmsg_level == IPPROTO_IPV6 &&
              cmsg->cmsg_type == IPV6_TCLASS)) {
 #endif  // BUILDFLAG(IS_APPLE)
-          last_tos_ = *(reinterpret_cast<uint8_t*>(CMSG_DATA(cmsg)));
+          auto cmsg_data_as_span =
+              // SAFETY: `CMSG_DATA` points to `control_buffer`. Its size is
+              // 512.
+              UNSAFE_BUFFERS(base::span(CMSG_DATA(cmsg), sizeof(uint8_t)));
+          base::byte_span_from_ref(last_tos_).copy_from(cmsg_data_as_span);
         }
       }
     }
diff --git a/net/socket/udp_socket_win.cc b/net/socket/udp_socket_win.cc
index 374e68c..5e446eb 100644
--- a/net/socket/udp_socket_win.cc
+++ b/net/socket/udp_socket_win.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "net/socket/udp_socket_win.h"
 
 #include <winsock2.h>
@@ -16,6 +11,7 @@
 #include <memory>
 
 #include "base/check_op.h"
+#include "base/containers/span.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/lazy_instance.h"
@@ -129,8 +125,8 @@
     : socket_(socket),
       reader_(this),
       writer_(this) {
-  memset(&read_overlapped_, 0, sizeof(read_overlapped_));
-  memset(&write_overlapped_, 0, sizeof(write_overlapped_));
+  FillOVERLAPPEDStruct(read_overlapped_, 0);
+  FillOVERLAPPEDStruct(write_overlapped_, 0);
 
   read_overlapped_.hEvent = WSACreateEvent();
   write_overlapped_.hEvent = WSACreateEvent();
@@ -142,9 +138,9 @@
   write_watcher_.StopWatching();
 
   WSACloseEvent(read_overlapped_.hEvent);
-  memset(&read_overlapped_, 0xaf, sizeof(read_overlapped_));
+  FillOVERLAPPEDStruct(read_overlapped_, 0xaf);
   WSACloseEvent(write_overlapped_.hEvent);
-  memset(&write_overlapped_, 0xaf, sizeof(write_overlapped_));
+  FillOVERLAPPEDStruct(write_overlapped_, 0xaf);
 }
 
 void UDPSocketWin::Core::WatchForRead() {
@@ -913,7 +909,21 @@
     cmsg->cmsg_len = WSA_CMSG_LEN(sizeof(int));
     cmsg->cmsg_level = is_ipv6 ? IPPROTO_IPV6 : IPPROTO_IP;
     cmsg->cmsg_type = is_ipv6 ? IPV6_ECN : IP_ECN;
-    *(int*)WSA_CMSG_DATA(cmsg) = static_cast<int>(send_ecn_);
+
+    DCHECK_LE(sizeof(int), control_buffer.len);
+    auto cmsg_data_as_span =
+        // SAFETY:
+        // https://learn.microsoft.com/en-us/windows/win32/api/ws2def/ns-ws2def-wsamsg
+        // The windows documentation says that WSA_CMSG_DATA is a pointer to the
+        // first byte of the data (called the cmsg_data member, although it's
+        // not defined in the structure). In the header file it is implied that
+        // it is an array of UCHAR.
+        //
+        // It actually points to `control_buffer`. So it is safe.
+        UNSAFE_BUFFERS(base::span(WSA_CMSG_DATA(cmsg), sizeof(int)));
+    const auto send_ecn_as_int = static_cast<int>(send_ecn_);
+    base::as_writable_byte_span(cmsg_data_as_span)
+        .copy_from(base::byte_span_from_ref(send_ecn_as_int));
   } else {
     message.Control.len = control_buffer.len;
   }
@@ -922,10 +932,17 @@
 void UDPSocketWin::SetLastTosFromWSAMSG(WSAMSG& message) {
   int ecn = 0;
   for (WSACMSGHDR* cmsg = WSA_CMSG_FIRSTHDR(&message); cmsg != NULL;
-       cmsg = WSA_CMSG_NXTHDR(&message, cmsg)) {
+       // SAFETY: The length and nullptr check are done in the WSA_CMSG_NXTHDR
+       // macro.
+       cmsg = UNSAFE_BUFFERS(WSA_CMSG_NXTHDR(&message, cmsg))) {
     if ((cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_ECN) ||
         (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_ECN)) {
-      ecn = *(int*)WSA_CMSG_DATA(cmsg);
+      auto cmsg_data_as_span =
+          // SAFETY: Same as above. Since all messages here are generated by
+          // `PopulateWSAMSG`, we ensure the size here in `PopulateWSAMSG`.
+          UNSAFE_BUFFERS(
+              base::span<UCHAR, 4>(WSA_CMSG_DATA(cmsg), sizeof(int)));
+      base::byte_span_from_ref(ecn).copy_from(cmsg_data_as_span);
       break;
     }
   }
@@ -954,9 +971,6 @@
     control_buffer.buf = core_->read_control_buffer_;
     control_buffer.len = sizeof(core_->read_control_buffer_);
     message = std::make_unique<WSAMSG>();
-    if (message == nullptr) {
-      return WSA_NOT_ENOUGH_MEMORY;
-    }
     PopulateWSAMSG(*message, storage, &read_buffer, control_buffer, false);
     rv = wsa_recv_msg_(socket_, message.get(), &num, &core_->read_overlapped_,
                        nullptr);
diff --git a/net/socket/unix_domain_client_socket_posix_unittest.cc b/net/socket/unix_domain_client_socket_posix_unittest.cc
index f4d0e2a3..a73cf105 100644
--- a/net/socket/unix_domain_client_socket_posix_unittest.cc
+++ b/net/socket/unix_domain_client_socket_posix_unittest.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "net/socket/unix_domain_client_socket_posix.h"
 
 #include <unistd.h>
@@ -353,7 +348,7 @@
   EXPECT_TRUE(client_socket.IsConnected());
 
   // Send data from client to server.
-  const int kWriteDataSize = 10;
+  const size_t kWriteDataSize = 10;
   auto write_buffer =
       base::MakeRefCounted<StringIOBuffer>(std::string(kWriteDataSize, 'd'));
   EXPECT_EQ(
@@ -361,7 +356,7 @@
       WriteSynchronously(&client_socket, write_buffer.get(), kWriteDataSize));
 
   // The buffer is bigger than write data size.
-  const int kReadBufferSize = kWriteDataSize * 2;
+  const size_t kReadBufferSize = kWriteDataSize * 2;
   auto read_buffer = base::MakeRefCounted<IOBufferWithSize>(kReadBufferSize);
   EXPECT_EQ(kWriteDataSize,
             ReadSynchronously(accepted_socket.get(),
@@ -377,24 +372,23 @@
                 accepted_socket.get(), write_buffer.get(), kWriteDataSize));
 
   // Read multiple times.
-  const int kSmallReadBufferSize = kWriteDataSize / 3;
+  const size_t kSmallReadBufferSize = kWriteDataSize / 3;
   EXPECT_EQ(kSmallReadBufferSize,
             ReadSynchronously(&client_socket,
                               read_buffer.get(),
                               kSmallReadBufferSize,
                               kSmallReadBufferSize));
-  EXPECT_EQ(std::string(write_buffer->data(), kSmallReadBufferSize),
-            std::string(read_buffer->data(), kSmallReadBufferSize));
+  EXPECT_EQ(write_buffer->first(kSmallReadBufferSize),
+            read_buffer->first(kSmallReadBufferSize));
 
   EXPECT_EQ(kWriteDataSize - kSmallReadBufferSize,
             ReadSynchronously(&client_socket,
                               read_buffer.get(),
                               kReadBufferSize,
                               kWriteDataSize - kSmallReadBufferSize));
-  EXPECT_EQ(std::string(write_buffer->data() + kSmallReadBufferSize,
-                        kWriteDataSize - kSmallReadBufferSize),
-            std::string(read_buffer->data(),
-                        kWriteDataSize - kSmallReadBufferSize));
+  EXPECT_EQ(write_buffer->span().subspan(kSmallReadBufferSize,
+                                         kWriteDataSize - kSmallReadBufferSize),
+            read_buffer->first(kWriteDataSize - kSmallReadBufferSize));
 
   // No more data.
   EXPECT_EQ(
diff --git a/services/network/public/cpp/content_security_policy/content_security_policy.cc b/services/network/public/cpp/content_security_policy/content_security_policy.cc
index a01d0ec6..358f76e 100644
--- a/services/network/public/cpp/content_security_policy/content_security_policy.cc
+++ b/services/network/public/cpp/content_security_policy/content_security_policy.cc
@@ -614,19 +614,28 @@
   return current;
 }
 
-bool ParseURLHash(std::string_view expression, mojom::CSPHashSource* hash) {
-  constexpr char prefix[] = "'url-";
-  constexpr size_t prefix_length = 5u;
+bool ParsePrefixedHash(std::string_view prefix,
+                       std::string_view expression,
+                       mojom::CSPHashSource* hash) {
   if (!base::StartsWith(expression, prefix,
                         base::CompareCase::INSENSITIVE_ASCII) ||
       expression[expression.length() - 1] != '\'') {
     return false;
   }
   return ParseUnquotedHash(
-      expression.substr(prefix_length, expression.length() - prefix_length - 1),
+      expression.substr(prefix.length(),
+                        expression.length() - prefix.length() - 1),
       hash);
 }
 
+bool ParseURLHash(std::string_view expression, mojom::CSPHashSource* hash) {
+  return ParsePrefixedHash("'url-", expression, hash);
+}
+
+bool ParseEvalHash(std::string_view expression, mojom::CSPHashSource* hash) {
+  return ParsePrefixedHash("'eval-", expression, hash);
+}
+
 // Parse source-list grammar.
 // https://www.w3.org/TR/CSP3/#grammardef-serialized-source-list
 // Append parsing errors to |parsing_errors|.
@@ -791,6 +800,20 @@
       continue;
     }
 
+    auto eval_hash = mojom::CSPHashSource::New();
+    if (ParseEvalHash(expression, eval_hash.get())) {
+      if (directive_name == CSPDirectiveName::ScriptSrcV2) {
+        directive->eval_hashes.push_back(std::move(eval_hash));
+      } else {
+        parsing_errors.emplace_back(base::StringPrintf(
+            "The Content-Security-Policy directive '%s' contains %s as a "
+            "source expression that is permitted only for 'script-src-v2' "
+            "directive. It will be ignored.",
+            ToString(directive_name).c_str(), std::string(expression).c_str()));
+      }
+      continue;
+    }
+
     // Parsing error.
     // Ignore this source-expression.
     parsing_errors.emplace_back(base::StringPrintf(
diff --git a/services/network/public/cpp/content_security_policy/content_security_policy_unittest.cc b/services/network/public/cpp/content_security_policy/content_security_policy_unittest.cc
index 466b1e79..49dabe73 100644
--- a/services/network/public/cpp/content_security_policy/content_security_policy_unittest.cc
+++ b/services/network/public/cpp/content_security_policy/content_security_policy_unittest.cc
@@ -1346,6 +1346,24 @@
           "'script-src-v2' directive. It will be ignored.",
       },
       {
+          mojom::CSPDirectiveName::ScriptSrc,
+          "'sha256-YWJj' 'nonce-cde' 'sha256-QUJD' 'eval-sha256-Y2Q='",
+          base::BindOnce([] {
+            auto csp = mojom::CSPSourceList::New();
+            csp->hashes.push_back(
+                mojom::CSPHashSource::New(mojom::IntegrityAlgorithm::kSha256,
+                                          std::vector<uint8_t>{'a', 'b', 'c'}));
+            csp->hashes.push_back(
+                mojom::CSPHashSource::New(mojom::IntegrityAlgorithm::kSha256,
+                                          std::vector<uint8_t>{'A', 'B', 'C'}));
+            csp->nonces.push_back("cde");
+            return csp;
+          }),
+          "The Content-Security-Policy directive 'script-src' contains "
+          "'eval-sha256-Y2Q=' as a source expression that is permitted only "
+          "for 'script-src-v2' directive. It will be ignored.",
+      },
+      {
           mojom::CSPDirectiveName::ScriptSrcV2,
           "'sha256-YWJj' 'nonce-cde' 'sha256-QUJD' 'url-sha256-Y2Q='",
           base::BindOnce([] {
@@ -1367,6 +1385,26 @@
       },
       {
           mojom::CSPDirectiveName::ScriptSrcV2,
+          "'sha256-YWJj' 'nonce-cde' 'sha256-QUJD' 'eval-sha256-Y2Q='",
+          base::BindOnce([] {
+            auto csp = mojom::CSPSourceList::New();
+            csp->hashes.push_back(
+                mojom::CSPHashSource::New(mojom::IntegrityAlgorithm::kSha256,
+                                          std::vector<uint8_t>{'a', 'b', 'c'}));
+            csp->hashes.push_back(
+                mojom::CSPHashSource::New(mojom::IntegrityAlgorithm::kSha256,
+                                          std::vector<uint8_t>{'A', 'B', 'C'}));
+            csp->nonces.push_back("cde");
+
+            csp->eval_hashes.push_back(
+                mojom::CSPHashSource::New(mojom::IntegrityAlgorithm::kSha256,
+                                          std::vector<uint8_t>{'c', 'd'}));
+            return csp;
+          }),
+          "",
+      },
+      {
+          mojom::CSPDirectiveName::ScriptSrcV2,
           "'sha256-YWJj' 'nonce-cde' 'sha256-QUJD' 'url-sha256-Y2Q=' "
           "https://a.com/",
           base::BindOnce([] {
diff --git a/testing/buildbot/filters/pixel_tests.filter b/testing/buildbot/filters/pixel_tests.filter
index 3f5fddc..a856639 100644
--- a/testing/buildbot/filters/pixel_tests.filter
+++ b/testing/buildbot/filters/pixel_tests.filter
@@ -58,6 +58,7 @@
 FirstRunIntroPixelTest.*
 GlobalErrorBubbleTest.*
 HatsBubbleTest.*
+HistorySyncOptinUIDialogPixelTest.*
 HomeButtonUiTest.*
 HungRendererDialogViewBrowserTest.*
 ImportLockDialogViewBrowserTest.*
diff --git a/testing/libfuzzer/README.md b/testing/libfuzzer/README.md
index 8db1c7ef..2fb06be 100644
--- a/testing/libfuzzer/README.md
+++ b/testing/libfuzzer/README.md
@@ -59,7 +59,8 @@
 ## Other Links
 * [Guided in-process fuzzing of Chrome components] blog post.
 * [ClusterFuzz Stats] for fuzz targets built with AddressSanitizer and
-  libFuzzer.
+* [http://go/chrome-fuzzing-dashboard](https://analysis.chromium.org/coverage/p/chromium?platform=fuzz&test_suite_type=any&path=%2F%2F&project=chromium%2Fsrc&path=%2F%2F&host=chromium.googlesource.com&ref=refs%2Fheads%2Fmain&modifier_id=0)
+  Code covered by fuzz tests.
 
 [Blackbox fuzzing]: https://google.github.io/clusterfuzz/setting-up-fuzzing/blackbox-fuzzing/
 [Bugs found in open-source projects]: http://llvm.org/docs/LibFuzzer.html#trophies
diff --git a/testing/libfuzzer/getting_started.md b/testing/libfuzzer/getting_started.md
index f34f460..38c5ae8 100644
--- a/testing/libfuzzer/getting_started.md
+++ b/testing/libfuzzer/getting_started.md
@@ -31,6 +31,10 @@
 will work a little better in these cases because if the fuzzer finds a problem,
 the test case will exactly match the binary format.)
 
+See
+[http://go/chrome-fuzzing-dashboard](https://analysis.chromium.org/coverage/p/chromium?platform=fuzz&test_suite_type=any&path=%2F%2F&project=chromium%2Fsrc&path=%2F%2F&host=chromium.googlesource.com&ref=refs%2Fheads%2Fmain&modifier_id=0)
+for which directories lack fuzz coverage.
+
 ## How to fuzz
 
 1. Find your existing unit test target. Create a new similar target
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 3801a22..25450c2 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1722,6 +1722,25 @@
             ]
         }
     ],
+    "AutofillAiTeamfoodInternal": [
+        {
+            "platforms": [
+                "chromeos",
+                "linux",
+                "mac",
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "Enabled",
+                    "enable_features": [
+                        "AutofillAiServerModel",
+                        "AutofillAiUploadModelRequestAndResponse"
+                    ]
+                }
+            ]
+        }
+    ],
     "AutofillAiTeamfoodV2": [
         {
             "platforms": [
@@ -23254,21 +23273,6 @@
             ]
         }
     ],
-    "SystemInfoAnswerCardsInLauncher": [
-        {
-            "platforms": [
-                "chromeos"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "LauncherSystemInfoAnswerCards"
-                    ]
-                }
-            ]
-        }
-    ],
     "TabAudioMuting": [
         {
             "platforms": [
diff --git a/third_party/androidx/build.gradle b/third_party/androidx/build.gradle
index 98dc36f..2dc5d93 100644
--- a/third_party/androidx/build.gradle
+++ b/third_party/androidx/build.gradle
@@ -305,7 +305,7 @@
     google()
     maven {
         // This URL is generated by the fetch_all_androidx.py script.
-        url 'https://androidx.dev/snapshots/builds/13513352/artifacts/repository'
+        url 'https://androidx.dev/snapshots/builds/13515081/artifacts/repository'
     }
     mavenCentral()
 }
diff --git a/third_party/angle b/third_party/angle
index 5fd79e6..9633d8f 160000
--- a/third_party/angle
+++ b/third_party/angle
@@ -1 +1 @@
-Subproject commit 5fd79e6a2ce891a7e4e965631d4b53e921170638
+Subproject commit 9633d8f745bfb1feebefc3578e188ff3b960983a
diff --git a/third_party/blink/renderer/core/css/container_query_evaluator.cc b/third_party/blink/renderer/core/css/container_query_evaluator.cc
index d9cfb84..6befe5c 100644
--- a/third_party/blink/renderer/core/css/container_query_evaluator.cc
+++ b/third_party/blink/renderer/core/css/container_query_evaluator.cc
@@ -138,7 +138,8 @@
       ContainerStuckPhysical::kNo, ContainerStuckPhysical::kNo, snapped_,
       static_cast<ContainerScrollableFlags>(ContainerScrollable::kNone),
       static_cast<ContainerScrollableFlags>(ContainerScrollable::kNone),
-      ContainerScrollDirection::kNone, ContainerScrollDirection::kNone);
+      ContainerScrollDirection::kNone, ContainerScrollDirection::kNone,
+      /*anchored_fallback=*/0);
   media_query_evaluator_ =
       MakeGarbageCollected<MediaQueryEvaluator>(query_values);
 }
@@ -402,10 +403,10 @@
   if (scroll_state_snapshot_) {
     change = StickyContainerChanged(scroll_state_snapshot_->StuckHorizontal(),
                                     scroll_state_snapshot_->StuckVertical());
-    Change overflow_change = ScrollableContainerChanged(
+    Change scrollable_change = ScrollableContainerChanged(
         scroll_state_snapshot_->ScrollableHorizontal(),
         scroll_state_snapshot_->ScrollableVertical());
-    change = std::max(change, overflow_change);
+    change = std::max(change, scrollable_change);
     if (RuntimeEnabledFeatures::CSSScrollDirectionContainerQueriesEnabled()) {
       Change scroll_direction_change = ScrollDirectionContainerChanged(
           scroll_state_snapshot_->ScrollDirectionHorizontal(),
@@ -460,7 +461,7 @@
   }
 
   UpdateContainerScrollable(scrollable_horizontal, scrollable_vertical);
-  Change change = ComputeOverflowChange();
+  Change change = ComputeScrollableChange();
   if (change != Change::kNone) {
     ClearResults(change, kScrollableContainer);
   }
@@ -487,6 +488,21 @@
   return change;
 }
 
+// Re-evaluate the cached results and clear any results which are affected by
+// the anchored fallback changes.
+ContainerQueryEvaluator::Change
+ContainerQueryEvaluator::AnchoredContainerChanged(int anchored_fallback) {
+  if (anchored_fallback_ == anchored_fallback) {
+    return Change::kNone;
+  }
+  UpdateAnchoredFallback(anchored_fallback);
+  Change change = ComputeAnchoredChange();
+  if (change != Change::kNone) {
+    ClearResults(change, kAnchoredContainer);
+  }
+  return change;
+}
+
 ContainerQueryEvaluator::Change
 ContainerQueryEvaluator::StyleContainerChanged() {
   if (!depends_on_style_) {
@@ -527,11 +543,11 @@
   }
   change = std::max(change, sticky_change);
 
-  Change overflow_change = ComputeOverflowChange();
-  if (overflow_change != Change::kNone) {
-    ClearResults(overflow_change, kScrollableContainer);
+  Change scrollable_change = ComputeScrollableChange();
+  if (scrollable_change != Change::kNone) {
+    ClearResults(scrollable_change, kScrollableContainer);
   }
-  change = std::max(change, overflow_change);
+  change = std::max(change, scrollable_change);
 
   if (RuntimeEnabledFeatures::CSSScrollDirectionContainerQueriesEnabled()) {
     Change scroll_direction_change = ComputeScrollDirectionChange();
@@ -553,7 +569,8 @@
       existing_values.ScrollableHorizontal(),
       existing_values.ScrollableVertical(),
       existing_values.ScrollDirectionHorizontal(),
-      existing_values.ScrollDirectionVertical());
+      existing_values.ScrollDirectionVertical(),
+      existing_values.AnchoredFallback());
   media_query_evaluator_ =
       MakeGarbageCollected<MediaQueryEvaluator>(query_values);
 }
@@ -598,7 +615,8 @@
       existing_values.SnappedFlags(), existing_values.ScrollableHorizontal(),
       existing_values.ScrollableVertical(),
       existing_values.ScrollDirectionHorizontal(),
-      existing_values.ScrollDirectionVertical());
+      existing_values.ScrollDirectionVertical(),
+      existing_values.AnchoredFallback());
   media_query_evaluator_ =
       MakeGarbageCollected<MediaQueryEvaluator>(query_values);
 }
@@ -618,7 +636,8 @@
       existing_values.SnappedFlags(), existing_values.ScrollableHorizontal(),
       existing_values.ScrollableVertical(),
       existing_values.ScrollDirectionHorizontal(),
-      existing_values.ScrollDirectionVertical());
+      existing_values.ScrollDirectionVertical(),
+      existing_values.AnchoredFallback());
   media_query_evaluator_ =
       MakeGarbageCollected<MediaQueryEvaluator>(query_values);
 }
@@ -637,7 +656,8 @@
       existing_values.ScrollableHorizontal(),
       existing_values.ScrollableVertical(),
       existing_values.ScrollDirectionHorizontal(),
-      existing_values.ScrollDirectionVertical());
+      existing_values.ScrollDirectionVertical(),
+      existing_values.AnchoredFallback());
   media_query_evaluator_ =
       MakeGarbageCollected<MediaQueryEvaluator>(query_values);
 }
@@ -657,7 +677,8 @@
       existing_values.StuckVertical(), existing_values.Snapped(),
       scrollable_horizontal, scrollable_vertical,
       existing_values.ScrollDirectionHorizontal(),
-      existing_values.ScrollDirectionVertical());
+      existing_values.ScrollDirectionVertical(),
+      existing_values.AnchoredFallback());
   media_query_evaluator_ =
       MakeGarbageCollected<MediaQueryEvaluator>(query_values);
 }
@@ -677,7 +698,25 @@
       existing_values.StuckVertical(), existing_values.Snapped(),
       existing_values.ScrollableHorizontal(),
       existing_values.ScrollableVertical(), scroll_direction_horizontal,
-      scroll_direction_vertical);
+      scroll_direction_vertical, existing_values.AnchoredFallback());
+  media_query_evaluator_ =
+      MakeGarbageCollected<MediaQueryEvaluator>(query_values);
+}
+
+void ContainerQueryEvaluator::UpdateAnchoredFallback(int anchored_fallback) {
+  anchored_fallback_ = anchored_fallback;
+
+  const MediaValues& existing_values = media_query_evaluator_->GetMediaValues();
+  Element* container = existing_values.ContainerElement();
+
+  auto* query_values = MakeGarbageCollected<CSSContainerValues>(
+      container->GetDocument(), *container, existing_values.Width(),
+      existing_values.Height(), existing_values.StuckHorizontal(),
+      existing_values.StuckVertical(), existing_values.Snapped(),
+      existing_values.ScrollableHorizontal(),
+      existing_values.ScrollableVertical(),
+      existing_values.ScrollDirectionHorizontal(),
+      existing_values.ScrollDirectionVertical(), anchored_fallback);
   media_query_evaluator_ =
       MakeGarbageCollected<MediaQueryEvaluator>(query_values);
 }
@@ -710,6 +749,8 @@
          (RuntimeEnabledFeatures::CSSScrollDirectionContainerQueriesEnabled() &&
           container_type == kScrollDirectionContainer &&
           pair.key->Selector().SelectsScrollDirectionContainers()) ||
+         (container_type == kAnchoredContainer &&
+          pair.key->Selector().SelectsAnchoredContainers()) ||
          (container_type == kStyleContainer &&
           pair.key->Selector().SelectsStyleContainers()))) {
       continue;
@@ -792,8 +833,8 @@
   return change;
 }
 
-ContainerQueryEvaluator::Change ContainerQueryEvaluator::ComputeOverflowChange()
-    const {
+ContainerQueryEvaluator::Change
+ContainerQueryEvaluator::ComputeScrollableChange() const {
   Change change = Change::kNone;
 
   for (const auto& result : results_) {
@@ -828,6 +869,24 @@
   return change;
 }
 
+ContainerQueryEvaluator::Change ContainerQueryEvaluator::ComputeAnchoredChange()
+    const {
+  Change change = Change::kNone;
+
+  for (const auto& result : results_) {
+    const ContainerQuery& query = *result.key;
+    if (!query.Selector().SelectsAnchoredContainers()) {
+      continue;
+    }
+    if (Eval(query).value == result.value.value) {
+      continue;
+    }
+    change = std::max(result.value.change, change);
+  }
+
+  return change;
+}
+
 void ContainerQueryEvaluator::UpdateContainerValuesFromUnitChanges(
     StyleRecalcChange change) {
   CHECK(media_query_evaluator_);
@@ -847,6 +906,23 @@
   UpdateContainerValues();
 }
 
+StyleRecalcChange ContainerQueryEvaluator::ApplyAnchoredChanges(
+    const StyleRecalcChange& child_change,
+    std::optional<wtf_size_t> try_fallback_index) {
+  int anchored_fallback = 0;
+  if (try_fallback_index.has_value()) {
+    anchored_fallback = ClampTo<int>(try_fallback_index.value()) + 1;
+  }
+  switch (AnchoredContainerChanged(anchored_fallback)) {
+    case ContainerQueryEvaluator::Change::kNone:
+      return child_change;
+    case ContainerQueryEvaluator::Change::kNearestContainer:
+      return child_change.ForceRecalcAnchoredContainer();
+    case ContainerQueryEvaluator::Change::kDescendantContainers:
+      return child_change.ForceRecalcDescendantAnchoredContainers();
+  }
+}
+
 StyleRecalcChange ContainerQueryEvaluator::ApplyScrollStateAndStyleChanges(
     const StyleRecalcChange& child_change,
     const ComputedStyle& old_style,
diff --git a/third_party/blink/renderer/core/css/container_query_evaluator.h b/third_party/blink/renderer/core/css/container_query_evaluator.h
index 9a1bacb..9fcca655 100644
--- a/third_party/blink/renderer/core/css/container_query_evaluator.h
+++ b/third_party/blink/renderer/core/css/container_query_evaluator.h
@@ -116,6 +116,13 @@
       const ComputedStyle& new_style,
       bool style_changed);
 
+  // Update which of position-try-fallbacks is used, if any. A nullopt means
+  // none of the fallbacks are applied. Otherwise, an index into the computed
+  // position-try-fallbacks.
+  StyleRecalcChange ApplyAnchoredChanges(
+      const StyleRecalcChange& child_change,
+      std::optional<wtf_size_t> try_fallback_index);
+
   // Set the pending snapped state when updating scroll snapshots.
   // ApplyScrollState() will set the snapped state from the pending snapped
   // state during style recalc.
@@ -152,6 +159,10 @@
   // computed style changes like writing direction.
   Change StyleAffectingScrollStateChanged();
 
+  // Re-evaluate the cached results and clear any results which are affected by
+  // the anchored fallback changes.
+  Change AnchoredContainerChanged(int fallback);
+
   // Update the CSSContainerValues with the new size and contained axes to be
   // used for queries.
   void UpdateContainerSize(PhysicalSize, PhysicalAxes contained_axes);
@@ -163,7 +174,7 @@
   // Update the CSSContainerValues with the new stuck state.
   void UpdateContainerSnapped(ContainerSnappedFlags snapped);
 
-  // Update the CSSContainerValues with the new overflowing state.
+  // Update the CSSContainerValues with the new scrollable state.
   void UpdateContainerScrollable(ContainerScrollableFlags scrollable_horizontal,
                                  ContainerScrollableFlags scrollable_vertical);
 
@@ -172,6 +183,9 @@
       ContainerScrollDirection scroll_direction_horizontal,
       ContainerScrollDirection scroll_direction_vertical);
 
+  // Update the CSSContainerValues with the new anchored fallback.
+  void UpdateAnchoredFallback(int anchored_fallback);
+
   // Re-evaluate the cached results and clear any results which are affected by
   // the ContainerStuckPhysical changes.
   Change StickyContainerChanged(ContainerStuckPhysical stuck_horizontal,
@@ -200,6 +214,7 @@
     kSnapContainer,
     kScrollableContainer,
     kScrollDirectionContainer,
+    kAnchoredContainer,
   };
   void ClearResults(Change change, ContainerType container_type);
 
@@ -219,14 +234,18 @@
   // which elements need to be invalidated if necessary.
   Change ComputeSnapChange() const;
 
-  // Re-evaluate cached query results after a overflowing state change and
+  // Re-evaluate cached query results after a scrollable state change and
   // return which elements need to be invalidated if necessary.
-  Change ComputeOverflowChange() const;
+  Change ComputeScrollableChange() const;
 
   // Re-evaluate cached query results after a scroll-direction state change and
   // return which elements need to be invalidated if necessary.
   Change ComputeScrollDirectionChange() const;
 
+  // Re-evaluate cached query results after an anchored(fallback) change and
+  // return which elements need to be invalidated if necessary.
+  Change ComputeAnchoredChange() const;
+
   struct Result {
     // Main evaluation result.
     bool value = false;
@@ -256,6 +275,8 @@
       ContainerScrollDirection::kNone;
   ContainerScrollDirection scroll_direction_vertical_ =
       ContainerScrollDirection::kNone;
+  int anchored_fallback_ = 0;
+
   HeapHashMap<Member<const ContainerQuery>, Result> results_;
   Member<ScrollStateQuerySnapshot> scroll_state_snapshot_;
   // The MediaQueryExpValue::UnitFlags of all queries evaluated against this
diff --git a/third_party/blink/renderer/core/css/container_query_test.cc b/third_party/blink/renderer/core/css/container_query_test.cc
index 7ed21ab..7ebbaf5 100644
--- a/third_party/blink/renderer/core/css/container_query_test.cc
+++ b/third_party/blink/renderer/core/css/container_query_test.cc
@@ -640,13 +640,13 @@
     PostStyleUpdateScope post_style_update_scope(GetDocument());
 
     // Should transition between [10px, 20px]. (Intermediate round).
-    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
         *container, LogicalSize(120, -1), kLogicalAxesInline);
     EXPECT_EQ("15px", ComputedValueString(target, "height"));
     EXPECT_EQ(0u, GetAnimationsCount(target));
 
     // Should transition between [10px, 30px]. (Intermediate round).
-    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
         *container, LogicalSize(130, -1), kLogicalAxesInline);
     EXPECT_EQ("20px", ComputedValueString(target, "height"));
     EXPECT_EQ(0u, GetAnimationsCount(target));
@@ -711,13 +711,13 @@
     PostStyleUpdateScope post_style_update_scope(GetDocument());
 
     // No transition property present. (Intermediate round).
-    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
         *container, LogicalSize(120, -1), kLogicalAxesInline);
     EXPECT_EQ("20px", ComputedValueString(target, "height"));
     EXPECT_EQ(0u, GetAnimationsCount(target));
 
     // Still no transition property present. (Intermediate round).
-    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
         *container, LogicalSize(130, -1), kLogicalAxesInline);
     EXPECT_EQ("30px", ComputedValueString(target, "height"));
     EXPECT_EQ(0u, GetAnimationsCount(target));
@@ -782,13 +782,13 @@
     PostStyleUpdateScope post_style_update_scope(GetDocument());
 
     // No transition property present yet. (Intermediate round).
-    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
         *container, LogicalSize(120, -1), kLogicalAxesInline);
     EXPECT_EQ("20px", ComputedValueString(target, "height"));
     EXPECT_EQ(0u, GetAnimationsCount(target));
 
     // Transition between [10px, 90px]. (Intermediate round).
-    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
         *container, LogicalSize(130, -1), kLogicalAxesInline);
     EXPECT_EQ("50px", ComputedValueString(target, "height"));
     EXPECT_EQ(0u, GetAnimationsCount(target));
@@ -853,13 +853,13 @@
     PostStyleUpdateScope post_style_update_scope(GetDocument());
 
     // Animation at 20%. (Intermediate round).
-    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
         *container, LogicalSize(120, -1), kLogicalAxesInline);
     EXPECT_EQ("20px", ComputedValueString(target, "height"));
     EXPECT_EQ(0u, GetAnimationsCount(target));
 
     // Animation at 30%. (Intermediate round).
-    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
         *container, LogicalSize(130, -1), kLogicalAxesInline);
     EXPECT_EQ("30px", ComputedValueString(target, "height"));
     EXPECT_EQ(0u, GetAnimationsCount(target));
@@ -922,7 +922,7 @@
     PostStyleUpdateScope post_style_update_scope(GetDocument());
 
     // Animation should appear to be canceled. (Intermediate round).
-    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+    GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
         *container, LogicalSize(130, -1), kLogicalAxesInline);
     EXPECT_EQ("auto", ComputedValueString(target, "height"));
     EXPECT_EQ(1u, GetAnimationsCount(target));
diff --git a/third_party/blink/renderer/core/css/container_state.h b/third_party/blink/renderer/core/css/container_state.h
index 5b4ba25..ebeeea3 100644
--- a/third_party/blink/renderer/core/css/container_state.h
+++ b/third_party/blink/renderer/core/css/container_state.h
@@ -59,18 +59,18 @@
 
 using ContainerScrollableFlags = unsigned;
 
-inline ContainerScrollableFlags Flip(ContainerScrollableFlags overflowing) {
-  if (overflowing ==
+inline ContainerScrollableFlags Flip(ContainerScrollableFlags scrollable) {
+  if (scrollable ==
       static_cast<ContainerScrollableFlags>(ContainerScrollable::kNone)) {
-    return overflowing;
+    return scrollable;
   }
   ContainerScrollableFlags flipped =
       static_cast<ContainerScrollableFlags>(ContainerScrollable::kNone);
-  if (overflowing &
+  if (scrollable &
       static_cast<ContainerScrollableFlags>(ContainerScrollable::kStart)) {
     flipped |= static_cast<ContainerScrollableFlags>(ContainerScrollable::kEnd);
   }
-  if (overflowing &
+  if (scrollable &
       static_cast<ContainerScrollableFlags>(ContainerScrollable::kEnd)) {
     flipped |=
         static_cast<ContainerScrollableFlags>(ContainerScrollable::kStart);
diff --git a/third_party/blink/renderer/core/css/css_container_values.cc b/third_party/blink/renderer/core/css/css_container_values.cc
index 74deb2d..c1febd9 100644
--- a/third_party/blink/renderer/core/css/css_container_values.cc
+++ b/third_party/blink/renderer/core/css/css_container_values.cc
@@ -23,7 +23,8 @@
     ContainerScrollableFlags scrollable_horizontal,
     ContainerScrollableFlags scrollable_vertical,
     ContainerScrollDirection scroll_direction_horizontal,
-    ContainerScrollDirection scroll_direction_vertical)
+    ContainerScrollDirection scroll_direction_vertical,
+    int anchored_fallback)
     : MediaValuesDynamic(document.GetFrame()),
       element_(&container),
       width_(width),
@@ -36,6 +37,7 @@
       scrollable_vertical_(scrollable_vertical),
       scroll_direction_horizontal_(scroll_direction_horizontal),
       scroll_direction_vertical_(scroll_direction_vertical),
+      anchored_fallback_(anchored_fallback),
       font_sizes_(CSSToLengthConversionData::FontSizes(
           container.ComputedStyleRef().GetFontSizeStyle(),
           document.documentElement()->GetComputedStyle())),
diff --git a/third_party/blink/renderer/core/css/css_container_values.h b/third_party/blink/renderer/core/css/css_container_values.h
index a7de4fd..2157dc18 100644
--- a/third_party/blink/renderer/core/css/css_container_values.h
+++ b/third_party/blink/renderer/core/css/css_container_values.h
@@ -26,7 +26,8 @@
       ContainerScrollableFlags scrollable_horizontal,
       ContainerScrollableFlags scrollable_vertical,
       ContainerScrollDirection scroll_direction_horizontal,
-      ContainerScrollDirection scroll_direction_vertical);
+      ContainerScrollDirection scroll_direction_vertical,
+      int anchored_fallback);
 
   // Returns std::nullopt if queries on the relevant axis is not
   // supported.
@@ -83,7 +84,7 @@
   ContainerScrollDirection ScrollDirectionInline() const override;
   ContainerScrollDirection ScrollDirectionBlock() const override;
 
-  int PositionTryFallback() const override { return position_try_fallback_; }
+  int AnchoredFallback() const override { return anchored_fallback_; }
 
  private:
   // The current computed style for the container.
@@ -113,9 +114,9 @@
       ContainerScrollDirection::kNone;
   ContainerScrollDirection scroll_direction_vertical_ =
       ContainerScrollDirection::kNone;
-  // A 1-based index into the position-try-fallback applied to an anchored()
+  // A 1-based index into position-try-fallbacks applied to an anchored()
   // container. 0 if no position-try-fallbacks are applied.
-  int position_try_fallback_ = 0;
+  int anchored_fallback_ = 0;
   // Container font sizes for resolving relative lengths.
   CSSToLengthConversionData::FontSizes font_sizes_;
   // LineHeightSize of the container element.
diff --git a/third_party/blink/renderer/core/css/css_container_values_test.cc b/third_party/blink/renderer/core/css/css_container_values_test.cc
index 0e7b7fb7..c3a8c68 100644
--- a/third_party/blink/renderer/core/css/css_container_values_test.cc
+++ b/third_party/blink/renderer/core/css/css_container_values_test.cc
@@ -39,7 +39,8 @@
         static_cast<ContainerSnappedFlags>(ContainerSnapped::kNone),
         static_cast<ContainerScrollableFlags>(ContainerScrollable::kNone),
         static_cast<ContainerScrollableFlags>(ContainerScrollable::kNone),
-        ContainerScrollDirection::kNone, ContainerScrollDirection::kNone);
+        ContainerScrollDirection::kNone, ContainerScrollDirection::kNone,
+        /*anchored_fallback=*/0);
   }
 
   CSSContainerValues* CreateSnappedValues(ContainerSnappedFlags snapped) {
@@ -48,7 +49,8 @@
         ContainerStuckPhysical::kNo, ContainerStuckPhysical::kNo, snapped,
         static_cast<ContainerScrollableFlags>(ContainerScrollable::kNone),
         static_cast<ContainerScrollableFlags>(ContainerScrollable::kNone),
-        ContainerScrollDirection::kNone, ContainerScrollDirection::kNone);
+        ContainerScrollDirection::kNone, ContainerScrollDirection::kNone,
+        /*anchored_fallback=*/0);
   }
 
   CSSContainerValues* CreateScrollableValues(
@@ -59,7 +61,8 @@
         ContainerStuckPhysical::kNo, ContainerStuckPhysical::kNo,
         static_cast<ContainerSnappedFlags>(ContainerSnapped::kNone), horizontal,
         vertical, ContainerScrollDirection::kNone,
-        ContainerScrollDirection::kNone);
+        ContainerScrollDirection::kNone,
+        /*anchored_fallback=*/0);
   }
 
   CSSContainerValues* CreateScrollDirectionValues(
@@ -71,7 +74,7 @@
         static_cast<ContainerSnappedFlags>(ContainerSnapped::kNone),
         static_cast<ContainerScrollableFlags>(ContainerScrollable::kNone),
         static_cast<ContainerScrollableFlags>(ContainerScrollable::kNone),
-        horizontal, vertical);
+        horizontal, vertical, /*anchored_fallback=*/0);
   }
 
  private:
diff --git a/third_party/blink/renderer/core/css/css_custom_ident_value.cc b/third_party/blink/renderer/core/css/css_custom_ident_value.cc
index efb4827..3a9a549 100644
--- a/third_party/blink/renderer/core/css/css_custom_ident_value.cc
+++ b/third_party/blink/renderer/core/css/css_custom_ident_value.cc
@@ -4,7 +4,11 @@
 
 #include "third_party/blink/renderer/core/css/css_custom_ident_value.h"
 
+#include "base/memory/values_equivalent.h"
+#include "third_party/blink/renderer/core/css/css_function_value.h"
 #include "third_party/blink/renderer/core/css/css_markup.h"
+#include "third_party/blink/renderer/core/css/css_primitive_value.h"
+#include "third_party/blink/renderer/core/css/css_string_value.h"
 #include "third_party/blink/renderer/core/css/properties/css_unresolved_property.h"
 #include "third_party/blink/renderer/core/dom/tree_scope.h"
 #include "third_party/blink/renderer/core/style/scoped_css_name.h"
@@ -31,11 +35,44 @@
   needs_tree_scope_population_ = false;
 }
 
+CSSCustomIdentValue::CSSCustomIdentValue(const CSSFunctionValue& ident_function)
+    : CSSValue(kCustomIdentClass),
+      ident_function_(&ident_function),
+      property_id_(CSSPropertyID::kInvalid) {
+  needs_tree_scope_population_ = true;
+}
+
+AtomicString CSSCustomIdentValue::ComputeIdent(
+    const CSSLengthResolver& length_resolver) const {
+  if (!ident_function_) {
+    return string_;
+  }
+
+  StringBuilder builder;
+
+  for (const Member<const CSSValue>& item : *ident_function_) {
+    if (auto* string_value = DynamicTo<CSSStringValue>(*item)) {
+      builder.Append(string_value->Value());
+    } else if (auto* custom_ident = DynamicTo<CSSCustomIdentValue>(*item)) {
+      builder.Append(custom_ident->ComputeIdent(length_resolver));
+    } else if (auto* primitive = DynamicTo<CSSPrimitiveValue>(*item)) {
+      builder.AppendNumber(primitive->ComputeInteger(length_resolver));
+    } else {
+      NOTREACHED();
+    }
+  }
+
+  return AtomicString(builder.ReleaseString());
+}
+
 String CSSCustomIdentValue::CustomCSSText() const {
   if (IsKnownPropertyID()) {
     return CSSUnresolvedProperty::Get(property_id_)
         .GetPropertyNameAtomicString();
   }
+  if (ident_function_) {
+    return ident_function_->CustomCSSText();
+  }
   StringBuilder builder;
   SerializeIdentifier(string_, builder);
   return builder.ReleaseString();
@@ -44,6 +81,8 @@
 unsigned CSSCustomIdentValue::CustomHash() const {
   if (IsKnownPropertyID()) {
     return WTF::HashInt(property_id_);
+  } else if (ident_function_) {
+    return WTF::HashPointer(ident_function_.Get());
   } else {
     return string_.Hash();
   }
@@ -59,8 +98,19 @@
   return *populated;
 }
 
+bool CSSCustomIdentValue::Equals(const CSSCustomIdentValue& other) const {
+  if (IsKnownPropertyID()) {
+    return property_id_ == other.property_id_;
+  }
+  return IsScopedValue() == other.IsScopedValue() &&
+         tree_scope_ == other.tree_scope_ &&
+         base::ValuesEquivalent(ident_function_, other.ident_function_) &&
+         string_ == other.string_;
+}
+
 void CSSCustomIdentValue::TraceAfterDispatch(blink::Visitor* visitor) const {
   visitor->Trace(tree_scope_);
+  visitor->Trace(ident_function_);
   CSSValue::TraceAfterDispatch(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/css/css_custom_ident_value.h b/third_party/blink/renderer/core/css/css_custom_ident_value.h
index 7586e85..bf3eb1c 100644
--- a/third_party/blink/renderer/core/css/css_custom_ident_value.h
+++ b/third_party/blink/renderer/core/css/css_custom_ident_value.h
@@ -13,20 +13,24 @@
 
 namespace blink {
 
-class TreeScope;
+class CSSFunctionValue;
+class CSSLengthResolver;
 class ScopedCSSName;
+class TreeScope;
 
 class CORE_EXPORT CSSCustomIdentValue : public CSSValue {
  public:
   explicit CSSCustomIdentValue(const AtomicString&);
   explicit CSSCustomIdentValue(CSSPropertyID);
   explicit CSSCustomIdentValue(const ScopedCSSName&);
+  explicit CSSCustomIdentValue(const CSSFunctionValue& ident_function);
 
   const TreeScope* GetTreeScope() const { return tree_scope_.Get(); }
   const AtomicString& Value() const {
     DCHECK(!IsKnownPropertyID());
     return string_;
   }
+  AtomicString ComputeIdent(const CSSLengthResolver&) const;
   bool IsKnownPropertyID() const {
     return property_id_ != CSSPropertyID::kInvalid;
   }
@@ -40,18 +44,13 @@
 
   const CSSCustomIdentValue& PopulateWithTreeScope(const TreeScope*) const;
 
-  bool Equals(const CSSCustomIdentValue& other) const {
-    if (IsKnownPropertyID()) {
-      return property_id_ == other.property_id_;
-    }
-    return IsScopedValue() == other.IsScopedValue() &&
-           tree_scope_ == other.tree_scope_ && string_ == other.string_;
-  }
+  bool Equals(const CSSCustomIdentValue& other) const;
 
   void TraceAfterDispatch(blink::Visitor*) const;
 
  private:
   WeakMember<const TreeScope> tree_scope_;
+  Member<const CSSFunctionValue> ident_function_;
   AtomicString string_;
   CSSPropertyID property_id_;
 };
diff --git a/third_party/blink/renderer/core/css/css_function_value.h b/third_party/blink/renderer/core/css/css_function_value.h
index 69b8c15..1f5dd238 100644
--- a/third_party/blink/renderer/core/css/css_function_value.h
+++ b/third_party/blink/renderer/core/css/css_function_value.h
@@ -20,6 +20,12 @@
   CSSFunctionValue(CSSValueID id)
       : CSSValueList(kFunctionClass, kCommaSeparator), value_id_(id) {}
 
+  CSSFunctionValue(CSSValueID id,
+                   ValueListSeparator argument_separator,
+                   HeapVector<Member<const CSSValue>, 4> values)
+      : CSSValueList(kFunctionClass, argument_separator, std::move(values)),
+        value_id_(id) {}
+
   WTF::String CustomCSSText() const;
 
   bool Equals(const CSSFunctionValue& other) const {
diff --git a/third_party/blink/renderer/core/css/css_value_keywords.json5 b/third_party/blink/renderer/core/css/css_value_keywords.json5
index f3017a64..2357042 100644
--- a/third_party/blink/renderer/core/css/css_value_keywords.json5
+++ b/third_party/blink/renderer/core/css/css_value_keywords.json5
@@ -1997,6 +1997,9 @@
     "sibling-count",
     "sibling-index",
 
+    // https://drafts.csswg.org/css-values-5/#ident
+    "ident",
+
     // @function
     "returns",
 
diff --git a/third_party/blink/renderer/core/css/css_value_list.cc b/third_party/blink/renderer/core/css/css_value_list.cc
index 3a300724..323cad2b5 100644
--- a/third_party/blink/renderer/core/css/css_value_list.cc
+++ b/third_party/blink/renderer/core/css/css_value_list.cc
@@ -43,7 +43,12 @@
 
 CSSValueList::CSSValueList(ValueListSeparator list_separator,
                            HeapVector<Member<const CSSValue>, 4> values)
-    : CSSValue(kValueListClass), values_(std::move(values)) {
+    : CSSValueList(kValueListClass, list_separator, std::move(values)) {}
+
+CSSValueList::CSSValueList(ClassType class_type,
+                           ValueListSeparator list_separator,
+                           HeapVector<Member<const CSSValue>, 4> values)
+    : CSSValue(class_type), values_(std::move(values)) {
   value_list_separator_ = list_separator;
 }
 
diff --git a/third_party/blink/renderer/core/css/css_value_list.h b/third_party/blink/renderer/core/css/css_value_list.h
index eb4af579..fd14171 100644
--- a/third_party/blink/renderer/core/css/css_value_list.h
+++ b/third_party/blink/renderer/core/css/css_value_list.h
@@ -56,6 +56,9 @@
   CSSValueList(ClassType, ValueListSeparator);
   explicit CSSValueList(ValueListSeparator);
   CSSValueList(ValueListSeparator, HeapVector<Member<const CSSValue>, 4>);
+  CSSValueList(ClassType,
+               ValueListSeparator,
+               HeapVector<Member<const CSSValue>, 4>);
   CSSValueList(const CSSValueList&) = delete;
   CSSValueList& operator=(const CSSValueList&) = delete;
 
diff --git a/third_party/blink/renderer/core/css/media_query_evaluator.cc b/third_party/blink/renderer/core/css/media_query_evaluator.cc
index e25525ec..d0b3a721 100644
--- a/third_party/blink/renderer/core/css/media_query_evaluator.cc
+++ b/third_party/blink/renderer/core/css/media_query_evaluator.cc
@@ -1663,7 +1663,7 @@
 static bool FallbackMediaFeatureEval(const MediaQueryExpValue& value,
                                      MediaQueryOperator op,
                                      const MediaValues& media_values) {
-  const int fallback = media_values.PositionTryFallback();
+  const int fallback = media_values.AnchoredFallback();
   if (!value.IsValid()) {
     return fallback == 0;
   }
diff --git a/third_party/blink/renderer/core/css/media_values.h b/third_party/blink/renderer/core/css/media_values.h
index 9743739c..184cc27 100644
--- a/third_party/blink/renderer/core/css/media_values.h
+++ b/third_party/blink/renderer/core/css/media_values.h
@@ -187,8 +187,8 @@
   }
   // Return the currently applied position-try-fallback for an anchored element.
   // 0 means no position-try-fallback is applied. Otherwise a 1-based index into
-  // the list of fallbacks of the computed position-try-fallback property.
-  virtual int PositionTryFallback() const { NOTREACHED(); }
+  // the list of fallbacks of the computed position-try-fallbacks property.
+  virtual int AnchoredFallback() const { NOTREACHED(); }
   // Returns the container element used to retrieve base style and parent style
   // when computing the computed value of a style() container query.
   virtual Element* ContainerElement() const { return nullptr; }
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 84e96de..da525475 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
@@ -1604,8 +1604,59 @@
   return ConsumeIdent(stream);
 }
 
+namespace {
+
+// https://drafts.csswg.org/css-values-5/#ident
+CSSFunctionValue* ConsumeIdentFunction(CSSParserTokenStream& stream,
+                                       const CSSParserContext& context) {
+  if (stream.Peek().FunctionId() != CSSValueID::kIdent) {
+    return nullptr;
+  }
+
+  CSSParserTokenStream::RestoringBlockGuard guard(stream);
+  stream.ConsumeWhitespace();
+
+  HeapVector<Member<const CSSValue>, 4> values;
+
+  while (!stream.AtEnd()) {
+    if (CSSValue* custom_ident = ConsumeCustomIdent(stream, context)) {
+      values.push_back(custom_ident);
+      continue;
+    }
+    if (CSSValue* string_value = ConsumeString(stream)) {
+      values.push_back(string_value);
+      continue;
+    }
+    if (CSSValue* integer_value = ConsumeInteger(
+            stream, context,
+            /*minimum_value=*/-std::numeric_limits<double>::max(),
+            /*is_percentage_allowed=*/false)) {
+      values.push_back(integer_value);
+      continue;
+    }
+    return nullptr;
+  }
+
+  if (values.empty()) {
+    return nullptr;
+  }
+
+  CHECK(guard.Release());
+  stream.ConsumeWhitespace();
+
+  return MakeGarbageCollected<CSSFunctionValue>(
+      CSSValueID::kIdent, CSSValueList::kSpaceSeparator, std::move(values));
+}
+
+}  // namespace
+
 CSSCustomIdentValue* ConsumeCustomIdent(CSSParserTokenStream& stream,
                                         const CSSParserContext& context) {
+  if (RuntimeEnabledFeatures::CSSIdentFunctionEnabled()) {
+    if (auto* ident_function = ConsumeIdentFunction(stream, context)) {
+      return MakeGarbageCollected<CSSCustomIdentValue>(*ident_function);
+    }
+  }
   if (stream.Peek().GetType() != kIdentToken ||
       IsCSSWideKeyword(stream.Peek().Id()) ||
       stream.Peek().Id() == CSSValueID::kDefault) {
diff --git a/third_party/blink/renderer/core/css/properties/css_property_test.cc b/third_party/blink/renderer/core/css/properties/css_property_test.cc
index b974079d..876d46c 100644
--- a/third_party/blink/renderer/core/css/properties/css_property_test.cc
+++ b/third_party/blink/renderer/core/css/properties/css_property_test.cc
@@ -477,6 +477,12 @@
             ComputedValue("max-height", "anchor-size(width, 0px)", context));
 }
 
+TEST_F(CSSPropertyTest, IdentFunctionFeatureDisabled) {
+  ScopedCSSIdentFunctionForTest scoped_feature(false);
+
+  EXPECT_FALSE(Parse("view-transition-name", "ident(a)"));
+}
+
 struct DirectionAwarePropertyData {
   CSSPropertyID physical;
   CSSPropertyID logical;
diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
index a1d89ff2..ae547021 100644
--- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc
@@ -2050,8 +2050,9 @@
     const CSSValue& value) {
   state.SetHasTreeScopedReference();
   const CSSCustomIdentValue& custom_ident = To<CSSCustomIdentValue>(value);
-  return MakeGarbageCollected<ScopedCSSName>(custom_ident.Value(),
-                                             custom_ident.GetTreeScope());
+  return MakeGarbageCollected<ScopedCSSName>(
+      custom_ident.ComputeIdent(state.CssToLengthConversionData()),
+      custom_ident.GetTreeScope());
 }
 
 ScopedCSSName* StyleBuilderConverter::ConvertPositionAnchor(
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
index eb5ebadb..db9024be 100644
--- a/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/style_resolver_test.cc
@@ -136,9 +136,10 @@
     StyleRulePositionTry* rule =
         GetStyleEngine().GetPositionTryRule(*scoped_name);
     CHECK(rule);
-    GetStyleEngine().UpdateStyleForOutOfFlow(
-        element, /* try_set */ &rule->Properties(), kNoTryTactics,
-        /* anchor_evaluator */ nullptr);
+    GetStyleEngine().UpdateStyleForOutOfFlow(element, /*try_fallback_index*/ 0,
+                                             /*try_set=*/&rule->Properties(),
+                                             kNoTryTactics,
+                                             /*anchor_evaluator=*/nullptr);
   }
 
   size_t GetCurrentOldStylesCount() {
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc
index fb86acb..f768c9e 100644
--- a/third_party/blink/renderer/core/css/style_engine.cc
+++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -3575,7 +3575,7 @@
   }
   if (!new_layout_style || !old_element_style) {
     // Container may not have a LayoutObject when called from
-    // UpdateStyleForNonEligibleContainer(), but then make sure the style is
+    // UpdateStyleForNonEligibleSizeContainer(), but then make sure the style is
     // null for both cases.
     return new_layout_style == old_element_style;
   }
@@ -3626,7 +3626,7 @@
 #endif  // DCHECK_IS_ON()
 }
 
-void StyleEngine::UpdateStyleForNonEligibleContainer(Element& container) {
+void StyleEngine::UpdateStyleForNonEligibleSizeContainer(Element& container) {
   DCHECK(InRebuildLayoutTree());
   // This method is called from AttachLayoutTree() when we skipped style recalc
   // for descendants of a size query container but figured that the LayoutObject
@@ -3666,7 +3666,7 @@
   RecalcStyleForContainer(container, change);
 }
 
-void StyleEngine::UpdateStyleAndLayoutTreeForContainer(
+void StyleEngine::UpdateStyleAndLayoutTreeForSizeContainer(
     Element& container,
     const LogicalSize& logical_size,
     LogicalAxes contained_axes) {
@@ -3771,10 +3771,12 @@
   GetDocument().UpdateScrollMarkerGroupToScrollableAreasMap();
 }
 
-void StyleEngine::UpdateStyleForOutOfFlow(Element& element,
-                                          const CSSPropertyValueSet* try_set,
-                                          const TryTacticList& tactic_list,
-                                          AnchorEvaluator* anchor_evaluator) {
+void StyleEngine::UpdateStyleForOutOfFlow(
+    Element& element,
+    std::optional<wtf_size_t> try_fallback_index,
+    const CSSPropertyValueSet* try_set,
+    const TryTacticList& tactic_list,
+    AnchorEvaluator* anchor_evaluator) {
   const CSSPropertyValueSet* try_tactics_set =
       try_value_flips_.FlipSet(tactic_list);
 
@@ -3790,6 +3792,13 @@
   style_recalc_context.try_tactics_set = try_tactics_set;
 
   StyleRecalcChange change = StyleRecalcChange().ForceRecalcChildren();
+  if (ContainerQueryEvaluator* evaluator =
+          element.GetContainerQueryEvaluator()) {
+    // TODO(crbug.com/417621241): This method now needs to be similar to
+    // UpdateStyleAndLayoutTreeForContainer() and also run layout tree rebuild
+    // when necessary.
+    change = evaluator->ApplyAnchoredChanges(change, try_fallback_index);
+  }
 
   if (auto* pseudo_element = DynamicTo<PseudoElement>(element)) {
     RecalcPositionTryStyleForPseudoElement(*pseudo_element, change,
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h
index 64edea2..67d7cd6 100644
--- a/third_party/blink/renderer/core/css/style_engine.h
+++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -670,20 +670,21 @@
   void UpdateViewport();
   void UpdateViewportStyle();
   void UpdateStyleAndLayoutTree();
-  // To be called from layout when container queries change for the container.
-  void UpdateStyleAndLayoutTreeForContainer(Element& container,
-                                            const LogicalSize&,
-                                            LogicalAxes contained_axes);
+  // To be called from layout when size queries change for the container.
+  void UpdateStyleAndLayoutTreeForSizeContainer(Element& container,
+                                                const LogicalSize&,
+                                                LogicalAxes contained_axes);
   // To be called from layout-tree building for subtree skipped for style
   // recalcs when we found out the container is eligible for size containment
   // after all.
-  void UpdateStyleForNonEligibleContainer(Element& container);
+  void UpdateStyleForNonEligibleSizeContainer(Element& container);
   // Updates the style of `element`, and descendants if needed.
   // The provided `try_set` represents the declaration block from
   // a @position-try rule. The specified TryTacticList will cause
   // CSSFlipRevertValues to appear in the try-tactics layer (see
   // OutOfFlowData::try_tactics_set_).
   void UpdateStyleForOutOfFlow(Element& element,
+                               std::optional<wtf_size_t> try_fallback_index,
                                const CSSPropertyValueSet* try_set,
                                const TryTacticList&,
                                AnchorEvaluator*);
@@ -712,7 +713,7 @@
   // return nullptr if the interleaving root is a PseudoElement, because such
   // elements can't be recalc roots.
   //
-  // See StyleEngine::UpdateStyleAndLayoutTreeForContainer.
+  // See StyleEngine::UpdateStyleAndLayoutTreeForSizeContainer.
   // See StyleEngine::UpdateStyleForOutOfFlow.
   Element* GetInterleavingRecalcRoot() const {
     if (InContainerQueryStyleRecalc() || InPositionTryStyleRecalc()) {
diff --git a/third_party/blink/renderer/core/css/style_engine_test.cc b/third_party/blink/renderer/core/css/style_engine_test.cc
index 235c9f3a..4bb79f8 100644
--- a/third_party/blink/renderer/core/css/style_engine_test.cc
+++ b/third_party/blink/renderer/core/css/style_engine_test.cc
@@ -4540,7 +4540,8 @@
             frame_document->GetStyleEngine().GetPreferredColorScheme());
 }
 
-TEST_F(StyleEngineContainerQueryTest, UpdateStyleAndLayoutTreeForContainer) {
+TEST_F(StyleEngineContainerQueryTest,
+       UpdateStyleAndLayoutTreeForSizeContainer) {
   GetDocument().body()->setInnerHTML(R"HTML(
     <style>
       .container {
@@ -4587,14 +4588,14 @@
   ASSERT_TRUE(container2);
 
   unsigned start_count = GetStyleEngine().StyleForElementCount();
-  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+  GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
       *container1, LogicalSize(200, 100), kLogicalAxesBoth);
 
   // The first span.affected child and #container2
   EXPECT_EQ(2u, GetStyleEngine().StyleForElementCount() - start_count);
 
   start_count = GetStyleEngine().StyleForElementCount();
-  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+  GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
       *container2, LogicalSize(200, 100), kLogicalAxesBoth);
 
   // Three direct span.affected children, and the two display:none elements.
@@ -4651,7 +4652,7 @@
 
   unsigned start_count = GetStyleEngine().StyleForElementCount();
 
-  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+  GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
       *container, LogicalSize(200, 100), kLogicalAxesBoth);
 
   // Even though none of the inner containers are eligible for containment,
@@ -4687,7 +4688,7 @@
   ASSERT_TRUE(span);
 
   unsigned start_count = GetStyleEngine().StyleForElementCount();
-  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+  GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
       *container, LogicalSize(200, 100), kLogicalAxesBoth);
 
   // The two ::before elements + #span.
@@ -4724,12 +4725,12 @@
   EXPECT_TRUE(old_inner_style);
 
   unsigned start_count = GetStyleEngine().StyleForElementCount();
-  GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+  GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
       *container, LogicalSize(200, 100), kLogicalAxesBoth);
 
   // Input elements mark their InnerEditorElement() style-dirty when they are
-  // recalculated. That means the UpdateStyleAndLayoutTreeForContainer() call
-  // above will involve marking ChildNeedsStyleRecalc all the way up to the
+  // recalculated. That means the UpdateStyleAndLayoutTreeForSizeContainer()
+  // call above will involve marking ChildNeedsStyleRecalc all the way up to the
   // documentElement. Check that we don't leave anything dirty.
   EXPECT_FALSE(GetDocument().NeedsLayoutTreeUpdate());
   EXPECT_FALSE(GetDocument().documentElement()->ChildNeedsStyleRecalc());
diff --git a/third_party/blink/renderer/core/css/style_recalc_change.cc b/third_party/blink/renderer/core/css/style_recalc_change.cc
index 6e26181..540b15cb 100644
--- a/third_party/blink/renderer/core/css/style_recalc_change.cc
+++ b/third_party/blink/renderer/core/css/style_recalc_change.cc
@@ -79,14 +79,18 @@
   if (pseudo_element.NeedsLayoutSubtreeUpdate()) {
     return true;
   }
-  if (!RecalcSizeContainerQueryDependent()) {
+  if (!RecalcContainerQueryDependent()) {
     return false;
   }
   const ComputedStyle& style = pseudo_element.ComputedStyleRef();
   return (RecalcSizeContainerQueryDependent() &&
           style.DependsOnSizeContainerQueries()) ||
          (RecalcStyleContainerQueryDependent() &&
-          style.DependsOnStyleContainerQueries());
+          style.DependsOnStyleContainerQueries()) ||
+         (RecalcScrollStateContainerQueryDependent() &&
+          style.DependsOnScrollStateContainerQueries()) ||
+         (RecalcAnchoredContainerQueryDependent() &&
+          style.DependsOnAnchoredContainerQueries());
 }
 
 String StyleRecalcChange::ToString() const {
@@ -170,7 +174,7 @@
   // kSuppressRecalc should only take effect for the query container itself, not
   // for children. Also make sure the kMarkReattach flag survives one level past
   // the container for ::first-line re-attachments initiated from
-  // UpdateStyleAndLayoutTreeForContainer().
+  // UpdateStyleAndLayoutTreeForSizeContainer().
   if (result & kSuppressRecalc) {
     result &= ~kSuppressRecalc;
   } else {
@@ -182,9 +186,9 @@
 
 bool StyleRecalcChange::IndependentInherit(
     const ComputedStyle& old_style) const {
-  // During UpdateStyleAndLayoutTreeForContainer(), if the old_style is marked
-  // as depending on container queries, we need to do a proper recalc for the
-  // element.
+  // During UpdateStyleAndLayoutTreeForSizeContainer(), if the old_style is
+  // marked as depending on container queries, we need to do a proper recalc for
+  // the element.
   return propagate_ == kIndependentInherit &&
          (!RecalcSizeContainerQueryDependent() ||
           !old_style.DependsOnSizeContainerQueries()) &&
diff --git a/third_party/blink/renderer/core/css/style_recalc_change.h b/third_party/blink/renderer/core/css/style_recalc_change.h
index 99535e6..aff317c 100644
--- a/third_party/blink/renderer/core/css/style_recalc_change.h
+++ b/third_party/blink/renderer/core/css/style_recalc_change.h
@@ -231,7 +231,7 @@
     return flags_ & kRecalcScrollStateContainerFlags;
   }
   bool RecalcAnchoredContainerQueryDependent() const {
-    return flags_ & kRecalcScrollStateContainerFlags;
+    return flags_ & kRecalcAnchoredContainerFlags;
   }
   bool RecalcContainerQueryDependent() const {
     return flags_ & kRecalcContainerFlags;
diff --git a/third_party/blink/renderer/core/dom/character_data.cc b/third_party/blink/renderer/core/dom/character_data.cc
index 0565c95..a626b2e 100644
--- a/third_party/blink/renderer/core/dom/character_data.cc
+++ b/third_party/blink/renderer/core/dom/character_data.cc
@@ -38,7 +38,6 @@
 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
 #include "third_party/blink/renderer/platform/bindings/parkable_string_manager.h"
 #include "third_party/blink/renderer/platform/wtf/text/strcat.h"
-#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
 
@@ -107,14 +106,10 @@
   }
 
   String current_data = this->data();
-  StringBuilder new_str;
-  new_str.ReserveCapacity(data.length() + current_data.length());
-  new_str.Append(StringView(current_data, 0, offset));
-  new_str.Append(data);
-  new_str.Append(StringView(current_data, offset));
+  String new_str = WTF::StrCat({StringView(current_data, 0, offset), data,
+                                StringView(current_data, offset)});
 
-  SetDataAndUpdate(new_str.ReleaseString(),
-                   TextDiffRange::Insert(offset, data.length()),
+  SetDataAndUpdate(new_str, TextDiffRange::Insert(offset, data.length()),
                    kUpdateFromNonParser);
 
   GetDocument().DidInsertText(*this, offset, data.length());
@@ -154,12 +149,9 @@
     return;
 
   String current_data = this->data();
-  StringBuilder new_str;
-  new_str.ReserveCapacity(current_data.length() - real_count);
-  new_str.Append(StringView(current_data, 0, offset));
-  new_str.Append(StringView(current_data, offset + real_count));
-  SetDataAndUpdate(new_str.ReleaseString(),
-                   TextDiffRange::Delete(offset, real_count),
+  String new_str = WTF::StrCat({StringView(current_data, 0, offset),
+                                StringView(current_data, offset + real_count)});
+  SetDataAndUpdate(new_str, TextDiffRange::Delete(offset, real_count),
                    kUpdateFromNonParser);
 
   GetDocument().DidRemoveText(*this, offset, real_count);
@@ -175,13 +167,10 @@
     return;
 
   String current_data = this->data();
-  StringBuilder new_str;
-  new_str.ReserveCapacity(data.length() + current_data.length() - real_count);
-  new_str.Append(StringView(current_data, 0, offset));
-  new_str.Append(data);
-  new_str.Append(StringView(current_data, offset + real_count));
+  String new_str = WTF::StrCat({StringView(current_data, 0, offset), data,
+                                StringView(current_data, offset + real_count)});
 
-  SetDataAndUpdate(new_str.ReleaseString(),
+  SetDataAndUpdate(new_str,
                    TextDiffRange::Replace(offset, real_count, data.length()),
                    kUpdateFromNonParser);
 
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 0ac7367..6c873bb 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -40,10 +40,12 @@
 #include "base/debug/dump_without_crashing.h"
 #include "base/i18n/time_formatting.h"
 #include "base/metrics/histogram_functions.h"
+#include "base/metrics/histogram_macros.h"
 #include "base/not_fatal_until.h"
 #include "base/notreached.h"
 #include "base/task/single_thread_task_runner.h"
 #include "base/time/time.h"
+#include "base/timer/elapsed_timer.h"
 #include "base/trace_event/trace_event.h"
 #include "cc/animation/animation_host.h"
 #include "cc/animation/animation_timeline.h"
@@ -7222,6 +7224,7 @@
   if (url.IsNull())
     return KURL();
 
+  SCOPED_BLINK_UMA_HISTOGRAM_TIMER_HIGHRES("Blink.Document.CompleteURLTime");
   KURL result = url_cache_.Get(base_url_override, url);
   if (result.IsEmpty()) {
     result = Encoding().IsValid() ? KURL(base_url_override, url, Encoding())
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 6679edcb..0a42bf6 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -525,7 +525,7 @@
 bool WillUpdateSizeContainerDuringLayout(const LayoutObject& layout_object) {
   // When a size-container LayoutObject is marked as needs layout,
   // BlockNode::Layout() will resume style recalc with an up-to-date size in
-  // StyleEngine::UpdateStyleAndLayoutTreeForContainer().
+  // StyleEngine::UpdateStyleAndLayoutTreeForSizeContainer().
   return layout_object.NeedsLayout() &&
          layout_object.IsEligibleForSizeContainment();
 }
@@ -3788,7 +3788,7 @@
     // size queries. This recalc must be resumed now, since we're not going to
     // create a LayoutObject for the Element after all.
     if (skipped_container_descendants) {
-      style_engine.UpdateStyleForNonEligibleContainer(*this);
+      style_engine.UpdateStyleForNonEligibleSizeContainer(*this);
       skipped_container_descendants = false;
     }
     // The above recalc may have marked some descendant for reattach, which
@@ -3833,7 +3833,7 @@
 
   if (skipped_container_descendants &&
       (!layout_object || !layout_object->IsEligibleForSizeContainment())) {
-    style_engine.UpdateStyleForNonEligibleContainer(*this);
+    style_engine.UpdateStyleForNonEligibleSizeContainer(*this);
     skipped_container_descendants = false;
   }
 
@@ -4319,7 +4319,7 @@
         !style->ScrollMarkerGroupNone();
     if (style->CanMatchSizeContainerQueries(*this)) {
       // IsSuppressed() means we are at the root of a container subtree called
-      // from UpdateStyleAndLayoutTreeForContainer(). If so, we can not skip
+      // from UpdateStyleAndLayoutTreeForSizeContainer(). If so, we can not skip
       // recalc again. Otherwise, we may skip recalc of the subtree if we can
       // guarantee that we will be able to resume during layout later.
       if (!change.IsSuppressed()) {
@@ -4506,7 +4506,8 @@
     const ComputedStyle& new_style) {
   return evaluator.DependsOnStyle() ||
          new_style.IsContainerForSizeContainerQueries() ||
-         new_style.IsContainerForScrollStateContainerQueries();
+         new_style.IsContainerForScrollStateContainerQueries() ||
+         new_style.IsContainerForAnchoredContainerQueries();
 }
 
 static const StyleRecalcChange ApplyComputedStyleDiff(
@@ -4791,6 +4792,13 @@
             .EnsureContainerQueryData()
             .SetContainerQueryEvaluator(nullptr);
       } else if (old_style) {
+        if (style_recalc_context.anchor_evaluator == nullptr) {
+          // position-try-fallbacks are only applied for UpdateStyleForOutOfFlow
+          // during layout, so we need to reset the anchored(fallback) state to
+          // no fallback for normal style recalc.
+          child_change = evaluator->ApplyAnchoredChanges(
+              child_change, /*try_fallback_index=*/std::nullopt);
+        }
         child_change = evaluator->ApplyScrollStateAndStyleChanges(
             child_change, *old_style, *new_style,
             diff != ComputedStyle::Difference::kEqual);
diff --git a/third_party/blink/renderer/core/editing/commands/insert_list_command.cc b/third_party/blink/renderer/core/editing/commands/insert_list_command.cc
index 05b3fde..caa67df 100644
--- a/third_party/blink/renderer/core/editing/commands/insert_list_command.cc
+++ b/third_party/blink/renderer/core/editing/commands/insert_list_command.cc
@@ -286,8 +286,23 @@
         visible_end_of_selection = CreateVisiblePosition(end_of_selection);
       }
 
-      start_of_current_paragraph =
+      VisiblePosition start_of_next_paragraph =
           StartOfNextParagraph(EndingVisibleSelection().VisibleStart());
+      // Move to the start of the next paragraph. If the start of the next
+      // paragraph goes before the start of the current paragraph, then we
+      // should move to the next position from the start of the next paragraph
+      // in order to avoid infinite loop causing a renderer freeze.
+      // TODO(crbug.com/417631316): Below change fixes the renderer freeze but
+      // it uncovers another bug where the empty span is not unlistified.
+      if (RuntimeEnabledFeatures::
+              FixNextPositionCalculationInInsertListEnabled() &&
+          !start_of_current_paragraph.IsOrphan() &&
+          start_of_next_paragraph.DeepEquivalent() <=
+              start_of_current_paragraph.DeepEquivalent()) {
+        start_of_current_paragraph = NextPositionOf(start_of_next_paragraph);
+      } else {
+        start_of_current_paragraph = start_of_next_paragraph;
+      }
     }
     SetEndingSelection(SelectionForUndoStep::From(
         SelectionInDOMTree::Builder()
diff --git a/third_party/blink/renderer/core/html/html_permission_element.cc b/third_party/blink/renderer/core/html/html_permission_element.cc
index 1773717..9eb2e94 100644
--- a/third_party/blink/renderer/core/html/html_permission_element.cc
+++ b/third_party/blink/renderer/core/html/html_permission_element.cc
@@ -464,9 +464,10 @@
 Node::InsertionNotificationRequest HTMLPermissionElement::InsertedInto(
     ContainerNode& insertion_point) {
   HTMLElement::InsertedInto(insertion_point);
-  if (!permission_descriptors_.empty()) {
+  if (!is_cache_registered_ && !permission_descriptors_.empty()) {
     CachedPermissionStatus::From(GetDocument().domWindow())
         ->RegisterClient(this, permission_descriptors_);
+    is_cache_registered_ = true;
   }
   return kInsertionDone;
 }
@@ -515,9 +516,11 @@
     disable_reason_expire_timer_.Stop();
   }
   intersection_rect_ = std::nullopt;
-  if (LocalDOMWindow* window = GetDocument().domWindow()) {
+  LocalDOMWindow* window = GetDocument().domWindow();
+  if (window && is_cache_registered_) {
     CachedPermissionStatus::From(window)->UnregisterClient(
         this, permission_descriptors_);
+    is_cache_registered_ = false;
   }
   EnsureUnregisterPageEmbeddedPermissionControl();
 }
@@ -1489,7 +1492,7 @@
   if (style->GetDisplayStyle().Display() != EDisplay::kNone &&
       style->GetDisplayStyle().Display() != EDisplay::kInlineBlock) {
     AddConsoleWarning(WTF::StrCat(
-        {"Invalid display style of the permission element", GetType(),
+        {"Invalid display style of the permission element ", GetType(),
          ". Only 'display: inline-block' or 'display: none' is allowed"}));
     base::UmaHistogramEnumeration("Blink.PermissionElement.InvalidStyleReason",
                                   InvalidStyleReason::kInvalidDisplayProperty);
diff --git a/third_party/blink/renderer/core/html/html_permission_element.h b/third_party/blink/renderer/core/html/html_permission_element.h
index d5dfc46..0f64c4f 100644
--- a/third_party/blink/renderer/core/html/html_permission_element.h
+++ b/third_party/blink/renderer/core/html/html_permission_element.h
@@ -527,6 +527,8 @@
 
   bool is_registered_in_browser_process_ = false;
 
+  bool is_cache_registered_ = false;
+
   // Holds reasons for which clicking is currently disabled (if any). Each
   // entry will have an expiration time associated with it, which can be
   // |base::TimeTicks::Max()| if it's indefinite.
diff --git a/third_party/blink/renderer/core/layout/block_node.cc b/third_party/blink/renderer/core/layout/block_node.cc
index 093d020..c729b9d 100644
--- a/third_party/blink/renderer/core/layout/block_node.cc
+++ b/third_party/blink/renderer/core/layout/block_node.cc
@@ -504,7 +504,7 @@
       const LogicalSize available_size = CalculateChildAvailableSize(
           constraint_space, *this, fragment_geometry->border_box_size,
           fragment_geometry->border + scrollbar + fragment_geometry->padding);
-      GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForContainer(
+      GetDocument().GetStyleEngine().UpdateStyleAndLayoutTreeForSizeContainer(
           *element, available_size, ContainedAxes());
 
       // Try the cache again. Container query matching may have affected
diff --git a/third_party/blink/renderer/core/layout/inline/inline_node.cc b/third_party/blink/renderer/core/layout/inline/inline_node.cc
index 937bd5e..f3a1bd0 100644
--- a/third_party/blink/renderer/core/layout/inline/inline_node.cc
+++ b/third_party/blink/renderer/core/layout/inline/inline_node.cc
@@ -941,6 +941,7 @@
 bool InlineNode::SetTextWithOffset(LayoutText* layout_text,
                                    String new_text,
                                    const TextDiffRange& diff) {
+  TRACE_EVENT0("blink", "InlineNode::SetTextWithOffset");
   if (!layout_text->HasValidInlineItems() ||
       !layout_text->IsInLayoutNGInlineFormattingContext())
     return false;
@@ -1666,6 +1667,7 @@
     InlineNodeData* data,
     const String* previous_text,
     const InlineItems* previous_items) const {
+  TRACE_EVENT0("blink", "InlineNode::ShapeTextIncludingFirstLine");
   InlineItem::UpdateIndex(data->items);
   ShapeText(data, previous_text, previous_items);
   ShapeTextForFirstLineIfNeeded(data);
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index 8ad6ba7..21efdfb 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -2756,6 +2756,13 @@
     // UpdateImageObservers to keep CSSImageGeneratorValue::clients_ up-to-date.
     if (!IsText()) {
       UpdateImageObservers(old_style, style_.Get());
+      // Ditto for CSSURIValues.
+      if (HasLayer()) {
+        PaintLayer* layer = To<LayoutBoxModelObject>(*this).Layer();
+        layer->UpdateFilters({}, old_style, *style_);
+        layer->UpdateBackdropFilters(old_style, *style_);
+        layer->UpdateClipPath(old_style, *style_);
+      }
     }
     return;
   }
diff --git a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
index c7ffe88a..15e3715 100644
--- a/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
+++ b/third_party/blink/renderer/core/layout/out_of_flow_layout_part.cc
@@ -336,8 +336,10 @@
     }
 
     CHECK(element_);
+
     element_->GetDocument().GetStyleEngine().UpdateStyleForOutOfFlow(
-        *element_, try_set, try_tactics, &anchor_evaluator_);
+        *element_, try_fallback_index, try_set, try_tactics,
+        &anchor_evaluator_);
     CHECK(element_->GetLayoutObject());
     // Returns LayoutObject ComputedStyle instead of element style for layout
     // purposes. The style may be different, in particular for body -> html
diff --git a/third_party/blink/renderer/core/paint/decoration_line_painter.cc b/third_party/blink/renderer/core/paint/decoration_line_painter.cc
index 40f21da..a14c52b7 100644
--- a/third_party/blink/renderer/core/paint/decoration_line_painter.cc
+++ b/third_party/blink/renderer/core/paint/decoration_line_painter.cc
@@ -40,14 +40,12 @@
 
 bool ShouldUseStrokeForTextLine(StrokeStyle stroke_style) {
   switch (stroke_style) {
-    case kNoStroke:
     case kSolidStroke:
     case kDoubleStroke:
       return false;
     case kDottedStroke:
     case kDashedStroke:
     case kWavyStroke:
-    default:
       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 288652d..0900934 100644
--- a/third_party/blink/renderer/core/style/computed_style.h
+++ b/third_party/blink/renderer/core/style/computed_style.h
@@ -1627,6 +1627,10 @@
     return IsScrollStateContainer() && StyleType() == kPseudoIdNone;
   }
 
+  bool IsContainerForAnchoredContainerQueries() const {
+    return IsAnchoredContainer() && StyleType() == kPseudoIdNone;
+  }
+
   bool DependsOnContainerQueries() const {
     return DependsOnSizeContainerQueries() ||
            DependsOnStyleContainerQueries() ||
@@ -2531,6 +2535,9 @@
   bool IsScrollStateContainer() const {
     return ContainerType() & kContainerTypeScrollState;
   }
+  bool IsAnchoredContainer() const {
+    return ContainerType() & kContainerTypeAnchored;
+  }
 
   static bool IsDisplayBlockContainer(EDisplay display) {
     return display == EDisplay::kBlock || display == EDisplay::kListItem ||
diff --git a/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc b/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc
index b11a742..46c6606 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc
+++ b/third_party/blink/renderer/modules/credentialmanagement/authentication_credentials_container.cc
@@ -1546,7 +1546,8 @@
       CredentialMetrics::From(script_state).RecordWebAuthnConditionalUiCall();
       mediation = Mediation::CONDITIONAL;
     } else if (options->mediation() == "immediate") {
-      if (RuntimeEnabledFeatures::WebAuthenticationImmediateGetEnabled()) {
+      if (RuntimeEnabledFeatures::WebAuthenticationImmediateGetEnabled(
+              context)) {
         mediation = Mediation::IMMEDIATE;
         EmitImmediateMediationUseCounters(context, options);
       } else {
diff --git a/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.cc b/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.cc
index 94eebf11..cc62fa4 100644
--- a/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.cc
+++ b/third_party/blink/renderer/modules/credentialmanagement/public_key_credential.cc
@@ -109,6 +109,18 @@
       [](const std::pair<String, bool>& a, const std::pair<String, bool>& b) {
         return CodeUnitCompare(a.first, b.first) < 0;
       });
+
+  // TODO(crbug.com/393055190): Remove this when the feature is graduated from
+  // origin trials.
+  if (!RuntimeEnabledFeatures::WebAuthenticationImmediateGetEnabled(
+          resolver->GetExecutionContext())) {
+    for (wtf_size_t i = 0; i < results.size(); ++i) {
+      if (results[i].first == "immediateGet") {
+        results.EraseAt(i);
+        break;
+      }
+    }
+  }
   resolver->Resolve(std::move(results));
 }
 
diff --git a/third_party/blink/renderer/platform/fonts/simple_font_data.cc b/third_party/blink/renderer/platform/fonts/simple_font_data.cc
index a7220e155..0225966 100644
--- a/third_party/blink/renderer/platform/fonts/simple_font_data.cc
+++ b/third_party/blink/renderer/platform/fonts/simple_font_data.cc
@@ -47,6 +47,7 @@
 #include "third_party/blink/renderer/platform/fonts/shaping/harfbuzz_face.h"
 #include "third_party/blink/renderer/platform/fonts/shaping/ng_shape_cache.h"
 #include "third_party/blink/renderer/platform/fonts/skia/skia_text_metrics.h"
+#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/character_names.h"
@@ -538,6 +539,16 @@
   // vertical-lr and text-orientation is upright).
   SkScalar size = font_.getSize();
   if (!platform_data_->IsVerticalNonCJKUpright()) {
+    if (RuntimeEnabledFeatures::CSSChUnitSpecCompliantFallbackEnabled()) {
+      return size * 0.5f;
+    }
+
+    // This is a bug that was unfortunately introduced in
+    // crrev.com/c/6333369, and has shipped in m136.
+    // TODO(crbug.com/416145497): Remove this old behaviour
+    // when feature flag
+    // `RuntimeEnabledFeatures::CSSChUnitSpecCompliantFallbackEnabled` is tested
+    // out and is enabled by default.
     return size / 0.5f;
   }
   return size;
diff --git a/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.cc b/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.cc
index 0f1d6cf..9d36bcd 100644
--- a/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.cc
+++ b/third_party/blink/renderer/platform/graphics/gpu/webgpu_mailbox_texture.cc
@@ -108,7 +108,11 @@
   scoped_refptr<CanvasResource> canvas_resource =
       recyclable_canvas_resource->resource_provider()->ProduceCanvasResource(
           FlushReason::kWebGPUTexture);
-  DCHECK(canvas_resource->IsValid());
+
+  if (!canvas_resource) {
+    return nullptr;
+  }
+  CHECK(canvas_resource->IsValid());
 
   scoped_refptr<gpu::ClientSharedImage> shared_image =
       canvas_resource->GetClientSharedImage();
diff --git a/third_party/blink/renderer/platform/graphics/graphics_context.cc b/third_party/blink/renderer/platform/graphics/graphics_context.cc
index c0f64657..897e118 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_context.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_context.cc
@@ -432,10 +432,6 @@
                                const cc::PaintFlags* paint_flags) {
   DCHECK(canvas_);
 
-  StrokeStyle pen_style = styled_stroke.Style();
-  if (pen_style == kNoStroke)
-    return;
-
   gfx::PointF p1 = gfx::PointF(point1);
   gfx::PointF p2 = gfx::PointF(point2);
   bool is_vertical_line = (p1.x() == p2.x());
@@ -450,6 +446,7 @@
       paint_flags ? *paint_flags : ImmutableState()->StrokeFlags();
   styled_stroke.SetupPaint(&flags, {length, width, false});
 
+  const StrokeStyle pen_style = styled_stroke.Style();
   if (pen_style == kDottedStroke) {
     if (StyledStrokeData::StrokeIsDashed(width, pen_style)) {
       // When the length of the line is an odd multiple of the width, things
diff --git a/third_party/blink/renderer/platform/graphics/styled_stroke_data.h b/third_party/blink/renderer/platform/graphics/styled_stroke_data.h
index ae19989..c2aabb7 100644
--- a/third_party/blink/renderer/platform/graphics/styled_stroke_data.h
+++ b/third_party/blink/renderer/platform/graphics/styled_stroke_data.h
@@ -41,7 +41,6 @@
 class StrokeData;
 
 enum StrokeStyle {
-  kNoStroke,
   kSolidStroke,
   kDottedStroke,
   kDashedStroke,
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index fbc9629..7d65b51 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -1194,6 +1194,17 @@
       status: "test",
     },
     {
+      // In the cases where it is impossible or impractical to determine the
+      // measure of the “0” glyph, it must be assumed to be 0.5em wide by 1em
+      // tall. Thus, the ch unit falls back to 0.5em in the general case, and to
+      // 1em when it would be typeset upright (i.e. writing-mode is vertical-rl or
+      // vertical-lr and text-orientation is upright).
+      // https://drafts.csswg.org/css-values-4/#ch
+      // See crbug.com/416145497 for more details.
+      name: "CSSChUnitSpecCompliantFallback",
+      status: "stable",
+    },
+    {
       name: "CSSColorContrast",
       status: "experimental",
     },
@@ -1261,6 +1272,11 @@
       status: "stable",
     },
     {
+      // // https://drafts.csswg.org/css-values-5/#ident
+      name: "CSSIdentFunction",
+      status: "test",
+    },
+    {
       // Support making elements inert through the interactivity property.
       name: "CSSInert",
       status: "stable",
@@ -1347,7 +1363,7 @@
     // progress()
     {
       name: "CSSProgressNotation",
-      status: "experimental",
+      status: "stable",
     },
     {
       // For ::column pseudo element for fragment styling.
@@ -1505,7 +1521,7 @@
     },
     {
       name: "CSSSignRelatedFunctions",
-      status: "experimental",
+      status: "stable",
     },
     {
       // Explainer: https://drafts.csswg.org/css-values/#round-func
@@ -2216,6 +2232,11 @@
       status: "stable",
     },
     {
+      // crbug.com/411739501
+      name: "FixNextPositionCalculationInInsertList",
+      status: "stable",
+    },
+    {
       name: "Fledge",
       status: "stable",
       base_feature: "none",
@@ -5271,7 +5292,7 @@
     // Prototyping https://github.com/w3c/webauthn/issues/2228
     {
       name: "WebAuthenticationImmediateGet",
-      status: "experimental",
+      origin_trial_feature_name: "WebAuthenticationImmediateGet",
       base_feature: "none",
       public: true,
     },
@@ -5679,6 +5700,6 @@
       // If enabled, the `getDisplayMedia()` family of APIs will ask for NV12
       // frames, which should trigger a zero-copy path in the tab capture code.
       name: "ZeroCopyTabCapture",
-    },
+    }
   ],
 }
diff --git a/third_party/blink/renderer/platform/wtf/stack_util.cc b/third_party/blink/renderer/platform/wtf/stack_util.cc
index 0ae79af4..946906e 100644
--- a/third_party/blink/renderer/platform/wtf/stack_util.cc
+++ b/third_party/blink/renderer/platform/wtf/stack_util.cc
@@ -23,6 +23,10 @@
 extern "C" void* __libc_stack_end;  // NOLINT
 #endif
 
+#if defined(ADDRESS_SANITIZER)
+#include <sanitizer/asan_interface.h>
+#endif
+
 namespace WTF {
 
 size_t GetUnderestimatedStackSize() {
@@ -101,7 +105,12 @@
 #endif
 }
 
-void* GetStackStart() {
+namespace {
+
+// A pointer to current thread's stack beginning.
+thread_local void* thread_stack_start = nullptr;
+
+void* GetStackStartImpl() {
 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \
     BUILDFLAG(IS_FREEBSD) || BUILDFLAG(IS_FUCHSIA)
   pthread_attr_t attr;
@@ -157,6 +166,35 @@
 #endif
 }
 
+}  // namespace
+
+void* GetStackStart() {
+  if (!thread_stack_start) {
+    thread_stack_start = GetStackStartImpl();
+  }
+  return thread_stack_start;
+}
+
+bool IsOnStack(void* address) {
+#if defined(ADDRESS_SANITIZER)
+  // If the address is part of a fake frame, then it is definitely on the stack.
+  if (__asan_addr_is_in_fake_stack(__asan_get_current_fake_stack(), address,
+                                   nullptr, nullptr)) {
+    return true;
+  }
+  // Fall through as there is still a regular stack present even when running
+  // with ASAN fake stacks.
+#endif  // defined(ADDRESS_SANITIZER)
+#if __has_feature(safe_stack)
+  if (__builtin___get_unsafe_stack_ptr() <= address &&
+      address <= __builtin___get_unsafe_stack_top()) {
+    return true;
+  }
+#endif  // __has_feature(safe_stack)
+  return (GetCurrentStackPosition() <= reinterpret_cast<uintptr_t>(address)) &&
+         (address <= GetStackStart());
+}
+
 uintptr_t GetCurrentStackPosition() {
 #if defined(COMPILER_MSVC)
   return reinterpret_cast<uintptr_t>(_AddressOfReturnAddress());
diff --git a/third_party/blink/renderer/platform/wtf/stack_util.h b/third_party/blink/renderer/platform/wtf/stack_util.h
index 17dde31..82995f0 100644
--- a/third_party/blink/renderer/platform/wtf/stack_util.h
+++ b/third_party/blink/renderer/platform/wtf/stack_util.h
@@ -15,6 +15,7 @@
 
 WTF_EXPORT size_t GetUnderestimatedStackSize();
 WTF_EXPORT void* GetStackStart();
+WTF_EXPORT bool IsOnStack(void* address);
 
 // Returns the current stack position such that it works correctly with ASAN and
 // SafeStack. Must be marked noinline because it relies on compiler intrinsics
diff --git a/third_party/blink/renderer/platform/wtf/text/strcat.cc b/third_party/blink/renderer/platform/wtf/text/strcat.cc
index 4eaaec6..bfc1006b 100644
--- a/third_party/blink/renderer/platform/wtf/text/strcat.cc
+++ b/third_party/blink/renderer/platform/wtf/text/strcat.cc
@@ -4,8 +4,6 @@
 
 #include "third_party/blink/renderer/platform/wtf/text/strcat.h"
 
-#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
-
 namespace WTF {
 
 String StrCat(base::span<const StringView> pieces) {
@@ -16,7 +14,6 @@
     is_8bit = is_8bit && view.Is8Bit();
   }
 
-  StringBuilder builder;
   if (is_8bit) {
     base::span<LChar> buffer;
     auto impl = StringImpl::CreateUninitialized(size, buffer);
diff --git a/third_party/blink/renderer/platform/wtf/vector.h b/third_party/blink/renderer/platform/wtf/vector.h
index 8255a2340..a45d934 100644
--- a/third_party/blink/renderer/platform/wtf/vector.h
+++ b/third_party/blink/renderer/platform/wtf/vector.h
@@ -51,6 +51,7 @@
 #include "third_party/blink/renderer/platform/wtf/container_annotations.h"
 #include "third_party/blink/renderer/platform/wtf/forward.h"  // For default Vector template parameters.
 #include "third_party/blink/renderer/platform/wtf/hash_table_deleted_value_type.h"
+#include "third_party/blink/renderer/platform/wtf/stack_util.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/type_traits.h"
 #include "third_party/blink/renderer/platform/wtf/vector_traits.h"
@@ -672,10 +673,16 @@
  public:
   using OffsetRange = typename Base::OffsetRange;
 
-  VectorBuffer() : Base(InlineBuffer(), InlineCapacity) { InitInlinedBuffer(); }
+  VectorBuffer() : Base(InlineBuffer(), InlineCapacity) {
+#if DCHECK_IS_ON()
+    VerifyInlinedBuffer();
+#endif
+  }
 
   explicit VectorBuffer(HashTableDeletedValueType value) : Base(value) {
-    InitInlinedBuffer();
+#if DCHECK_IS_ON()
+    VerifyInlinedBuffer();
+#endif
   }
   bool IsHashTableDeletedValue() const {
     return Base::IsHashTableDeletedValue();
@@ -683,7 +690,9 @@
 
   explicit VectorBuffer(wtf_size_t capacity)
       : Base(InlineBuffer(), InlineCapacity) {
-    InitInlinedBuffer();
+#if DCHECK_IS_ON()
+    VerifyInlinedBuffer();
+#endif
     if (capacity > InlineCapacity) {
       Base::AllocateBuffer(capacity, VectorOperationOrigin::kConstruction);
     }
@@ -975,9 +984,14 @@
     return unsafe_reinterpret_cast_ptr<const T*>(inline_buffer_);
   }
 
-  void InitInlinedBuffer() {
-    if (Allocator::kIsGarbageCollected) {
-      memset(&inline_buffer_, 0, kInlineBufferSize);
+  void VerifyInlinedBuffer() {
+    // On heap allocations are always zero-initialized. Stack is anyway scanned
+    // conservatively, stack-to-stack pointers are filtered out, so no need to
+    // clear out the inlined buffer.
+    if constexpr (Allocator::kIsGarbageCollected) {
+      const bool is_zeroed =
+          std::ranges::all_of(inline_buffer_, [](char c) { return c == 0; });
+      DCHECK(is_zeroed || WTF::IsOnStack(inline_buffer_));
     }
   }
 
@@ -2504,7 +2518,17 @@
     }
 
     // Inline buffer requires tracing immediately.
-    internal::TraceInlinedBuffer<Allocator>(visitor, buffer, InlineCapacity);
+    if (visitor->IsConcurrent()) {
+      // For the concurrent marker we're guaranteed to have an on-heap object
+      // (which means that the unused slots are zeroed), since we don't follow
+      // heap->stack references.
+      internal::TraceInlinedBuffer<Allocator>(visitor, buffer, InlineCapacity);
+    } else {
+      // Trace until size, because inlined storages for on-stack collections are
+      // not zeroed out. This path covers both main-thread marking and the write
+      // barrier.
+      internal::TraceInlinedBuffer<Allocator>(visitor, buffer, size());
+    }
   }
 }
 
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index 3b2efee..efdf23c 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -1462,3 +1462,6 @@
 
 # Gardener 2025-05-02
 crbug.com/387598611 fast/overflow/scrollbar-drag-origin.html [ Slow ]
+
+# Gardener 2025-05-19
+crbug.com/418572857 external/wpt/background-fetch/fetch.https.window.html [ Slow ]
\ No newline at end of file
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index e6a0309c..a936e26d 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -3782,7 +3782,6 @@
 crbug.com/626703 external/wpt/css/css-text/white-space/text-wrap-balance-float-004.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-text/white-space/text-wrap-balance-float-005.html [ Failure ]
 crbug.com/626703 [ Win11 ] external/wpt/fetch/orb/tentative/known-mime-type.sub.any.html [ Failure Timeout ]
-crbug.com/626703 [ Win ] external/wpt/background-fetch/fetch.https.window.html [ Timeout ]
 crbug.com/626703 external/wpt/css/css-shadow-parts/animation-part.html [ Failure ]
 crbug.com/626703 [ Win10.20h2 ] external/wpt/css/css-fonts/font-display/font-display-feature-policy-02.tentative.html [ Failure ]
 crbug.com/626703 [ Win ] external/wpt/fetch/api/redirect/redirect-keepalive.any.html [ Skip Timeout ]
@@ -3870,9 +3869,6 @@
 crbug.com/626703 [ Mac13 ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/default-enabled-features-attribute-disallow.https.html [ Timeout ]
 crbug.com/626703 [ Win ] virtual/fenced-frame-mparch/external/wpt/fenced-frame/default-enabled-features-attribute-disallow.https.html [ Timeout ]
 crbug.com/626703 [ Mac13 ] virtual/fsa-incognito/external/wpt/fs/FileSystemBaseHandle-IndexedDB.https.any.html [ Timeout ]
-crbug.com/626703 [ Mac13 ] virtual/keepalive-in-browser-migration/external/wpt/background-fetch/fetch.https.window.html [ Timeout ]
-crbug.com/626703 [ Mac14 ] virtual/keepalive-in-browser-migration/external/wpt/background-fetch/fetch.https.window.html [ Timeout ]
-crbug.com/626703 [ Win10.20h2 ] virtual/keepalive-in-browser-migration/external/wpt/background-fetch/fetch.https.window.html [ Timeout ]
 crbug.com/626703 [ Mac13 Release ] virtual/keepalive-in-browser-migration/external/wpt/background-fetch/match.https.window.html [ Timeout ]
 crbug.com/626703 [ Win10.20h2 ] external/wpt/web-locks/bfcache/sharedworker-multiple.tentative.https.html [ Skip Timeout ]
 crbug.com/626703 external/wpt/css/css-color/border-color-currentcolor.html [ Failure ]
@@ -7835,11 +7831,6 @@
 # Gardener 2023-10-19
 crbug.com/1494133 media/controls/video-overlay-cast-light-rendering.html [ Failure Pass ]
 
-# Gardener 2023-10-20
-crbug.com/1494242 [ Mac11 Release ] external/wpt/background-fetch/fetch.https.window.html [ Failure Timeout ]
-crbug.com/1494242 [ Mac12 Release ] external/wpt/background-fetch/fetch.https.window.html [ Failure Timeout ]
-crbug.com/1494242 [ Mac13 Release ] external/wpt/background-fetch/fetch.https.window.html [ Failure Timeout ]
-
 # Gardener 2023-11-10
 crbug.com/1486131 [ Debug Linux ] external/wpt/html/browsers/history/the-history-interface/001.html [ Failure Pass ]
 
diff --git a/third_party/blink/web_tests/editing/execCommand/insert_list/unlistify_with_empty_span_no_hang.html b/third_party/blink/web_tests/editing/execCommand/insert_list/unlistify_with_empty_span_no_hang.html
new file mode 100644
index 0000000..dd90681
--- /dev/null
+++ b/third_party/blink/web_tests/editing/execCommand/insert_list/unlistify_with_empty_span_no_hang.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../assert_selection.js"></script>
+<html>
+  <head>
+    <title>
+      Unlistifying with empty span should not freeze renderer in any writing
+      mode
+    </title>
+    <style>
+      section {
+        border: 1px solid #000;
+        margin: 20px;
+        padding: 10px;
+      }
+      h2 {
+        text-align: center;
+      }
+    </style>
+  </head>
+  <body>
+    <section>
+      <h2>LTR Horizontal</h2>
+      <div id="compose1" contenteditable="true">
+        <ul>
+          <li>a<br /><span></span></li>
+          <li>b</li>
+        </ul>
+      </div>
+    </section>
+    <section>
+      <h2>RTL Horizontal</h2>
+      <div id="compose2" contenteditable="true" dir="rtl">
+        <ul>
+          <li>a<br /><span></span></li>
+          <li>b</li>
+        </ul>
+      </div>
+    </section>
+    <section>
+      <h2>LTR Vertical (vertical-rl)</h2>
+      <div
+        id="compose3"
+        contenteditable="true"
+        style="writing-mode: vertical-rl"
+      >
+        <ul>
+          <li>a<br /><span></span></li>
+          <li>b</li>
+        </ul>
+      </div>
+    </section>
+    <section>
+      <h2>RTL Vertical (vertical-rl)</h2>
+      <div
+        id="compose4"
+        contenteditable="true"
+        dir="rtl"
+        style="writing-mode: vertical-rl"
+      >
+        <ul>
+          <li>a<br /><span></span></li>
+          <li>b</li>
+        </ul>
+      </div>
+    </section>
+    <script>
+      function applyCommand(id) {
+        const ul = document.querySelector(`#${id} ul`);
+        const range = document.createRange();
+        range.selectNode(ul);
+        const selection = getSelection();
+        selection.removeAllRanges();
+        selection.addRange(range);
+        document.execCommand("insertUnorderedList", false);
+      }
+      test(() => {
+        applyCommand("compose1");
+      }, "Unlistifying with empty span should not freeze renderer in LTR Horizontal mode");
+      test(() => {
+        applyCommand("compose2");
+      }, "Unlistifying with empty span should not freeze renderer in RTL Horizontal mode");
+      test(() => {
+        applyCommand("compose3");
+      }, "Unlistifying with empty span should not freeze renderer in LTR Vertical mode");
+      test(() => {
+        applyCommand("compose4");
+      }, "Unlistifying with empty span should not freeze renderer in RTL Vertical mode");
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
index 2e23cc4..fd3ac8c0 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -250644,6 +250644,19 @@
        {}
       ]
      ],
+     "ch-unit-019.html": [
+      "1b97165d72df7743f935405025089f41db0f0b1f",
+      [
+       null,
+       [
+        [
+         "/css/css-values/reference/ch-unit-019-ref.html",
+         "=="
+        ]
+       ],
+       {}
+      ]
+     ],
      "ex-calc-expression-001.html": [
       "4eab829697f87606a64d60f360a04639e61ccabb",
       [
@@ -325718,7 +325731,7 @@
      []
     ],
     "OWNERS": [
-     "24eebbdf6f5547a1c45be04bb52d5365f8ff325d",
+     "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
      []
     ],
     "README.md": [
@@ -373587,6 +373600,10 @@
        "74e304be728daa5ae3d5425b5b125b46c7ad1800",
        []
       ],
+      "ch-unit-019-ref.html": [
+       "f73a3c6e1d8c350f135783a2cc3983a9a35f9ea6",
+       []
+      ],
       "ex-unit-001-ref.html": [
        "33b241f58c0501172006de6222bb3a77a0e680b3",
        []
@@ -418769,7 +418786,7 @@
       []
      ],
      "link-header-referrer-policy.py": [
-      "984518d364d8c53388c3379f4aefb64fba3c2072",
+      "d3c659b113576d53e8a21ff70d496b14e8de6c6d",
       []
      ],
      "module1.js": [
@@ -418828,6 +418845,10 @@
       "f4bc87940ec9b55737aaec28c3a21cafe8b420d1",
       []
      ],
+     "stash-referrer.py": [
+      "db498f35507cd339ba8124d024c5f7dc4e9661d2",
+      []
+     ],
      "stash-take.py": [
       "9977197cae5591f62240a2d97a0c66be1f8bbeeb",
       []
@@ -529968,6 +529989,20 @@
        {}
       ]
      ],
+     "ident-function-computed.html": [
+      "0a4068702e48f4faea3e95e7be783907fa9e78ed",
+      [
+       null,
+       {}
+      ]
+     ],
+     "ident-function-parsing.html": [
+      "615051c78d5257a0e74c5aa97ca57a64f18afec7",
+      [
+       null,
+       {}
+      ]
+     ],
      "if-conditionals.html": [
       "caead7864d550f8732eff919c1b555c8f759508d",
       [
@@ -687326,8 +687361,17 @@
       {}
      ]
     ],
+    "preload-referrer-policy-subresource-header.tentative.html": [
+     "0e1c9be8be3283f091113ba3baae47bbf3006ff7",
+     [
+      null,
+      {
+       "timeout": "long"
+      }
+     ]
+    ],
     "preload-referrer-policy.html": [
-     "0a4fbb0b4a1d4995ae55ca2d76a9655cb14405a6",
+     "a33d1b664e3bf3b70e9ab3fcf8cc2147f8a18133",
      [
       null,
       {
@@ -701827,7 +701871,7 @@
      ]
     ],
     "sanitizer-parseHTML.tentative.html": [
-     "c4a31e2eb0365f67479968f4ce08117d04384889",
+     "290c138a3589e08e3481e3a2fe4cc9f6925499c7",
      [
       null,
       {}
@@ -774101,7 +774145,7 @@
       ]
      ],
      "pad.https.any.js": [
-      "54289aa9b46d4601b1ddedefe7acf2bbf99a21b1",
+      "02c9ec24508e07a66df5d7eae8e5e02d52f5575b",
       [
        "webnn/conformance_tests/pad.https.any.html?cpu",
        {
@@ -774980,7 +775024,7 @@
       ]
      ],
      "qdq_subgraph.https.any.js": [
-      "70e8cab0d95ee7d2870a462621de80be95cf2e79",
+      "aeebc67f856ce8eac450b9e0d81aef7616231861",
       [
        "webnn/conformance_tests/qdq_subgraph.https.any.html?cpu",
        {
@@ -793091,7 +793135,7 @@
       ]
      ],
      "pad.https.any.js": [
-      "75486a50b328d353e5fbe6c1308d6698b6dcfb01",
+      "fb285ed8277fcf7ce04c6927b7173cc3b47b2544",
       [
        "webnn/validation_tests/pad.https.any.html?cpu",
        {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-019.html b/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-019.html
new file mode 100644
index 0000000..1b97165
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-019.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Values and Units Test: support for the ch unit</title>
+<link rel="author" title="Jason Leo" href="mailto:cgqaq@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-values-4/#ch">
+<link rel="match" href="reference/ch-unit-019-ref.html">
+<meta name="assert" content="The ch unit should fallback to 0.5em or 1em respectively when it's impossible to derive the width of the “0” (ZERO, U+0030) glyph in the font used to render it">
+<style>
+@font-face {
+  font-family: "icon-font";
+  src: url("data:font/woff2;base64,d09GMgABAAAAAAngAAoAAAAAGagAAAmTAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmAAgkIKpCiZEAtSAAE2AiQDgR4EIAUGB4c4GwcTUZRuUo/sx2HsZqnF1Ss+lo/VoBPFm3bORkgye4CbdpcESkOCaKDFK5aqUjHbYGKWTMxZ5/zE5c2c+/lT7mXjVgWS1L7JWnGZ6oNz2lw2vxvYI0lMaw+OWM7YxLY24f+qtPVE8Ofap2RSJVCdGkXj62T2ZScvm3xIc0S0W/wErsDyXG98gVmYGlOhK2ytrNpwFbgue9FMHHzf3P5liGoq87CmwInm9vUFgMLPyQUgASC2Ek8AYPXq1vgNmGABAAHBJIwPQBXcsbC9uRZufIgfRuLjwFmAp4bq22lQgIXSfuAmZzIUygByJbbKKLCXU0C1fRgZCIypWGUH1H8goBTGMgrSGlILciX7iqRJGTJJ0AcTSbRMkCT44okXB0Gxxi6/AUVpKLuM4xSci+MSbCouXqXn9CpapX3tYk0K4gyccjHmDrSeaeAYDadL0iU4Na5WpzHoKItcwlJt+B50976oQxzIUR4NcpwbE+OnO4FBr3y5cTPithh/BHP1W4n6jXI5bB1PXcwr4jZN42GQWRTNh0Cfx5jDRL0n46O9URyITrAYcb0CiHBTT6Pjta9LHeaCstGgGNYpztFEDL97l1ev6N+8H1y//O1fXb957/Jl9+KJaE6PjBhWjFVl4WZ5uKh9JSk1/CPKZEB9XBeWLlpnouMSP7Q+c/PzQtPWyenUqkct+eU0ZUmVbco4dw6m6XfMATCzM83IPExAIGSMqYs6S+VxakkX6Kgf9bVYrEAhsQRbX4ia2zURhnUxLFaUKBGmAAhduVdoHgDAgQv3bh5Zsog+fM4Go2++eDQlDIMFabZsXV0JgR3l7rB3+r5YAhoH/nURWC4OnXlCEuIVCm0xjTjRJP1WHPdsyjBYqRczJ7N/r173Lp9+dqnLFT5e11f+7r+esZP/D65v3l+o0afAn9uPx9+7z6f7jeORMuOsL7KucZ2Ly5fHv+u3/av/BGrAaNpp1w0K0Gsmpk1nk31XKpNVkpVHTSlOWAkLoQBkhy5skK9qOzDUGufIteuU9aF1QeH0rUYn19EgcWbqW/JoOWCik7ZMkg7DRlNKKvo9FjbpeqPxBHC/evaI/sXzuA5kWDt/8t7pQ7fyialWD4xaP/IVkLjaOM6rtcO6+f+lrHGsRQh/Ol7PMCOmQ4I6KY8xjHWiwMhPeKFC60hlL/VHRfXMI390RqkyyAY9YsdlpFFGlPI+o1XwjF4dCvMqc4UQ/VJoTVwOeMRMyJskxEnpt/PzMCiHTWaB94ooaiDJlPHLUl1s04XlplQl7n/k+wVWD1S2Va7qA2cIFPogyAVCoLwHAuoG0JCOo7Rx1wlrz1p3iDBWkYjEC5ylAnQRJ+3GFPb6Qt7M1MnGW8bRfLm6C1jSlEZaXMq+0d2wM0t/1QEStiJbO8yGXfHGwETaXGKsXaQwsCws80L7+yV1M/UW2bKiqcHXkCaJBKCeRkJdDrwPX07I6mp5G7sPg5ft7z1840l87tE/mVfwbqNwd0plnK2xLNvl9+VfK7uTe0UPE2Tp9RsaMWDq39vpV/m/czwZ8gSE6z61mva4PZ88KXYXm51PnObQ/h7wFNjsiiVLFB5arfaJ12Ff292NdBijvN+o1zfyCvBl2xaW3XL9+lKIpRt16PAP+nBIEvsb9abjVC++KAgsG3jxRZb9fswYFm5AabDp9M3i4punTcG7o9W2qJQeUtE6ZOoQermXS4sEkiQiiVqXIBzfx7+tpWQovKhi2Rdf/P57a77f747Y7OwDopZMmTJ0a0kVDEi1CCzJoX3olMD3bXqGWIB3ceq0BAnvF3hfxpLMknWETpr9Ihs88eI5VkHE/J8+Jmk1+LgAUfbZESf8LAc1CA1vvaXN9qmBJj9OSj531szfFCRVnLuYpNGSBAEEqkuk2VIdsb40EwKuYZU0tZYnqu6QVYZomF8y5+Ya40t6LQ+dsHd0hrUxKBNi3DyhceqVO8joylOeE55TlbS84o2xm2o4NZ6owxZ3T03GRfV6+GB340PKYe5aEwflUCYbeevU2XNxsd3LKGxsbW1EkmaVmUxz15bBjUc6qxoxTFJm6yXdBw9tq8qhUlPUgDBRptltWre2HKzVCzYRO160/dCoT0wsEBkJ7TsNouu6fAiv3Y0T9B+Lwh1aSzDueyZhdCRR1dAChzG5Y5TFThmqc4uSUod6CFSVz57jB38+Z3Y0/Lh27Rgjz8iQM8euZe2u1D8z0m7XaiIiNNrZNxJ+LWSVbg78ZIxtjtV2+SH/Qr8vCXbJZac7yUeTOtfIEUtY+6p+fqhId/oFwfu+JXwFW/O8YsbuZ9teGV8w/ULey8UGQ8rHeRemF4x/Zduz1eWiN2/r+RCWDTmPKShfvW3axbyPU3YWv5x3cVrJ7owrXS0z9/TcvWcy3bvrfM21hXD9OvnxTz99TPZngxOrNI4q7OmVQ7M9Ebt4GruAnbYY+he4GOalF53OF1+afV09DR8SKtxz2+bfDLsfjCV2dApWb+K7T0kJ9Y1aMYI4ckQjZVcy5NN3E9cUTQ1JOX48JVGifvQwydwnDo4Dj41rho9l4waLfeaUv/9OQbeHTC1aY6+XWclKNd14YoRC/Q0lgXleq9C5A5YcKr9qQXhczZUVp12cn96xbKs97gtsfVbg3OtauiDAJHGDU173Jokg3HX1+oREdyK2DVapBrfhY21Cpy2+eXPxNOV9XN/Txq6hDRy7cHbNvuQ/ri8oc3+TIQFwzidPuqtWtwxcEG7CDwAyIDRGkXGBOOiJwb4xJkAY90fiFHDeaTDLnjY3Ddx7Vln7sm7s8eFuKIIHxTSvEcvFagC6S6sUjF4u3jDo9oDHtzJ8l3gZozQoayDc5mfsGkbBMKBgvEkKBISQ/AkHrd8NCSSRQglKMQhlGIw0ypFBFhWoRBWqUYNa1KEeDWhEDk1oxhAMRQta0YZ2dKATXRiG4RiBkRgl+T2gWw0cNnjKhLFj+MnjmC182Lt1vwMpeSnBzYsJ/oYzGCRgOGta3gFYs0OWkYUWWCeg6RKOsrQgejCkn/4UUTL0iyEOjhkKNXhahnvfDuPBrVdQr0U/cdy/fJd8GIUpBDBeQKRQh2sEL6owlI/2BBEZGichvrpj7Hu5f6k+Ycj04WOG8lIYawQ0n7XbFQPkIcHLTyn2YwTBSIzsplTs2S5DGCiFAoJUS149E/nJ4wqHJwg4ALZgtTgiBG4mn3Ch4mE7P2ksH1RidwcLPY86GRZTvhS8NOXsBLfdsLMSAnbt/MjB08OxKc4zCP6mAchcbBAkZIQDAAA=") format("woff2");
+}
+
+div {
+  height: 10px;
+  background-color: blue;
+  margin-top: 10px;
+}
+
+div::before {
+  content: "\e00b";
+  display: none;
+}
+
+.test {
+  /* 50px + 10 * (0.5em)*/
+  /* 50px + 10 * (0.5 * 10px) = 100px*/
+  width: calc(50px + 10ch);
+}
+
+.test-upright {
+  writing-mode: vertical-rl;
+  text-orientation: upright;
+  /* 50px + 5 * (1em)*/
+  /* 50px + 5 * (1 * 10px) = 100px*/
+  width: calc(50px + 5ch);
+}
+
+.ref {
+  width: 100px;
+}
+</style>
+<p>The test passes if there are two blue rectangles of equal length.</p>
+<!-- In the cases where it is impossible or impractical to determine
+     the measure of the “0” glyph, it must be assumed to be 0.5em
+     wide by 1em tall. Thus, the ch unit falls back to 0.5em in the general case-->
+<div class="test" style="font: 10px icon-font"></div>
+<!-- and to 1em when it would be typeset upright (i.e. writing-mode
+     is vertical-rl or vertical-lr and text-orientation is upright).-->
+<div class="test-upright" style="font: 10px icon-font"></div>
+<div class="ref"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/ident-function-computed.html b/third_party/blink/web_tests/external/wpt/css/css-values/ident-function-computed.html
new file mode 100644
index 0000000..0a406870
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/ident-function-computed.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<title>CSS Values: The ident() Function (computed values)</title>
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#ident">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+<div id="target"></div>
+<script>
+  // Any property that accepts <custom-ident> will do.
+  let prop = 'view-transition-name';
+
+  test_computed_value(prop, 'ident(myident)', 'myident');
+  test_computed_value(prop, 'ident("myident")', 'myident');
+  test_computed_value(prop, 'ident("myident" 3)', 'myident3');
+  test_computed_value(prop, 'ident(3 "myident")', '\\33 myident');
+  test_computed_value(prop, 'ident("my" "ident")', 'myident');
+  test_computed_value(prop, 'ident(my "ident")', 'myident');
+  test_computed_value(prop, 'ident("my" ident)', 'myident');
+  test_computed_value(prop, 'ident(my ident)', 'myident');
+  test_computed_value(prop, 'ident(-- myident)', '--myident');
+  test_computed_value(prop, 'ident(my 3 3 3 3 ident)', 'my3333ident');
+
+  // Check for support in specific properties (WIP):
+  let actual_ident = 'ident("myident" 42)';
+  let expected_ident = 'myident42';
+
+  // https://drafts.csswg.org/css-view-transitions/#view-transition-name-prop
+  test_computed_value('view-transition-name', actual_ident, expected_ident);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/ident-function-parsing.html b/third_party/blink/web_tests/external/wpt/css/css-values/ident-function-parsing.html
new file mode 100644
index 0000000..615051c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/ident-function-parsing.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>CSS Values: The ident() Function (parsing)</title>
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#ident">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+<script>
+  // Any property that accepts <custom-ident> will do.
+  let prop = 'view-transition-name';
+
+  test_invalid_value(prop, 'ident()');
+  test_invalid_value(prop, 'ident( )');
+  test_invalid_value(prop, 'ident(rgb(1, 2, 3))');
+  test_invalid_value(prop, 'ident(5px)');
+
+  test_valid_value(prop, 'ident(myident)');
+  test_valid_value(prop, 'ident( myident)', 'ident(myident)');
+  test_valid_value(prop, 'ident(myident )', 'ident(myident)');
+  test_valid_value(prop, 'ident("myident")');
+  test_valid_value(prop, 'ident("myident" 3)');
+  test_valid_value(prop, 'ident(3 "myident")');
+  test_valid_value(prop, 'ident("my" "ident")');
+  test_valid_value(prop, 'ident(my "ident")');
+  test_valid_value(prop, 'ident("my" ident)');
+  test_valid_value(prop, 'ident(my ident)');
+  test_valid_value(prop, 'ident(-- myident)');
+  test_valid_value(prop, 'ident(my 3 3 3 3 ident)')
+
+  // Check for support in specific properties (WIP):
+  let ident = 'ident("myident" 42)';
+
+  // https://drafts.csswg.org/css-view-transitions/#view-transition-name-prop
+  test_valid_value('view-transition-name', ident);
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/reference/ch-unit-019-ref.html b/third_party/blink/web_tests/external/wpt/css/css-values/reference/ch-unit-019-ref.html
new file mode 100644
index 0000000..f73a3c6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/reference/ch-unit-019-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Values and Units Test Reference File</title>
+<link rel="author" title="Jason Leo" href="mailto:cgqaq@chromium.org">
+<style>
+div {
+  height: 10px;
+  background-color: blue;
+  margin-top: 10px;
+  width: 100px;
+}
+</style>
+<p>The test passes if there are two blue rectangles of equal length.</p>
+<div></div>
+<div></div>
+<div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/filter-effects/crashtests/external-reference-in-interleaved-oof-crash.html b/third_party/blink/web_tests/external/wpt/css/filter-effects/crashtests/external-reference-in-interleaved-oof-crash.html
new file mode 100644
index 0000000..a1aad3a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/filter-effects/crashtests/external-reference-in-interleaved-oof-crash.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Interleaved OOF layout with url() valued 'filter' property</title>
+<style>
+  #target {
+    width: 100px;
+    height: 100px;
+    background-color: blue;
+    --f: url("data:image/svg+xml,");
+    position: absolute;
+    position-area: block-end span-inline-end;
+    position-try-fallbacks: block-start span-inline-end;
+  }
+</style>
+<div id="target" style="filter: var(--f)"></div>
+<script>
+  document.body.offsetTop;
+  target.style.setProperty('filter', 'var(--f)');
+  document.body.offsetTop;
+  target.style.removeProperty('filter');
+  document.body.offsetTop;
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/preload-referrer-policy-subresource-header.tentative.html b/third_party/blink/web_tests/external/wpt/preload/preload-referrer-policy-subresource-header.tentative.html
new file mode 100644
index 0000000..0e1c9be
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/preload-referrer-policy-subresource-header.tentative.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>The referrerpolicy attribute on Link header should be ignored for subresources</title>
+<meta name="timeout" content="long">
+<script src="resources/dummy.js?link-header-preload2"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/preload/resources/preload_helper.js"></script>
+<body>
+    <p>The referrerpolicy attribute on Link header should be ignored for subresources
+    to prevent cross-origin referrer leakage</p>
+<script>
+window.referrers = {};
+const {REMOTE_ORIGIN} = get_host_info();
+async function loader(t, {preloadPolicy, resourcePolicy, isCrossOriginResource, hrefUrl, hrefParams}) {
+    const img = document.createElement('img');
+    const params = new URLSearchParams();
+    params.set('href', `${hrefUrl}?${hrefParams.toString()}`);
+    if (preloadPolicy === '')
+        params.set('preload-policy', '');
+    else
+        params.set('preload-policy', `referrerpolicy=${preloadPolicy}`);
+    params.set('resource-name', 'green.png');
+    img.src = `${isCrossOriginResource ? REMOTE_ORIGIN : location.origin}/preload/resources/link-header-referrer-policy.py?${params.toString()}`;
+    img.referrerPolicy = resourcePolicy;
+    const preloaded = new Promise(resolve => img.addEventListener('load', resolve));
+    t.add_cleanup(() => img.remove());
+    document.body.appendChild(img);
+    await preloaded;
+    hrefParams.set('operation', 'take');
+    const take_href = `${hrefUrl}?${hrefParams.toString()}`;
+    let actualReferrer;
+    for (let i = 0; i < 10; ++i) {
+        actualReferrer = await fetch(take_href).then(res => res.text());
+        if (actualReferrer === '') {
+            // Preload request has not yet been received. Retry after timeout.
+            await new Promise(resolve => t.step_timeout(resolve, 100));
+        } else {
+            break;
+        }
+    }
+    return {actualReferrer, unsafe: img.src};
+};
+
+function test_referrer_policy(preloadPolicy, resourcePolicy, isCrossOriginPreload, isCrossOriginResource) {
+    promise_test(async t => {
+        const id = token();
+        const hrefUrl = `${isCrossOriginPreload ? REMOTE_ORIGIN : location.origin}/preload/resources/stash-referrer.py`;
+        const hrefParams = new URLSearchParams();
+        hrefParams.set('key', id);
+        hrefParams.set('operation', 'put');
+        const {actualReferrer, unsafe} = await loader(t, {preloadPolicy, resourcePolicy, isCrossOriginResource, hrefUrl, hrefParams})
+        assert_equals(actualReferrer, 'NO-REFERER');
+    }, `referrer policy (${preloadPolicy} -> ${resourcePolicy}, ${isCrossOriginPreload ? 'cross-origin' : 'same-origin'}, ${isCrossOriginResource ? 'cross-origin' : 'same-origin'})`)
+}
+const policies = [
+"",
+"no-referrer",
+"same-origin",
+"origin",
+"origin-when-cross-origin",
+"strict-origin-when-cross-origin",
+"unsafe-url"]
+
+for (const preloadPolicy of policies) {
+    for (const resourcePolicy of policies) {
+        for (const isCrossOriginPreload of [true, false]) {
+            for (const isCrossOriginResource of [true, false]) {
+                test_referrer_policy(
+                    preloadPolicy,
+                    resourcePolicy,
+                    isCrossOriginPreload,
+                    isCrossOriginResource);
+            }
+        }
+    }
+}
+
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/preload-referrer-policy.html b/third_party/blink/web_tests/external/wpt/preload/preload-referrer-policy.html
index 0a4fbb0..a33d1b664 100644
--- a/third_party/blink/web_tests/external/wpt/preload/preload-referrer-policy.html
+++ b/third_party/blink/web_tests/external/wpt/preload/preload-referrer-policy.html
@@ -24,6 +24,7 @@
             params.set('preload-policy', '');
         else
             params.set('preload-policy', `referrerpolicy=${preloadPolicy}`);
+        params.set('resource-name', 'link-header-referrer-policy.html');
         iframe.src = `resources/link-header-referrer-policy.py?${params.toString()}`;
         t.add_cleanup(() => iframe.remove());
         const done = new Promise(resolve => {
diff --git a/third_party/blink/web_tests/external/wpt/preload/resources/link-header-referrer-policy.py b/third_party/blink/web_tests/external/wpt/preload/resources/link-header-referrer-policy.py
index 984518d..d3c659b1 100644
--- a/third_party/blink/web_tests/external/wpt/preload/resources/link-header-referrer-policy.py
+++ b/third_party/blink/web_tests/external/wpt/preload/resources/link-header-referrer-policy.py
@@ -3,9 +3,9 @@
                         (request.GET.first(b"href", b""),
                          request.GET.first(b"preload-policy", b"")))]
     body = ""
-    body_name_list = __file__.split(".")[:-1]
-    body_name_list.append("html")
-    filename = ".".join(body_name_list)
+    body_name_list = __file__.split("/")[:-1]
+    body_name_list.append(request.GET.first(b"resource-name",  b"").decode("utf-8"))
+    filename = "/".join(body_name_list)
     with open(filename, 'r+b') as f:
         body = f.readlines()
     return (200, response_headers, body)
diff --git a/third_party/blink/web_tests/external/wpt/preload/resources/stash-referrer.py b/third_party/blink/web_tests/external/wpt/preload/resources/stash-referrer.py
new file mode 100644
index 0000000..db498f35
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/preload/resources/stash-referrer.py
@@ -0,0 +1,18 @@
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+    response.headers.set(b'Access-Control-Allow-Origin', b'*')
+
+    # We assume this is a string representing a UUID
+    key = request.GET.first(b'key')
+    operation = request.GET.first(b'operation')
+
+    if operation == b'put':
+        referer = request.headers.get(b'referer') or 'NO-REFERER'
+        request.server.stash.put(key, referer)
+        return "Added value to stash"
+    elif operation == b'take':
+        value = request.server.stash.take(key)
+        return value or ''
+    else:
+        assert False
diff --git a/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-parseHTML.tentative.html b/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-parseHTML.tentative.html
index c4a31e2e..290c138 100644
--- a/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-parseHTML.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/sanitizer-api/sanitizer-parseHTML.tentative.html
@@ -58,6 +58,29 @@
 |     <div>
 |       "hello"
 
+#data
+<div data-xyz="1" id="2" title="3">
+#config
+{ "attributes": ["id"] }
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       id="2"
+
+#data
+<div>a<!-- xx -->b
+#config
+{ }
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       "a"
+|       "b"
+
 </script>
 <script id="unsafe" type="html5lib-testcases">
 #data
@@ -80,6 +103,30 @@
 |     <div>
 |       "hello"
 
+#data
+<div data-xyz="1" id="2" title="3">
+#config
+{ "attributes": ["id"] }
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       data-xyz="1"
+|       id="2"
+
+#data
+<div>a<!-- xx -->b
+#config
+{ }
+#document
+| <html>
+|   <head>
+|   <body>
+|     <div>
+|       "a"
+|       <!-- xx -->
+|       "b"
 </script>
 <script id="document" type="html5lib-testcases">
 #data
@@ -96,18 +143,18 @@
 <script>
 function test_safe(testcase, index) {
   let config = undefined;
-  try {
+  if (testcase.config) {
     config = { sanitizer: JSON.parse(testcase.config) };
-  } catch { /* config remains undefined */ }
+  }
   test(_ => {
     assert_testcase(Document.parseHTML(testcase.data, config), testcase);
   }, `parseHTML testcase ${index}, "${testcase.data}"`);
 }
 function test_unsafe(testcase, index) {
   let config = undefined;
-  try {
+  if (testcase.config) {
     config = { sanitizer: JSON.parse(testcase.config) };
-  } catch { /* config remains undefined */ }
+  }
   test(_ => {
     assert_testcase(Document.parseHTMLUnsafe(testcase.data, config), testcase);
   }, `parseHTMLUnsafe testcase ${index}, "${testcase.data}"`);
diff --git a/third_party/blink/web_tests/platform/linux/fast/forms/text/text-font-height-mismatch-expected.png b/third_party/blink/web_tests/platform/linux/fast/forms/text/text-font-height-mismatch-expected.png
index fa6d275..0705ac0 100644
--- a/third_party/blink/web_tests/platform/linux/fast/forms/text/text-font-height-mismatch-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/forms/text/text-font-height-mismatch-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/anchored/anchored-fallback-basic.html b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/anchored/anchored-fallback-basic.html
new file mode 100644
index 0000000..2195a3ba
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/anchored/anchored-fallback-basic.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<title>CSS Conditional Test: Basic @container anchored(fallback) support</title>
+<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-rule">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/8171">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
+<style>
+  body { margin: 0; }
+  #anchor {
+    anchor-name: --a;
+    margin-top: 100px;
+    width: 100px;
+    height: 100px;
+  }
+  .anchored {
+    position: absolute;
+    position-anchor: --a;
+    position-area: top;
+    position-try-fallbacks: flip-block;
+    width: 100px;
+    height: 100px;
+    container-type: anchored;
+  }
+  .anchored + .anchored {
+    /* Too tall to fit over the anchor to trigger fallback */
+    height: 200px;
+  }
+  @container anchored(fallback: 0) {
+    div { --fallback: no; }
+  }
+  @container anchored(fallback: 1) {
+    div { --fallback: yes; }
+  }
+</style>
+<div id="anchor"></div>
+<div class="anchored">
+  <div id="t1"></div>
+</div>
+<div class="anchored">
+  <div id="t2"></div>
+</div>
+<script>
+  test(() => {
+    assert_equals(document.querySelector(".anchored").offsetTop, 0);
+    assert_equals(getComputedStyle(t1).getPropertyValue("--fallback"), "no");
+  }, "@container anchored() without applied fallback");
+
+  test(() => {
+    assert_equals(document.querySelector(".anchored + .anchored").offsetTop, 200);
+    assert_equals(getComputedStyle(t2).getPropertyValue("--fallback"), "yes");
+  }, "@container anchored() with fallback applied");
+</script>
diff --git a/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/anchored/anchored-fallback-scroll.html b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/anchored/anchored-fallback-scroll.html
new file mode 100644
index 0000000..735f296b
--- /dev/null
+++ b/third_party/blink/web_tests/wpt_internal/css/css-conditional/container-queries/anchored/anchored-fallback-scroll.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<title>CSS Conditional Test: @container anchored(fallback) changes on scroll</title>
+<link rel="help" href="https://drafts.csswg.org/css-conditional-5/#container-rule">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/8171">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/css-conditional/container-queries/support/cq-testcommon.js"></script>
+<script src="/css/css-transitions/support/helper.js"></script>
+<style>
+  body { margin: 0; }
+  #anchor {
+    anchor-name: --a;
+    margin-top: 100px;
+    width: 100px;
+    height: 100px;
+  }
+  #anchored {
+    position: absolute;
+    position-anchor: --a;
+    position-area: top;
+    position-try-fallbacks: flip-block;
+    width: 100px;
+    height: 100px;
+    container-type: anchored;
+  }
+  @container anchored(fallback: 0) {
+    #t1 { --fallback: no; }
+  }
+  @container anchored(fallback: 1) {
+    #t1 { --fallback: yes; }
+  }
+</style>
+<div id="anchor"></div>
+<div id="anchored">
+  <div id="t1"></div>
+</div>
+<div style="height:3000px">XXX</div>
+<script>
+  promise_test(async t => {
+    await waitForAnimationFrames(2);
+    assert_equals(anchored.offsetTop, 0);
+    assert_equals(document.documentElement.scrollTop, 0);
+    assert_equals(getComputedStyle(t1).getPropertyValue("--fallback"), "no");
+  }, "@container anchored() without applied fallback at initial scroll position");
+
+  promise_test(async t => {
+    document.documentElement.scrollTop = 100;
+    await waitForAnimationFrames(2);
+    assert_equals(anchored.offsetTop, 200);
+    assert_equals(getComputedStyle(t1).getPropertyValue("--fallback"), "yes");
+  }, "@container anchored() applies fallback after scrolling down");
+</script>
diff --git a/third_party/compiler-rt/src b/third_party/compiler-rt/src
index 1d27130..21a487e 160000
--- a/third_party/compiler-rt/src
+++ b/third_party/compiler-rt/src
@@ -1 +1 @@
-Subproject commit 1d27130259209a2b5c8ac992b832fc5522f38a8d
+Subproject commit 21a487e9fae57351be896c5c34dce665e0990c76
diff --git a/third_party/crossbench b/third_party/crossbench
index bd90f97..de2f8a4 160000
--- a/third_party/crossbench
+++ b/third_party/crossbench
@@ -1 +1 @@
-Subproject commit bd90f97371197657589c4615e71db1601c391a25
+Subproject commit de2f8a499257e1622a1c122fab1c84a46b98e469
diff --git a/third_party/dawn b/third_party/dawn
index 82963fe..a4426dc 160000
--- a/third_party/dawn
+++ b/third_party/dawn
@@ -1 +1 @@
-Subproject commit 82963fe1f77a313839809d955fffea7b235fc32b
+Subproject commit a4426dc31c07fa49a58063cae9d7647d096d132d
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src
index 0987a35..5c5b644 160000
--- a/third_party/devtools-frontend/src
+++ b/third_party/devtools-frontend/src
@@ -1 +1 @@
-Subproject commit 0987a35390f74a2ecff9ac90e3531be5412c21fc
+Subproject commit 5c5b6444531b90826ac72ba9c8fe99965dbff83e
diff --git a/third_party/perfetto b/third_party/perfetto
index 5acde97..998e664 160000
--- a/third_party/perfetto
+++ b/third_party/perfetto
@@ -1 +1 @@
-Subproject commit 5acde97bf62e479089bd6466a8d84cd80846f502
+Subproject commit 998e664be79bba4703c0674ff62a29e37cbeccdb
diff --git a/third_party/webrtc b/third_party/webrtc
index 658d2f5..3bf5351 160000
--- a/third_party/webrtc
+++ b/third_party/webrtc
@@ -1 +1 @@
-Subproject commit 658d2f57f201457e9f8fb7c76ab7ce25505b580a
+Subproject commit 3bf535164c7c13ad6fc5b5dab313dc6acc7c2552
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 21720bc..515cfcc 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -12895,6 +12895,7 @@
   <int value="-441972413" label="OobeJellyModal:disabled"/>
   <int value="-441840790" label="TailoredSecurityUpdatedMessages:disabled"/>
   <int value="-441139232" label="ExcludeDisplayInMirrorMode:enabled"/>
+  <int value="-440532303" label="FillRecoveryPassword:disabled"/>
   <int value="-439110184" label="AndroidWebAppLaunchHandler:disabled"/>
   <int value="-438529902" label="CaptivePortalErrorPage:enabled"/>
   <int value="-438495497" label="enable-unsafe-swiftshader"/>
@@ -18444,6 +18445,7 @@
   <int value="1660491118" label="AllowAmbientEQ:enabled"/>
   <int value="1660605883" label="WebPaymentAPICSP:disabled"/>
   <int value="1660772828" label="NtpModulesDragAndDrop:enabled"/>
+  <int value="1661091335" label="FillRecoveryPassword:enabled"/>
   <int value="1661354480" label="BlockInsecurePrivateNetworkRequests:disabled"/>
   <int value="1661501921" label="BackToHomeAnimation:disabled"/>
   <int value="1661870048" label="SkipUndecryptablePasswords:disabled"/>
@@ -22824,6 +22826,18 @@
   <int value="11" label="No RenderFrame"/>
 </enum>
 
+<!-- LINT.IfChange(PrivacySandboxActSurveyStatus) -->
+
+<enum name="PrivacySandboxActSurveyStatus">
+  <int value="0" label="Survey was successfully launched"/>
+  <int value="1" label="Feature not enabled"/>
+  <int value="2" label="Failed to fetch HaTS service"/>
+  <int value="3" label="Failed to launch survey"/>
+  <int value="4" label="Not in Incognito profile"/>
+</enum>
+
+<!-- LINT.ThenChange(/chrome/browser/privacy_sandbox/incognito/privacy_sandbox_incognito_survey_service.h:PrivacySandboxActSurveyStatus) -->
+
 <enum name="PrivacySandboxAggregationServiceKeyFetcherStatus">
   <int value="0" label="Success"/>
   <int value="1" label="Download error"/>
diff --git a/tools/metrics/histograms/metadata/ash/OWNERS b/tools/metrics/histograms/metadata/ash/OWNERS
index 78e66c9..636d87e6 100644
--- a/tools/metrics/histograms/metadata/ash/OWNERS
+++ b/tools/metrics/histograms/metadata/ash/OWNERS
@@ -5,5 +5,4 @@
 tby@chromium.org
 jimmyxgong@chromium.org
 gavinwill@chromium.org
-michelefan@chromium.org
 junis@google.com
diff --git a/tools/metrics/histograms/metadata/autofill/enums.xml b/tools/metrics/histograms/metadata/autofill/enums.xml
index a511158..2c9abf82 100644
--- a/tools/metrics/histograms/metadata/autofill/enums.xml
+++ b/tools/metrics/histograms/metadata/autofill/enums.xml
@@ -3993,6 +3993,7 @@
   <int value="66" label="Identity Credentials"/>
   <int value="67" label="Loyalty card entry"/>
   <int value="68" label="Manage Loyalty Cards information"/>
+  <int value="69" label="Home and Work address suggestion"/>
 </enum>
 
 <!-- LINT.ThenChange(/components/autofill/core/browser/suggestions/suggestion_type.h:SuggestionType) -->
diff --git a/tools/metrics/histograms/metadata/blink/histograms.xml b/tools/metrics/histograms/metadata/blink/histograms.xml
index a187433..96416145 100644
--- a/tools/metrics/histograms/metadata/blink/histograms.xml
+++ b/tools/metrics/histograms/metadata/blink/histograms.xml
@@ -1084,6 +1084,19 @@
   </summary>
 </histogram>
 
+<histogram name="Blink.Document.CompleteURLTime" units="microseconds"
+    expires_after="2025-08-19">
+  <owner>nidhijaju@chromium.org</owner>
+  <owner>chrome-loading@google.com</owner>
+  <summary>
+    Time taken to resolve a non-null URL in Document::CompleteURLWithOverride.
+    This is recorded each time a URL is resolved, and can happen multiple times
+    per navigation.
+
+    This histogram only records metrics on machines with high-resolution clocks.
+  </summary>
+</histogram>
+
 <histogram
     name="Blink.DocumentLoader.CommitNavigationToStartLoadingResponse.Time.OutermostMainFrame.NewNavigation.IsHTTPOrHTTPS"
     units="ms" expires_after="2025-10-26">
diff --git a/tools/metrics/histograms/metadata/cookie/OWNERS b/tools/metrics/histograms/metadata/cookie/OWNERS
index 24bd1e4..dfa6ce5 100644
--- a/tools/metrics/histograms/metadata/cookie/OWNERS
+++ b/tools/metrics/histograms/metadata/cookie/OWNERS
@@ -2,5 +2,4 @@
 
 # Prefer sending CLs to the owners listed below.
 # Use chromium-metrics-reviews@google.com as a backup.
-bingler@chromium.org
 cfredric@chromium.org
diff --git a/tools/metrics/histograms/metadata/ios/enums.xml b/tools/metrics/histograms/metadata/ios/enums.xml
index 4b47984..bbe7af5 100644
--- a/tools/metrics/histograms/metadata/ios/enums.xml
+++ b/tools/metrics/histograms/metadata/ios/enums.xml
@@ -1266,6 +1266,10 @@
   <int value="13" label="Search Passwords Widget - Search Passwords"/>
 </enum>
 
+<enum name="IOSWidgetKitDeletedUiCount">
+  <int value="0" label="Deleted account ui displayed"/>
+</enum>
+
 <enum name="IOSWidgetKitExtensionKind">
   <int value="0" label="Dino game widget"/>
   <int value="1" label="Search widget"/>
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml
index 35545ddb..83a7d527 100644
--- a/tools/metrics/histograms/metadata/ios/histograms.xml
+++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -6108,6 +6108,18 @@
   </token>
 </histogram>
 
+<histogram name="IOS.WidgetsForMIM.DeletedAccountUiDisplayed"
+    enum="IOSWidgetKitDeletedUiCount" expires_after="2025-12-31">
+  <owner>fedegermi@google.com</owner>
+  <owner>bling-fundamentals@google.com</owner>
+  <summary>
+    Logs the number of times that the deleted account ui was displayed on
+    widgets. Note that this is only available if widgets for multi-profile
+    feature is enabled. This data is collected when the ui is presented on the
+    widget. The value is then added to the histogram when the app is launched.
+  </summary>
+</histogram>
+
 <histogram name="IOS.WKWebViewFinishBeforeCommit" enum="Boolean"
     expires_after="2025-10-30">
   <owner>gambard@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/privacy/histograms.xml b/tools/metrics/histograms/metadata/privacy/histograms.xml
index 7954e587..617803d 100644
--- a/tools/metrics/histograms/metadata/privacy/histograms.xml
+++ b/tools/metrics/histograms/metadata/privacy/histograms.xml
@@ -789,6 +789,16 @@
   </summary>
 </histogram>
 
+<histogram name="PrivacySandbox.ActSurvey.Status"
+    enum="PrivacySandboxActSurveyStatus" expires_after="2025-11-12">
+  <owner>kjarosz@google.com</owner>
+  <owner>polonium@google.com</owner>
+  <summary>
+    Records the status when attempting to surface the ACT survey. The ACT survey
+    is triggered on a new tab page in Incognito with a configurable delay.
+  </summary>
+</histogram>
+
 <histogram
     name="PrivacySandbox.AggregationService.KeyFetcher.HttpResponseOrNetErrorCode"
     enum="CombinedHttpResponseAndNetErrorCode" expires_after="2025-07-13">
diff --git a/tools/metrics/histograms/metadata/sync/histograms.xml b/tools/metrics/histograms/metadata/sync/histograms.xml
index f781b6b..2fc3a2c 100644
--- a/tools/metrics/histograms/metadata/sync/histograms.xml
+++ b/tools/metrics/histograms/metadata/sync/histograms.xml
@@ -736,17 +736,6 @@
   </summary>
 </histogram>
 
-<histogram name="Sync.CrossUserSharingPublicPrivateKeyInitSuccess"
-    enum="Boolean" expires_after="2025-06-22">
-  <owner>rushans@google.com</owner>
-  <owner>mmoskvitin@google.com</owner>
-  <summary>
-    Histogram that keeps track of the bootstrap of Public-private key
-    initialization outcome. Recorded upon attempt to commit NigoriSpecifics with
-    CrossUserSharingPrivateKey, that happens upon browser restart.
-  </summary>
-</histogram>
-
 <histogram
     name="Sync.Crypto.CustomPassphraseKeyDerivationMethodOnSuccessfulDecryption"
     enum="SyncCustomPassphraseKeyDerivationMethodState"
@@ -1850,6 +1839,27 @@
   </summary>
 </histogram>
 
+<histogram
+    name="Sync.SearchEngine.ChangesCommittedUpon{UpdateType}_{Operation}"
+    units="changes" expires_after="2026-03-07">
+  <owner>ankushkush@google.com</owner>
+  <owner>mahmadi@chromium.org</owner>
+  <summary>
+    Records the count of new changes committed to the sync server for search
+    engines upon every startup and/or initial sync and upon every incremental
+    update, separately for each type of sync change (add, delete or update).
+  </summary>
+  <token key="UpdateType">
+    <variant name="IncrementalUpdate"/>
+    <variant name="SyncStart"/>
+  </token>
+  <token key="Operation">
+    <variant name="Added"/>
+    <variant name="Deleted"/>
+    <variant name="Updated"/>
+  </token>
+</histogram>
+
 <histogram name="Sync.SearchEngine.DuplicateIsDefaultSearchProvider"
     enum="Boolean" expires_after="2026-03-07">
   <owner>ankushkush@google.com</owner>
@@ -1899,16 +1909,6 @@
   </summary>
 </histogram>
 
-<histogram name="Sync.SearchEngine.NewChangesCommittedUponSyncStart"
-    units="changes" expires_after="2026-03-07">
-  <owner>ankushkush@google.com</owner>
-  <owner>mahmadi@chromium.org</owner>
-  <summary>
-    Records the count of new changes committed to the sync server for search
-    engines upon every startup and/or initial sync.
-  </summary>
-</histogram>
-
 <histogram name="Sync.SearchEngine.RemoteSearchEngineIsUntouchedAutogenerated"
     enum="Boolean" expires_after="2026-03-03">
   <owner>ankushkush@google.com</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json
index 4379619f..670a66a 100644
--- a/tools/perf/core/perfetto_binary_roller/binary_deps.json
+++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -22,7 +22,7 @@
         },
         "linux": {
             "hash": "006eaca0358a3f2f7a4cf97a8c8c196bf6c653c6",
-            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/5acde97bf62e479089bd6466a8d84cd80846f502/trace_processor_shell"
+            "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/0552ccbfb3849d33781b5a4fe53b988e82539a1b/trace_processor_shell"
         }
     },
     "power_profile.sql": {
diff --git a/ui/base/resource/data_pack.cc b/ui/base/resource/data_pack.cc
index d72d8a25..17f72953 100644
--- a/ui/base/resource/data_pack.cc
+++ b/ui/base/resource/data_pack.cc
@@ -28,6 +28,8 @@
 #include "base/memory/raw_span.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/synchronization/lock.h"
+#include "base/types/expected_macros.h"
+#include "build/build_config.h"
 #include "net/filter/gzip_header.h"
 #include "third_party/zlib/google/compression_utils.h"
 #include "ui/base/resource/resource_scale_factor.h"
@@ -181,9 +183,23 @@
 DataPack::~DataPack() {
 }
 
+namespace {
+
+#if BUILDFLAG(IS_WIN)
+inline DWORD GetLastErrorOrErrno() {
+  return ::GetLastError();
+}
+#else
+inline int GetLastErrorOrErrno() {
+  return errno;
+}
+#endif
+
+}  // namespace
+
 // static
-std::unique_ptr<DataPack::DataSource> DataPack::LoadFromPathInternal(
-    const base::FilePath& path) {
+base::expected<std::unique_ptr<DataPack::DataSource>, DataPack::ErrorState>
+DataPack::LoadFromPathInternal(const base::FilePath& path) {
   std::unique_ptr<base::MemoryMappedFile> mmap =
       std::make_unique<base::MemoryMappedFile>();
   // Open the file for reading; allowing other consumers to also open it for
@@ -192,33 +208,43 @@
                                  base::File::FLAG_WIN_EXCLUSIVE_WRITE |
                                  base::File::FLAG_WIN_SHARE_DELETE);
   if (!data_file.IsValid()) {
-    DLOG(ERROR) << "Failed to open datapack with base::File::Error "
-                << data_file.error_details();
-    return nullptr;
+    const auto error = GetLastErrorOrErrno();
+    DPLOG(ERROR) << "Failed to open datapack";
+    return base::unexpected(
+        ErrorState{FailureReason::kOpenFile, error, data_file.error_details()});
   }
   if (!mmap->Initialize(std::move(data_file))) {
-    DLOG(ERROR) << "Failed to mmap datapack";
-    return nullptr;
+    const auto error = GetLastErrorOrErrno();
+    DPLOG(ERROR) << "Failed to mmap datapack";
+    return base::unexpected(ErrorState{FailureReason::kMapFile, error});
   }
   if (net::GZipHeader::HasGZipHeader(mmap->bytes())) {
     std::string_view compressed(reinterpret_cast<char*>(mmap->data()),
                                 mmap->length());
     std::string data;
     if (!compression::GzipUncompress(compressed, &data)) {
+      const auto error = GetLastErrorOrErrno();
       LOG(ERROR) << "Failed to unzip compressed datapack: " << path;
-      return nullptr;
+      return base::unexpected(ErrorState{FailureReason::kUnzip, error});
     }
-    return std::make_unique<StringDataSource>(std::move(data));
+    return base::ok(std::make_unique<StringDataSource>(std::move(data)));
   }
-  return std::make_unique<MemoryMappedDataSource>(std::move(mmap));
+  return base::ok(std::make_unique<MemoryMappedDataSource>(std::move(mmap)));
 }
 
 bool DataPack::LoadFromPath(const base::FilePath& path) {
-  std::unique_ptr<DataSource> data_source = LoadFromPathInternal(path);
-  if (!data_source)
-    return false;
+  return LoadFromPathWithError(path).has_value();
+}
 
-  return LoadImpl(std::move(data_source));
+base::expected<void, DataPack::ErrorState> DataPack::LoadFromPathWithError(
+    const base::FilePath& path) {
+  std::unique_ptr<DataPack::DataSource> data_source;
+  ASSIGN_OR_RETURN(data_source, LoadFromPathInternal(path));
+  RETURN_IF_ERROR(LoadImpl(std::move(data_source)),
+                  [](DataPack::FailureReason failure_reason) {
+                    return ErrorState{failure_reason};
+                  });
+  return base::ok();
 }
 
 bool DataPack::LoadFromFile(base::File file) {
@@ -236,16 +262,18 @@
     mmap.reset();
     return false;
   }
-  return LoadImpl(std::make_unique<MemoryMappedDataSource>(std::move(mmap)));
+  return LoadImpl(std::make_unique<MemoryMappedDataSource>(std::move(mmap)))
+      .has_value();
 }
 
 bool DataPack::LoadFromBuffer(base::span<const uint8_t> buffer) {
-  return LoadImpl(std::make_unique<BufferDataSource>(buffer));
+  return LoadImpl(std::make_unique<BufferDataSource>(buffer)).has_value();
 }
 
-bool DataPack::SanityCheckFileAndRegisterResources(size_t margin_to_skip,
-                                                   const uint8_t* data,
-                                                   size_t data_length) {
+base::expected<void, DataPack::FailureReason>
+DataPack::SanityCheckFileAndRegisterResources(size_t margin_to_skip,
+                                              const uint8_t* data,
+                                              size_t data_length) {
   // 1) Check we have enough entries. There's an extra entry after the last item
   // which gives the length of the last item.
   size_t resource_table_size = (resource_count_ + 1) * sizeof(Entry);
@@ -258,7 +286,7 @@
                << " bytes, expected longer than "
                << margin_to_skip + resource_table_size + alias_table_size
                << " bytes.";
-    return false;
+    return base::unexpected(FailureReason::kTooShort);
   }
 
   resource_table_ = reinterpret_cast<const Entry*>(&data[margin_to_skip]);
@@ -271,7 +299,7 @@
     if (resource_table_[i].file_offset > data_length) {
       LOG(ERROR) << "Data pack file corruption: "
                  << "Entry #" << i << " past end.";
-      return false;
+      return base::unexpected(FailureReason::kBoundsExceeded);
     }
   }
 
@@ -280,7 +308,7 @@
     if (resource_table_[i].file_offset > resource_table_[i + 1].file_offset) {
       LOG(ERROR) << "Data pack file corruption: " << "Entry #" << i + 1
                  << " before Entry #" << i << ".";
-      return false;
+      return base::unexpected(FailureReason::kOrderingViolation);
     }
   }
 
@@ -289,14 +317,15 @@
     if (alias_table_[i].entry_index >= resource_count_) {
       LOG(ERROR) << "Data pack file corruption: "
                  << "Alias #" << i << " past end.";
-      return false;
+      return base::unexpected(FailureReason::kAliasTableCorrupt);
     }
   }
 
-  return true;
+  return base::ok();
 }
 
-bool DataPack::LoadImpl(std::unique_ptr<DataPack::DataSource> data_source) {
+base::expected<void, DataPack::FailureReason> DataPack::LoadImpl(
+    std::unique_ptr<DataPack::DataSource> data_source) {
   const uint8_t* data = data_source->GetData();
   size_t data_length = data_source->GetLength();
   // Parse the version and check for truncated header.
@@ -308,7 +337,7 @@
       version == kFileFormatV4 ? kHeaderLengthV4 : kHeaderLengthV5;
   if (version == 0 || data_length < header_length) {
     DLOG(ERROR) << "Data pack file corruption: incomplete file header.";
-    return false;
+    return base::unexpected(FailureReason::kIncompleteHeader);
   }
 
   // Parse the header of the file.
@@ -324,22 +353,22 @@
   } else {
     LOG(ERROR) << "Bad data pack version: got " << version << ", expected "
                << kFileFormatV4 << " or " << kFileFormatV5;
-    return false;
+    return base::unexpected(FailureReason::kBadPakVersion);
   }
 
   if (text_encoding_type_ != UTF8 && text_encoding_type_ != UTF16 &&
       text_encoding_type_ != BINARY) {
     LOG(ERROR) << "Bad data pack text encoding: got " << text_encoding_type_
                << ", expected between " << BINARY << " and " << UTF16;
-    return false;
+    return base::unexpected(FailureReason::kBadEncodingType);
   }
 
   // Sanity check the file.
-  if (!SanityCheckFileAndRegisterResources(header_length, data, data_length))
-    return false;
+  RETURN_IF_ERROR(
+      SanityCheckFileAndRegisterResources(header_length, data, data_length));
 
   data_source_ = std::move(data_source);
-  return true;
+  return base::ok();
 }
 
 const DataPack::Entry* DataPack::LookupEntryById(uint16_t resource_id) const {
diff --git a/ui/base/resource/data_pack.h b/ui/base/resource/data_pack.h
index d2407b5..9fb984ba 100644
--- a/ui/base/resource/data_pack.h
+++ b/ui/base/resource/data_pack.h
@@ -14,6 +14,7 @@
 
 #include <map>
 #include <memory>
+#include <optional>
 #include <string_view>
 #include <vector>
 
@@ -21,8 +22,14 @@
 #include "base/files/file.h"
 #include "base/files/memory_mapped_file.h"
 #include "base/memory/raw_ptr.h"
+#include "base/types/expected.h"
+#include "build/build_config.h"
 #include "ui/base/resource/resource_handle.h"
 
+#if BUILDFLAG(IS_WIN)
+#include "base/win/windows_types.h"
+#endif
+
 namespace base {
 class FilePath;
 class RefCountedStaticMemory;
@@ -129,10 +136,41 @@
   // memory, with the mapping owned by |data_source_|.
   bool LoadFromPath(const base::FilePath& path);
 
-  // The static part of the implementation in LoadFromPath().
-  static std::unique_ptr<DataSource> LoadFromPathInternal(
+  enum class FailureReason {
+    kOpenFile,
+    kMapFile,
+    kUnzip,
+    kIncompleteHeader,
+    kBadPakVersion,
+    kBadEncodingType,
+    kTooShort,
+    kBoundsExceeded,
+    kOrderingViolation,
+    kAliasTableCorrupt,
+  };
+
+  struct ErrorState {
+    FailureReason reason;
+#if BUILDFLAG(IS_WIN)
+    DWORD error;
+#else
+    int error;
+#endif
+    base::File::Error file_error;
+
+    friend bool operator==(const ErrorState& lhs,
+                           const ErrorState& rhs) = default;
+  };
+
+  // As LoadFromPath, but returns an ErrorState on failure.
+  base::expected<void, DataPack::ErrorState> LoadFromPathWithError(
       const base::FilePath& path);
 
+  // The static part of the implementation in LoadFromPath().
+  static base::expected<std::unique_ptr<DataPack::DataSource>,
+                        DataPack::ErrorState>
+  LoadFromPathInternal(const base::FilePath& path);
+
   // Invokes LoadFromFileRegion with the entire contents of |file|. Compressed
   // files are not supported.
   bool LoadFromFile(base::File file);
@@ -192,7 +230,8 @@
 
   // Does the actual loading of a pack file.
   // Called by Load and LoadFromFile and LoadFromBuffer.
-  bool LoadImpl(std::unique_ptr<DataSource> data_source);
+  base::expected<void, DataPack::FailureReason> LoadImpl(
+      std::unique_ptr<DataSource> data_source);
   const Entry* LookupEntryById(uint16_t resource_id) const;
 
   // Sanity check the file. If it passed the check, register `resource_table_`
@@ -200,10 +239,12 @@
   // `margin_to_skip` represents the size of the margin in bytes before
   // resource_table information starts.
   // If there is no extra data in data pack, `margin_to_skip` is equal to the
-  // length of file header.
-  bool SanityCheckFileAndRegisterResources(size_t margin_to_skip,
-                                           const uint8_t* data,
-                                           size_t data_length);
+  // length of file header. Returns std::nullopt on success or a FailureReason
+  // on failure.
+  base::expected<void, DataPack::FailureReason>
+  SanityCheckFileAndRegisterResources(size_t margin_to_skip,
+                                      const uint8_t* data,
+                                      size_t data_length);
 
   // Returns the string between `target_offset` and `next_offset` in data pack.
   static std::string_view GetStringViewFromOffset(uint32_t target_offset,
diff --git a/ui/base/resource/data_pack_unittest.cc b/ui/base/resource/data_pack_unittest.cc
index 9efca373..e3e1d4e 100644
--- a/ui/base/resource/data_pack_unittest.cc
+++ b/ui/base/resource/data_pack_unittest.cc
@@ -22,6 +22,7 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/path_service.h"
+#include "base/test/gmock_expected_support.h"
 #include "build/build_config.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/zlib/google/compression_utils.h"
@@ -227,6 +228,9 @@
 
   DataPack pack(k100Percent);
   ASSERT_FALSE(pack.LoadFromPath(data_path));
+  ASSERT_THAT(pack.LoadFromPathWithError(data_path),
+              base::test::ErrorIs(DataPack::ErrorState{
+                  DataPack::FailureReason::kIncompleteHeader}));
 }
 
 TEST_P(DataPackTest, Write) {
diff --git a/ui/base/resource/resource_bundle.cc b/ui/base/resource/resource_bundle.cc
index 328028f..1892bdcc 100644
--- a/ui/base/resource/resource_bundle.cc
+++ b/ui/base/resource/resource_bundle.cc
@@ -21,7 +21,6 @@
 
 #include "base/command_line.h"
 #include "base/containers/span.h"
-#include "base/debug/alias.h"
 #include "base/files/file.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
@@ -71,6 +70,9 @@
 #endif
 
 #if BUILDFLAG(IS_WIN)
+#include <windows.h>
+
+#include "base/threading/scoped_blocking_call.h"
 #include "ui/display/win/dpi.h"
 
 // To avoid conflicts with the macro from the Windows SDK...
@@ -372,7 +374,49 @@
 // static
 bool ResourceBundle::LocaleDataPakExists(const std::string& locale) {
   const auto path = GetLocaleFilePath(locale);
-  return !path.empty() && base::PathExists(path);
+  if (path.empty()) {
+    return false;
+  }
+#if BUILDFLAG(IS_WIN)
+  // https://crbug.com/40688225: Chrome sometimes fails to find standard .pak
+  // files. One theory is that this happens shortly after an update because
+  // scanners (e.g., A/V) are busy checking Chrome's files. If this is
+  // happening, then `base::PathExists` is reporting `false` for files that
+  // exist but can't be opened.
+  DWORD attributes;
+  {
+    base::ScopedBlockingCall scoped_blocking_call(
+        FROM_HERE, base::BlockingType::MAY_BLOCK);
+    attributes = ::GetFileAttributes(path.value().c_str());
+  }
+  if (attributes == FILE_ATTRIBUTE_DIRECTORY) {
+    return false;  // A directory is not a .pak file.
+  }
+  if (attributes != INVALID_FILE_ATTRIBUTES) {
+    return true;  // Attributes were read; the file must exist.
+  }
+  const auto error = ::GetLastError();
+  if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND) {
+    return false;  // `path` does not exist.
+  }
+  // The attributes could not be read yet `path` exists. This is likely a case
+  // of the file being locked by other software. Either the file will be
+  // readable by the time it's needed, or the failure to open it will be handled
+  // at that time.
+
+  // Include the path and the error in subsequent crashes (e.g., in Chrome's
+  // InitResourceBundleAndDetermineLocale).
+  static auto* const busy_path_key = base::debug::AllocateCrashKeyString(
+      "LocaleDataPakExists-busy_path", base::debug::CrashKeySize::Size256);
+  base::debug::SetCrashKeyString(busy_path_key, path.AsUTF8Unsafe());
+  static auto* const busy_error_key = base::debug::AllocateCrashKeyString(
+      "LocaleDataPakExists-busy_error", base::debug::CrashKeySize::Size32);
+  base::debug::SetCrashKeyString(busy_error_key, base::NumberToString(error));
+
+  return true;
+#else
+  return base::PathExists(path);
+#endif
 }
 #endif  // !BUILDFLAG(IS_ANDROID)
 
@@ -448,34 +492,53 @@
     locale_file_path = GetLocaleFilePath(app_locale);
 
   if (locale_file_path.empty()) {
-    // It's possible that there is no locale.pak.
+    // locale.pak was provided by neither GetOverriddenPakPath() nor
+    // GetLocaleFilePath().
+    if (crash_on_failure) {
+      // Store the locale strings in crash keys in case the caller subsequently
+      // crashes the process; see https://crbug.com/40688225.
+      static auto* const app_locale_key = base::debug::AllocateCrashKeyString(
+          "LoadLocaleResourcesNoPath-app_locale",
+          base::debug::CrashKeySize::Size32);
+      base::debug::SetCrashKeyString(app_locale_key, app_locale);
+      static auto* const pref_locale_key = base::debug::AllocateCrashKeyString(
+          "LoadLocaleResourcesNoPath-pref_locale",
+          base::debug::CrashKeySize::Size32);
+      base::debug::SetCrashKeyString(pref_locale_key, pref_locale);
+    }
     LOG(WARNING) << "locale_file_path.empty() for locale " << app_locale;
     return std::string();
   }
 
   auto data_pack = std::make_unique<DataPack>(k100Percent);
-  if (!data_pack->LoadFromPath(locale_file_path) && crash_on_failure) {
-    // https://crbug.com/1076423: Chrome can't start when the locale file cannot
-    // be loaded. Crash early and gather some data.
-#if BUILDFLAG(IS_WIN)
-    const auto last_error = ::GetLastError();
-    base::debug::Alias(&last_error);
-    wchar_t path_copy[MAX_PATH];
-    base::wcslcpy(path_copy, locale_file_path.value().c_str(),
-                  std::size(path_copy));
-    base::debug::Alias(path_copy);
-#endif  // BUILDFLAG(IS_WIN)
+  if (auto result = data_pack->LoadFromPathWithError(locale_file_path);
+      !result.has_value() && crash_on_failure) {
+    DataPack::ErrorState& error = result.error();
+    // https://crbug.com/40688225 and https://crbug.com/394631579: Chrome can't
+    // start when the locale file cannot be loaded. Crash early and gather some
+    // data.
 
-    // Collect diagnostic info for https://crbug.com/394631579 .
-#if BUILDFLAG(IS_MAC)
+    // The local contained in prefs; provided by the caller.
     SCOPED_CRASH_KEY_STRING32("LoadLocaleResources", "pref_locale",
                               pref_locale);
+    // The app locale resolved from the pref value.
     SCOPED_CRASH_KEY_STRING32("LoadLocaleResources", "app_locale", app_locale);
-    SCOPED_CRASH_KEY_STRING1024("LoadLocaleResources", "override_filepath",
-                                GetOverriddenPakPath().AsUTF8Unsafe());
+    // The path to the (possibly overridden) file that could not be opened.
     SCOPED_CRASH_KEY_STRING1024("LoadLocaleResources", "locale_filepath",
                                 locale_file_path.AsUTF8Unsafe());
-#endif  // BUILDFLAG(IS_MAC)
+
+    // A ui::DataPack::FailureReason indicating what step during the attempt to
+    // load the file failed.
+    SCOPED_CRASH_KEY_NUMBER("LoadLocaleResources", "reason",
+                            static_cast<int>(error.reason));
+    // A last-error code on Windows; otherwise, errno. Only relevant if `reason`
+    // is `kOpenFile` (0) or `kMapFile` (1).
+    SCOPED_CRASH_KEY_NUMBER("LoadLocaleResources", "error", error.error);
+    // The base::File::Error from opening the file. Only relevant if `reason` is
+    // `kOpenFile` (0). Most likely redundant given `error` above, but reporting
+    // anyway just in case.
+    SCOPED_CRASH_KEY_NUMBER("LoadLocaleResources", "file_error",
+                            error.file_error);
 
     NOTREACHED();
   }
diff --git a/v8 b/v8
index b5f7eab..57c1b23 160000
--- a/v8
+++ b/v8
@@ -1 +1 @@
-Subproject commit b5f7eab40bf7a2f2b1d6acf93a8f672a7cc63df9
+Subproject commit 57c1b239277abc4012a5afe76048710b0c9e56d1